Cypher query syntax

Hello All,

I'm learning data modelling my creating an express app in javascript and facing a few issues. For node and express linting isn't provided to highlight syntax errors. Could you please point me to the mistake in the below code?

    create (m:Message{msg:$msg}) match (u1: User),(u2:User) where u1.id=$fromUserId and u2.id=$toUserId create (u1)-[s:SENT]->(m)-[b:SENT_TO]->(u2)

Thanks,
Arun

Hello @arunkumar413 :slight_smile:

MATCH (u1:User {id: $fromUserId})
MATCH (u2:User {id: $toUserId})
CREATE (u1)-[:SENT]->(:Message {msg: $msg})-[:SENT_TO]->(u2)

Regards,
Cobra

Should we use compulsorily use match two times here. Can't we go with the comma operator to refer to the same nodes?

MATCH (u1:User {id: $fromUserId}), (u2:User {id: $toUserId})

In my code I'm first creating a message and getting it's reference as m and using it in CREATE statement. It's looks valid. Any explanation on why my code didn't work would be of great help.

Maybe the space between the : and User in the match in your query.

In this context both queries do the same thing. The comma becomes important when you are traversing relationships. A relationship will only be traversed once per MATCH. Say you had a graph with:

CREATE (:Person {name: "Tom Hanks"})-[:ACTED_IN]->(m:Movie {title: "The Green Mile"}),
(:Person {name: "Michael Clarke Duncan"})-[:ACTED_IN]->(m)

Running this query wouldn't return Tom Hanks in the result set:

MATCH (:Person {name: "Tom Hanks"})-[r1:ACTED_IN]->(m:Movie {title: "The Green Mile"})),
 (m)<-[r2:ACTED_IN]-(p)
RETURN p

Because r1 would never be included in r2.

However, adding another MATCH means the relationship would be traversed as part of the second clause:

MATCH (:Person {name: "Tom Hanks"})-[r1:ACTED_IN]->(m:Movie {title: "The Green Mile"}))
MATCH (m)<-[r2:ACTED_IN]-(p)

You can't mix match and create without 'with' clause. The editor gives this warning.

match (u1:User)
match (u2:User)
where u1.id=$fromUserId and u2.id=$toUserId
with u1, u2
merge (u1)-[:SENT]->(:Message{msg:$msg})-[:SENT_TO]->(u2)

You want to use 'merge' to create the path, as create will create it again if the query is repeated, i.e. each time it is run a new message node will be created and linked to the existing u1 and u2 nodes. Merge only adds the missing parts.

An alternative implementation to show further use of the 'with' clause:

merge (m:Message{msg:$msg})
with m
match (u1:User)
match (u2:User)
where u1.id=$fromUserId and u2.id=$toUserId
with m, u1, u2
merge (u1)-[:SENT]->(m)-[:SENT_TO]->(u2)

I used 'merge' with the message node, so it only gets created once if the query is executed multiple times.

I'm trying to follow this example which doesn't contain any merge and with clause.

MATCH
  (a:Person),
  (b:Person)
WHERE a.name = 'A' AND b.name = 'B'
CREATE (a)-[r:RELTYPE]->(b)
RETURN type(r)

: is a valid delimiter.

The ':' is used to specify the node or relationship type, i.e., Person and RELTYPE in your example.

The author of this cypher statement assumes that nodes a and b exist in the database. If not, either or both match clauses would return null (if a or b or both are missing), thus terminating the query.

Using the create clause in the pattern, will cause the relationships to be created regardless of its existence, thus it gets created each time the query is executed.

You can rewrite the query using merge clause, so redundant data is not created

MERGE (a:Person{name:'A'})
MERGE (b:Person{name:'B'})
MERGE (a)-[r:RELTYPE]->(b)
RETURN type(r)

Or, in one statement:

MERGE (a:Person{name:'A'})-[r:RELTYPE]->(b:Person{name:'B'})
RETURN type(r)

Neo4j Desktop complains that 'with' is required between either 'create' and 'merge' and a following 'match.' It would seem that create and merge are considered terminal statements, so a with clause is required to chain more cypher, by passing previous results to the next cypher block. That being correct, the query posted earlier does not require the second 'with' clause, .i.e., 'with m, u1, u2.' The statement executes fine without it. The cypher query does require the 'with m' clause, as the merge can't be followed with a match without a separating 'with' clause.

Actually the query isn't working

  `MATCH (u: User {id: $senderId}), (v: User {id: $receiverId})` User exists in the database.

Will the below code work?


MATCH (u: User {id: $senderId}), (v: User{id:$receiverId})
CREATE(m:Message{msg:$msg})
CREATE(u)-[s1:SENT]->(m)-[s2:SENT_TO]->(v)

Yes, as you don't need the 'with' clause between a match followed by a create. You need it when transitioning from a create/merge to a match.

Note, the use of create clauses will cause the message node and the relationships to be created each time the query is run. It is safer to use merge to avoid redundant data, unless you are sure in your use case that the items don't already exist.

Thanks. Is it possible to disable duplicate or enforce only single relationship of a type?

You can create uniqueness constraints by label and property.

1 Like

Just tried this query in on my local machine. It executes but the message node and it's relationships aren't created

Are both match nodes in the database?

Yes, they exist before I ran this query.

That is unexpected. I tested it myself. To verify, execute the two match statements separately to verify that results are returned by each.

Even match isn't working. Is it because of trying to match with the ids created by system?