Optional nodes in a path? - neo4j

I'm trying to write a query where I get the :LIKES relationships.
(:USER)
|
[:CREATED]
|
(:POST)<-[:LIKES]-(:USER)
|
[:RESHARED]
|
(:POST)<-[:LIKES]-(:USER)
I was trying something along the lines of:
MATCH (u:USER {name: "Lamoni"})-[:CREATED]-(p:POST)
OPTIONAL MATCH p<-[:LIKES]-(u2:USER)
OPTIONAL MATCH p<-[:RESHARED]-(p2:POST)<-[:LIKES]-(u3:USER)
Any ideas on an optimal way to do this and be able to order them by a property called created_at in a descending order?
Thanks!

If the POST structure always looks like this you can try:
// match the whole user-post-post path
MATCH (u:USER {name: "Lamoni"})-[:CREATED]-(p_direct:POST)-[:RESHARED]-(p_shared:Post)
WITH u, p_direct, p_shared
OPTIONAL MATCH (p_direct)<-[:LIKES]-(u2:USER)
OPTIONAL MATCH (p_shared)<-[:LIKES]-(u3:USER)
RETURN u.name, p_direct.xyz, collect(u2.name), p_shared.xyz, collect(u3.name)
If you just want all USERS that like a POST by a given USER (independent of the type of POST, created or shared) you can also collect all POST:
MATCH (u:USER {name: "Lamoni"})-[:CREATED|RESHARED*1..2]-(p:Post)
WITH u, p
OPTIONAL MATCH (p)<-[:LIKES]-(u2:USER)
WITH u.name, p, u2
ORDER BY u2.created_at
RETURN u.name, p, collect(u2.name)

Related

query with both MATCH and CREATE

I have 3 Nodes:
:User which has a unique id property
:Palette which has a unique name property
:Color which has a unique hex property
When a user saves a pallete I would like to:
create a new pallete if a palette with this name does not exist, and add a :CREATED relationship from the :User to the :Palette
create a :SAVED relationship from the :User to the :Palette if one does not exist
Afterwards I would like to delete all :INCLUDES relationships that this :Palette has to :Color nodes inside the database (in order to create new ones afterwards).
This is my query (I replaced the variables with hardcoded strings so it's easier to execute):
MATCH (u:User)
WHERE u.id = '4f3d1904'
MERGE (p:Palette {name: 'Test'})
ON CREATE
SET p.name = "Test"
MERGE (u)-[cr:CREATED]->(p)
MERGE (u)-[sa:SAVED]->(p)
MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()
DELETE in
When running the query I get the following error:
WITH is required between MERGE and MATCH (line 8, column 1 (offset: 181))
"MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()"
^
But if I add a WITH I get the following:
MATCH (u:User)
WHERE u.id = '4f3d1904'
MERGE (p:Palette {name: 'Test'})
ON CREATE
SET p.name = "Test"
MERGE (u)-[cr:CREATED]->(p)
MERGE (u)-[sa:SAVED]->(p)
WITH
MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()
DELETE in
Invalid input ')': expected whitespace or a relationship pattern (line 9, column 32 (offset: 217))
"MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()"
^
What am I doing wrong?
MERGE and MATCH stages (or MATCH and MATCH) require a WITH between them in order to use the result of the former in the latter.
In your case you can use the p that you already have like this:
...
WITH p
MATCH (p)-[in:INCLUDES]->()
DELETE in
So you won't need to find it again. without the WITH, it is like two different queries.

Aggregating multiple matches before ordering results

I have an Activity node (a) that refers to (:Something) which can be matched by a direct :LIKE relationship to :User 'me' OR a :LIKE relationship by a :FRIEND.
The first relationship can be described as:
MATCH (a)-[:REF]->(:Something)<-[:LIKE]-(:User {user: 'me'})
While the second relationship can be described as:
MATCH (a)-[:REF]->(:Something)<-[:LIKE]-(:User)<-[:FRIEND]-(:User {user: 'me'})
How would I go about grouping all of the different activity nodes (a) so that I can sort the full list by timestamps? It would look something like:
MATCH
(a)-[:REF]->(:Something)<-[:LIKE]-(:User {user: 'me'})
OR
(a)-[:REF]->(:Something)<-[:LIKE]-(:User)<-[:FRIEND]-(:User {user: 'me'})
RETURN a
ORDER BY a.ts DESC
In your case, you can use the variable-length pattern matching:
// u = node "me" or the node "friend"
MATCH
(:User {user: 'me'})-[:FRIEND*0..1]->(u:User)
MATCH
(a)-[:REF]->(:Something)<-[:LIKE]-(u)
RETURN DISTINCT a
ORDER BY a.ts DESC
Update: If the queries are completely different, then you can collect the result of the first query, then the result of the second query, sum up and unwind:
MATCH
(a1)-[:REF]->(:Something)<-[:OWN]-(:User {user: 'me'})
WITH
collect(DISTINCT a1) AS ac1
MATCH
(a2)-[:REF]->(:Something)<-[:INCLUDES]-(:SomethingElse)<-[:LIKE]-(:User {user: 'me'})
WITH
ac1, collect(DISTINCT a2) AS ac2
UNWIND
ac1 + ac2 AS a
RETURN DISTINCT a
ORDER BY a.ts DESC

neo4j pass parameter to variable length relationship

How do I use parameters with variable length relationships?
MATCH path=(:Person {id: {id}})=[:HAS_FRIEND*0..{num_friends}]->(:Person)
I'm trying to create a generic query so that I can pass a value 'num_friends' into the cypher query for various levels of relationships that I need.
I get an error so I'm wondering how something like this would be done?
Parameters can not be used as hops count.
But you can use path expander from apoc:
match (P:Person {id: {id}}) with P
call apoc.path.expand( P, 'HAS_FRIEND>', 'Person', 0, {num_friends}) yield path
return path
Adapted for comment:
match (P:Person {id: {id}}) with P
call apoc.path.expand( P, 'HAS_FRIEND>', 'Person', 0, {num_friends}) yield path
with path, last(nodes(path)) as lst where not (lst)-[:HAS_FRIEND]->(:Person)
return path

Chaining result with "WITH" doesn't work when the subsequent query doesn't have matched result in Neo4j cypher query

For example, I created two linked nodes:
create (a:ACTOR {id: "a1", name: "bruce wellis"})
create (m:MOVIE {id: "m1", title: "die hardest"})
create (a)-[:ACTED_IN]->(m)
1. From this cypher query:
match (a:ACTOR {id: "a1"})
with a
optional match (m:MOVIE {id: "m1"})
set m += {
title: "die easier"
}
return a;
I can have result:
+-----------------------------------------+
| a |
+-----------------------------------------+
| Node[1000]{name:"bruce wellis",id:"a1"} |
+-----------------------------------------+
1 row
Properties set: 1
The query successfully returned the actor node.
2. (UPDATED) But if you make the match MOVIE subquery failed:
match (a:ACTOR {id: "a1"})
with a
optional match (m:MOVIE {id: "mm"})
set m += {
title: "die easier"
}
return a;
I got error:
CypherTypeException: Expected m to be a node or a relationship, but it was :`null`.
How to make the second query returning matched actor result?
A MATCH that fails to match anything will always return no rows.
So, in #2, since the second MATCH failed, it returns no rows.
You could use OPTIONAL MATCH in place of the second MATCH, and you should see results.
[EDITED]
For the Updated question, this (somewhat ugly) workaround should work:
MATCH (a:ACTOR {id: "a1"})
WITH a
OPTIONAL MATCH (m:MOVIE {id: "mm"})
WITH a, COLLECT(m) AS cm
FOREACH(m IN cm | SET m += {title: "die easier"})
RETURN a;

match in clause in cypher

How can I do an match in clause in cypher
e.g. I'd like to find movies with ids 1, 2, or 3.
match (m:movie {movie_id:("1","2","3")}) return m
if you were going against an auto index the syntax was
START n=node:node_auto_index('movie_id:("123", "456", "789")')
how is this different against a match clause
The idea is that you can do:
MATCH (m:movie)
WHERE m.movie_id in ["1", "2", "3"]
However, this will not use the index as of 2.0.1. This is a missing feature in the new label indexes that I hope will be resolved soon. https://github.com/neo4j/neo4j/issues/861
I've found a (somewhat ugly) temporary workaround for this.
The following query doesn't make use of an index on Person(name):
match (p:Person)... where p.name in ['JOHN', 'BOB'] return ...;
So one option is to repeat the entire query n times:
match (p:Person)... where p.name = 'JOHN' return ...
union
match (p:Person)... where p.name = 'BOB' return ...
If this is undesirable then another option is to repeat just a small query for the id n times:
match (p:Person) where p.name ='JOHN' return id(p)
union
match (p:Person) where p.name ='BOB' return id(p);
and then perform a second query using the results of the first:
match (p:Person)... where id(p) in [8,16,75,7] return ...;
Is there a way to combine these into a single query? Can a union be nested inside another query?

Resources