Reputation: 262
I have just started using neo4j.
I was trying to use it to describe a company's Enterprise Architecture.
I'd like to show that, by removing the node "Middleware", Service_A goes from Client_A and Server_X, and the same for the other services.
Is there a way to do that? Is this the right approach?
Next is the code used to generate the graph.
from neo4j import GraphDatabase
import logging
from neo4j.exceptions import ServiceUnavailable
class App:
def __init__(self, uri, user, password):
self.driver = GraphDatabase.driver(uri, auth=(user, password))
def close(self):
# Don't forget to close the driver connection when you are finished with it
self.driver.close()
def drop_everything(self):
with self.driver.session(database="neo4j") as session:
# Write transactions allow the driver to handle retries and transient errors
session.execute_write(self._drop_everything)
print("Graph DB dropped")
@staticmethod
def _drop_everything(tx):
query = (
"MATCH (n) DETACH DELETE (n)"
)
try:
tx.run(query)
# Capture any errors along with the query and data for traceability
except ServiceUnavailable as exception:
logging.error("{query} raised an error: \n {exception}".format(query=query, exception=exception))
raise
def create_system(self, system_name):
with self.driver.session(database="neo4j") as session:
# Write transactions allow the driver to handle retries and transient errors
result = session.execute_write(self._create_system, system_name)
print("Created system: {p1}".format(p1=result))
@staticmethod
def _create_system(tx, system_name):
# To learn more about the Cypher syntax, see https://neo4j.com/docs/cypher-manual/current/
# The Reference Card is also a good resource for keywords https://neo4j.com/docs/cypher-refcard/current/
query = (
"CREATE (n:system{name:$system_name}) "
"RETURN (n)"
)
result = tx.run(query, system_name=system_name)
try:
return result.single()[0]["name"]
# Capture any errors along with the query and data for traceability
except ServiceUnavailable as exception:
logging.error("{query} raised an error: \n {exception}".format(query=query, exception=exception))
raise
def create_flow(self, service_name_a, system1_name, system2_name):
with self.driver.session(database="neo4j") as session:
# Write transactions allow the driver to handle retries and transient errors
result = session.execute_write(self._create_flow, service_name_a, system1_name, system2_name)
print("Created flow: {p1}".format(p1=result))
@staticmethod
def _create_flow(tx, service_name, system1_name, system2_name):
query = (
"MATCH (p1:system), (p2:system) "
"WHERE p1.name = '" + system1_name + "' AND p2.name = '" + system2_name +"' "
"CREATE (p1)-[r:" + service_name + " {type:'call'}]->(p2) "
"RETURN ('" + service_name + "')"
)
# CREATE (p1)-[r:service {name:'" + service_name + "'}]->(p2)
# CALL apoc.create.relationship(p1, '$service_name', NULL, p2) YIELD rel
# CREATE (p1)-[:service {name:'$service_name'}]->(p2) "
result = tx.run(query, service_name=service_name, system1_name=system1_name, system2_name=system2_name)
try:
return service_name
# Capture any errors along with the query and data for traceability
except ServiceUnavailable as exception:
logging.error("{query} raised an error: \n {exception}".format(query=query, exception=exception))
raise
def find_system(self, system_name):
with self.driver.session(database="neo4j") as session:
result = session.execute_read(self._find_system, system_name)
for row in result:
print("Found system: {row}".format(row=row))
@staticmethod
def find_system(tx, system_name):
query = (
"MATCH (p:system) "
"WHERE p.name = $system_name "
"RETURN p.name AS name"
)
result = tx.run(query, system_name=system_name)
return [{ row["name"] } for row in result]
if __name__ == "__main__":
# Aura queries use an encrypted connection using the "neo4j+s" URI scheme
# https://console.neo4j.io/ - Login with google [email protected]
uri = "neo4j+s://8a9f54ab.databases.neo4j.io"
user = "neo4j"
password = "Xxx"
app = App(uri, user, password)
app.drop_everything()
app.create_system("Client_A")
app.create_system("Middleware")
app.create_system("Server_X")
app.create_system("Server_Y")
app.create_system("Server_Z")
app.create_flow("Service_A", "Client_A", "Middleware")
app.create_flow("Service_B", "Client_A", "Middleware")
app.create_flow("Service_C", "Client_A", "Middleware")
app.create_flow("Service_A", "Middleware", "Server_X")
app.create_flow("Service_B", "Middleware", "Server_Y")
app.create_flow("Service_C", "Middleware", "Server_Z")
app.close()
Upvotes: 0
Views: 192
Reputation: 150
Do you have distinct labels for the different types of nodes? i.e., client nodes vs. server nodes
If you would like to retain the graph's current structure, you can use the Virtual Nodes/Relationships concept to collapse the intermediary node (i.e., Middleware) and create a (virtual) relationship between the Client and Servers.
Note that the virtual relationship will not exist within the graph or require you to remove the Middleware intermediary, but it will be available at the UI layer for visualization/presentation.
MATCH (from:system)-[rel1:service]->(inter:system)-[rel2:service]->(to:system)
RETURN from, to, apoc.create.vRelationship(from,'SERVES', {}, to) as myVirtualRel;
Check out https://neo4j.com/labs/apoc/4.1/virtual/virtual-nodes-rels/.
Upvotes: 1
Reputation: 9294
For the graph, created by this query as depicted in your image:
MERGE (c:Client)
MERGE (m:Middleware)
MERGE (s:Server_X)
MERGE (s1:Server_Y)
MERGE (s2:Server_Z)
MERGE (c)-[:SERVICE_A]->(m)-[:SERVICE_A]->(s)
MERGE (c)-[:SERVICE_B]->(m)-[:SERVICE_B]->(s1)
MERGE (c)-[:SERVICE_C]->(m)-[:SERVICE_C]->(s2)
This query should work:
MATCH (c:Client)-[rel1]->(m:Middleware)-[rel2]->(server) WHERE type(rel1) = type(rel2)
MERGE (c)-[:CONNECTION]->(server)
Upvotes: 1