CREATE GRAPH g1
USE GRAPH g1
CREATE (Person:john)-[CALLS]->(Person:mike)

CREATE (Person:mike)-[CALLS]->(Person:john)

CREATE (Person:abbie)-[CALLS]->(Person:polly)

CREATE (Person:polly)-[CALLS]->(Person:john)

Here as we see, John calls Mike and then Mike calls John. It will be a complicated query to figure out all those who called each other. If we simply run following, then it will return results which will not be desirable.

S=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*)
+------------+-----+------------------------+
|sub         |pred | obj                    |
+------------+-----+------------------------+
|Person:john |CALLS| Person:mike            |
+------------+-----+------------------------+
|Person:abbie|CALLS| Person:polly           |
+------------+-----+------------------------+
|Person:polly|CALLS| Person:john            |
+------------+-----+------------------------+
|Person:mike |CALLS| Person:john            |
+------------+-----+------------------------+

As we see, we get all persons calling other.

Let's modify it to ensure that we have people calling each other. But how do we do that?

We somehow need to know the Person1 calling Person2, then connect Person2 to Person1 with the same "CALLS" relation. This is not easy as we wish to know the list for all the persons (note Person:*). The query can become really complicated and it may not even be feasible with single query.

But if we look closely, we are talking about a relationship which are symmetrical to each other. Hence it will be good if we can somehow tell that in the query.

"SYMM" keyword does exactly that. Look at the query and how it simplifies things for us.

S{SYMM}=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*)
+-----------+-----+-----------+
|sub        |pred |obj        |
+-----------+-----+-----------+
|Person:john|CALLS|Person:mike|
+-----------+-----+-----------+
|Person:mike|CALLS|Person:john|
+-----------+-----+-----------+

This is quite powerful, and, in many cases, we need this, having "SYMM" solves the problem really elegantly.

Let's modify the query a bit, now we wish to know all persons who called someone but didn't receive call from the people who the called.

This is quite opposite to "SYMM", which is "ASYMM", so let's use "ASYMM" here.

S{ASYMM}=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*)
+------------+-----+------------+
|sub         |pred |obj         |
+------------+-----+------------+
|Person:abbie|CALLS|Person:polly|
+------------+-----+------------+
|Person:polly|CALLS|Person:john |
+------------+-----+------------+

Now add following triple:

CREATE (Person:polly)-[CALLS]->(Person:john)

Note that this is a repeated triple, it already exists but still a valid triple. Now if we run following query, (Polly, John) combination appears twice in the list of all the callers, since she has made two calls to John.

s=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*); RETURN p1.name AS Caller, p2.name AS Callee
+------+------+
|Caller|Callee|
+------+------+
|john  |mike  |
+------+------+
|abbie |polly |
+------+------+
|polly |john  |
+------+------+
|polly |john  |
+------+------+
|mike  |john  |
+------+------+

But that's not what we wish to see, we want to know all the caller and callee set uniquely. How do we enforce uniqueness for (sub, obj) pair?

We can use "UNIQUE" keyword here for this, it does exactly same.

s{UNIQUE}=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*); RETURN p1.name AS Caller, p2.name AS Callee
+------+------+
|Caller|Callee|
+------+------+
|john  |mike  |
+------+------+
|abbie |polly |
+------+------+
|polly |john  |
+------+------+
|mike  |john  |
+------+------+

This is good. Now what if we wish to know all unique callers or callee, not at the pair level (i.e., uniqueness at (sub,obj)), but simply uniqueness based on what we select in the query (or RETURN from the query). This uniqueness is very contextual, let's see what this means.

Let's add another triple where polly again calls someone but this time a different person, let's say Travis.

CREATE (Person:polly)-[CALLS]->(Person:travis)

Now if we run the previous query again and let's observe the result.

s{UNIQUE}=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*); RETURN p1.name AS Caller, p2.name AS Callee
+------+------+
|Caller|Callee|
+------+------+
|john  |mike  |
+------+------+
|abbie |polly |
+------+------+
|polly |john  |
+------+------+
|polly |travis|
+------+------+
|mike  |john  |
+------+------+

Here, the sets (Polly, John) and (Polly, Travis) shows up as they are unique as a pair. But what if we just want to see caller or callee. Look at the following query.

s{UNIQUE}=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*); RETURN p1.name AS Caller
+------+
|Caller|
+------+
|john  |
+------+
|abbie |
+------+
|polly |
+------+
|polly |
+------+
|mike  |
+------+

Here we see Polly appearing twice. Although this is correct from the point of view that Polly calls two different persons, so these calls are unique, but here in this query we just want to see unique callers (not the unique calls).

Therefore, there is a context here within the query which is the uniqueness is needed for the given attribute which is being returned from the query. To do this, we will use uniqueness in context, which is UNIQUE_IN_CONTEXT

s{UNIQUE_IN_CONTEXT}=>(@p1 Person:*)-[@c CALLS]->(@p2 Person:*); RETURN p1.name AS Caller
+------+
|Caller|
+------+
|john  |
+------+
|abbie |
+------+
|polly |
+------+
|mike  |
+------+

Now we get the right result (in the context) what we were looking for.