cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Significance of using " , " in neo4j multiple relationships cypher query

Hello Team,

As a beginner in neo4j , I want to understand the significance and impact if I use " , " in my Cypher Query where multiple relationships are chained.

Please suggest which way is correct.

Below is my query:

MATCH (a:SITE_CODE_TABLE)<-[:site2sitecode]-(b:SITE)-[:site2container]->(c:CONTAINER)-[:container2mostatic]->(d:MO_STATIC_TABLE) ,
(e:LTE_ANTENNA)-[:letantenna2enodeb]->(f:ENODEB)-[:enodeb2container]->(g:CONTAINER)<-[:enodebamo2container]-(h:ENODEB_AMO)
where c.EQUIP_NAME=g.EQUIP_NAME
RETURN
a.SITE_REGION ,
d.type AS DEVICE_TYPE,
c.CUSTOM_OPERATIONCONTEXT AS EQUIP_OC,
d.STD_OMID AS EQUIP_OMID,
d.STD_EMS_TYPE AS EQUIP_EMS_TYPE,
d.STD_SISID AS EQUIP_SSID,
b.SITE_STATUS AS SITE_STATUS,
b.SITE_COUNTY AS SITE_COUNTY,
b.SITE_LATITUDE AS SITE_LATITUDE,
b.SITE_LONGITUDE AS SITE_LONGITUDE,
b.STRUCTURE_NAME AS STRUCTURE_NAME,
b.SITE_PKEY AS SITE_PKEY,
b.SITE_COUNTRY as SITE_COUNTRY,
b.PARENT_SITE_NAME AS PARENT_SITE_NAME,
b.SITE_AREACODE AS SITE_AREACODE,
b.SITE_CIRCLE AS SITE_CIRCLE,
b.SITE_STATE AS SITE_STATE,
b.POST_CODE AS POST_CODE,
b.SITE_FLOOR AS SITE_FLOOR,
b.SITE_NAME AS SITE_NAME,
b.SITE_ADDRESS AS SITE_ADDRESS,
b.SITE_ROOM AS SITE_ROOM,
b.SITE_TYPE AS SITE_TYPE,
b.SITE_FRIENDLY_NAME AS SITE_FRIENDLY_NAME,
b.SITE_R4G_STATE AS SITE_R4G_STATE,
b.JIO_CENTER AS JIO_CENTER,
b.SITE_DISTRICT AS SITE_DISTRICT,
c.EQUIP_NAME AS EQUIP_NAME,
c.EQUIP_RINGNUMBER AS EQUIP_RINGNUMBER,
c.EQUIP_MM_SERVICEID AS EQUIP_MM_SERVICEID,
c.EQUIP_SITE_NAME AS EQUIP_SITE_NAME,
c.EQUIP_EMS_INSTANCENAME AS EQUIP_EMS_INSTANCENAME,
c.EQUIP_TRACKINGAREA AS EQUIP_TRACKINGAREA,
c.EQUIP_NATIVENAME AS EQUIP_NATIVENAME,
c.EQUIP_STATUS AS EQUIP_STATUS,
c.EQUIP_LEVEL AS EQUIP_LEVEL,
c.EQUIP_CUSTOMERTYPE AS EQUIP_CUSTOMERTYPE,
c.EQUIP_PKEY AS EQUIP_PKEY,
c.EQUIP_EQ_CLS AS EQUIP_EQ_CLS,
c.EQUIP_TYPE AS EQUIP_TYPE,
c.PARENT_EQUIP_NAME AS PARENT_EQUIP_NAME,
c.EQUIP_ECS_SERVICEID AS EQUIP_ECS_SERVICEID,
c.EQUIP_NMS_EMS AS EQUIP_NMS_EMS,
c.EQUIP_MODEL AS EQUIP_MODEL,
c.EQUIP_CLEI AS EQUIP_CLEI,
c.EQUIP_VENDOR AS EQUIP_VENDOR,
c.EQUIP_MAINTENANCEPOINT AS EQUIP_MAINTENANCEPOINT,
b.SITE_CITY AS SITE_CITY,
c.SAPID AS SAPID,
c.CUSTOM_MANAGEDOBJECT AS EQUIP_MO,
h.ENB_INST_NAME AS ENB_INST_NAME,
h.ENB_INST_ID AS ENB_INST_ID,
e.LTEANT_INST_NAME AS LTEANT_INST_NAME,
e.LTEANT_ELECTRICAL_TILT AS LTEANT_ELECTRICAL_TILT,
e.LTEANT_AZIMUTH AS LTEANT_AZIMUTH,
e.LTEANT_MECHANICAL_TILT AS LTEANT_MECHANICAL_TILT,
e.LTEANT_CELL_VALUE AS LTEANT_CELL_VALUE,
e.LTEANT_STATUS AS LTEANT_STATUS,
e.LTEANT_TX_POWER AS LTEANT_TX_POWER,
e.LTEANT_CELL_NUMBER AS LTEANT_CELL_NUMBER,
e.LTEANT_INST_ID AS LTEANT_INST_ID,
e.SEC_SECTOR_ID AS SEC_SECTOR_ID,
f.ENB_STATUS AS ENB_STATUS,
f.ENB_ID AS ENB_ID,
f.ENB_MNC_ID AS ENB_MNC_ID,
f.ENB_PRIMARY_MME_ID AS ENB_PRIMARY_MME_ID,
f.ENB_BTS_TYPE AS ENB_BTS_TYPE,
f.ENB_MCC_ID AS ENB_MCC_ID,
f.ENB_LSMRID AS ENB_LSMRID,
f.ENB_SECONDARY_MME_ID AS ENB_SECONDARY_MME_ID,
f.ENB_DEVICE_CODE AS ENB_DEVICE_CODE,
f.ENB_VENDOR AS ENB_VENDOR,
f.ENB_EMS_TYPE AS ENB_EMS_TYPE,
f.ENB_MODEL AS ENB_MODEL,
f.ENB_TRACKINGAREA AS ENB_TRACKINGAREA,
f.ENB_SITE_NAME AS ENB_SITE_NAME,
f.ENB_EMS_INSTANCENAME AS ENB_EMS_INSTANCENAME,
f.ENB_NATIVENAME AS ENB_NATIVENAME,
f.EMF_STATUS_850 AS EMF_STATUS_850,
f.EMF_STATUS_1800 AS EMF_STATUS_1800,
f.EMF_STATUS_2300 AS EMF_STATUS_2300 limit 10

Please help.

Regards
Akshat

13 REPLIES 13

The comma comes down to uniqueness - in neo4j a single MATCH clause has path uniqueness, meaning the same relationship isn't traversed twice. For example, if you had the following graph:

(:Person {name: "Adam"})-[:KNOWS {id: 1}]-(:Person {name: "Karin"})-[:KNOWS {id: 2}]-(:Person {name: "Akshat"})

If you run the following query:

MATCH (:Person {name: "Adam"})-[:KNOWS]-(friend),
  (friend)-[:KNOWS]-(fof)
RETURN fof.name

you will get one result:

╒══════════╕
│"fof.name"│
╞══════════╡
│"Akshat"  │
└──────────┘

This is because the :KNOWS relationship with {id: 1} won't be traversed a second time in the match. However, if you run:

MATCH (:Person {name: "Adam"})-[:KNOWS]-(friend)
MATCH (friend)-[:KNOWS]-(fof)
RETURN fof.name

:KNOWS {id: 1} wouldn't have been traversed as part of that MATCH so you will get two results.

╒══════════╕
│"fof.name"│
╞══════════╡
│"Akshat"  │
├──────────┤
│"Adam"    │
└──────────┘

You can also use this to write a subsequent match something in the middle of another path.

MATCH (:Person {name: "Adam"})-[:KNOWS]-(friend)-[:KNOWS]-(fof),
  (friend)-[:LIVES_IN]->(location)<-[:LIVES_IN]-(fof)
RETURN friend, location, fof

I hope that helps!

Hello Adam,

Thanks for explaining it in detail.
Unfortunately , I am still not able to understand the difference bet ween the queries and comma concept in multiple relationships query.

Any other way to understand this?

Apologies for the same.

Best Regards
Akshat

Hello Team,

Can Anyone Please help in this?

Best Regards
AK

Hello Team,

Can Anyone Please help in this? Or share some link to read it more about this?

Best Regards
Akshat

I think the example works a little better with a slightly simpler graph:

(:Person {name: "Adam"})-[:KNOWS]-(:Person {name: "Karin"})

If we do this query:

MATCH (a:Person {name: "Adam"})-[:KNOWS]-(k:Person {name: "Karin"}), (k)-[:KNOWS]-(anotherPerson)
RETURN anotherPerson

This will not return any results.

  1. The first desired path in the graph matching the pattern is found by traversing (from either Adam or Karin, depending on which the planner picks as a start node) the only :KNOWS relationship between them. But for this MATCH to be successful, all of the comma-separated patterns have to be fulfilled
  2. The second pattern cannot be fulfilled. Why? Because the :KNOWS relationship was already used in this path to fulfill the first pattern. Cypher's uniqueness behavior (called 'RELATIONSHIP_PATH' uniqueness) is that a relationship can only be used once within a path. This prevents us from doubling-back on an already-traversed relationship.
  3. No results because we cannot traverse a relationship twice in a path. We would need the existence of a second :KNOWS relationship for this to work.

But on the other hand if we had this query instead:

MATCH (a:Person {name: "Adam"})-[:KNOWS]-(k:Person {name: "Karin"})
MATCH (k)-[:KNOWS]-(anotherPerson)
RETURN anotherPerson

This would return 1 result, where anotherPerson is the node for Adam. Why does this work? Because a path is specific to a MATCH. The uniqueness behavior only applies to a single path.

  1. We MATCH to the path fitting the pattern in the first line. Only one such path exists, and we traverse the :KNOWS relationship to get there.
  2. But now the first MATCH has ended. We're on a second MATCH now, which will generate a new path. The :KNOWS relationship between these two nodes can be traversed without issue.

So again, within a single MATCH (or OPTIONAL MATCH, or pattern comprehension), per path, a relationship can only be used once, and that applies even when multiple comma-separated patterns are present. If the relationship is used to fulfill a path in one of those comma-separated patterns, it cannot be used to satisfy a separate one of those comma-separated patterns.

As for the reasoning as to why we do this, one big reason is to prevent infinite loops when we're using variable-length relationship patterns.

Consider if we used this same 2-node, 1-relationship graph and tried to do this:

MATCH (a:Person {name: "Adam"})-[:KNOWS*]-(anotherPerson)
RETURN anotherPerson

This query will find all paths of any length traversing a potentially unlimited number of :KNOWS relationships.

If we allowed relationships to be traversed any number of times in a pattern (comma-separated or otherwise), then even on this 2-node 1-relationship graph the query would get stuck in an infinite loop, because it could traverse the same :KNOWS relationship over and over and over.

But instead, we have the restriction that a relationship may only be traversed once per path. We traverse it once at the first hop, and then we can't use it again, so there's no risk of an infinite loop. For this reason infinite loop queries are not possible in Cypher.

Hello Andrew,

First of All , Thanks for the detailed level explanation.
I really want to practically perform this on machine and then let you know my understandings or outcome. Is there any role of relationship direction in above explanation?
And just to share , PATH is a trail where no nodes are repeated. And) Trail is a walk where no relationship is repeated. So how above explanation ( shared by you ) is related to PATH | WALK | TRAIL.

Regards
Akshat

Relationship direction only has as much meaning as your model allows.

That is, if conceptually a :KNOWS relationship means that two connected people know each other, then the direction doesn't carry much meaning, and you can omit the direction in your MATCHes.

But if a relation direction carries significance, then you might want to include it in your MATCHes, and you might consider adding relationships in the other direction when desired. For example, a :LIKES relationship might need direction for context, as the interest may not be reciprocal, and the direction of who is doing the liking might be important in your query.

As far as query efficiency, if you can constrain the direction of the relationship, and if that adequately differentiates the relationship from those of the same type going the other direction, then it will be more performant to use the directed relationship in your queries.

In any case, as far as traversal, once is relationship is traversed (doesn't matter which direction is being matched, or in which direction you're traversing) it cannot be traversed again within the same path.

Paths in Cypher are more like trails in graph theory, relationships cannot repeat, but nodes may be repeated.

Hi Akshat,

The ability to output paths depends upon the semantics (underlying algorithms) that a query language uses. Cypher uses no edge repeat isomorphism based semantics. For more technical explanation please see https://dl.acm.org/doi/10.1145/3104031. In simple words, while searching for paths Cypher will only traverses an edge once. Cypher searches for paths where relationships do not repeat however nodes can repeat. In this way infinite paths are not produced in the result set of a query. Infinite paths can exist if there are cycles in graph DB, Cypher puts restrictions to avoid such scenarios.

clem
Graph Steward

This is a subtle point. The comma between two match clauses creates a cartesian product, which can be very expensive. That is:

MATCH condition1, condition2
...

creates every possible combination of conduction1 and conduction2.

But if you do:

MATCH condition1
MATCH condition2
...

you don't get a cartesian product.

For more info, see:

Keep in mind, even though a CartesianProduct operation won't be occurring in the plan, separating out into multiple MATCHes still will effectively result in a cartesian product.

In many cases, this is fine, especially when you're trying to MATCH to two separate nodes by unique identifier (cartesian product of 1 x 1 = 1). This is most common when matching on nodes prior to creating a relationship between them.

Remember that Cypher operations produce rows and execute per row. So the first MATCH will generate some number of rows, and for each of those rows the next MATCH will execute. After the two MATCHes, the interim results will be every result of the first match in combination with every result of the second match.

clem
Graph Steward

Then why do I get a Cartesian Product warning (Yellow Triangle next to the query) in the first version but not the second?

I believe it's looking explicitly for separate disconnected patterns within a MATCH.

Hi Akshat,

In a graph query language such as Cypher, we are define a sub graph structure. This structure is then extracted from the graph database. Now graph can be of any shape such as chain, star, tree, cycle, star chain, forest etc. Chains can be easily expressed without even using commas, for example, (a)--(b)--(c)--(d) is a chain. However, if we want to express a graph structure that has another outgoing edge from node (b), then we will use a comma for example, (a)--(b)--(c)--(d), (b)-->(e). This is a basic explanation only.