neo4j all relations to certain depth between 2 sets - neo4j

Playing with simple neo4j queries. My base match is :
MATCH (:Movie { id: '10' })-[*0..3]-(p:Producer)
RETURN p.id
This returns some results, so obviously there are some relations between movie-10 and any producer. Part of the result set is:
'producer_12'
'producer_18'
'producer_36'
.........
Now I want to return all relations between movie-10 and producer_12 or producer_18 up to 3 hops. I modified my match.
MATCH (:Movie { id: '10' })-[*0..3]-(p:Producer)
WHERE p.id IN ['producer_12', 'producer_18']
RETURN p.id
And this already doesn't return any value, while I expected producers 12 and 18 to be in the answer. Besides I can't find the way to label the relation. This is not accepted. [r:*0..3].
My final query must be to get all relations between 2 sets (movies 10, 12 or 15) and (producers 12 or 18) for example.

I simulated your scenario here.
The sample data:
CREATE (movie:Movie {id : '10'})
CREATE (producer12:Producer {id:'producer_12'})
CREATE (producer18:Producer {id:'producer_18'})
CREATE (producer36:Producer {id:'producer_36'})
CREATE (movie)-[:PRODUCTED_BY]->(producer12)
CREATE (movie)-[:PRODUCTED_BY]->(producer18)
CREATE (movie)-[:PRODUCTED_BY]->(producer36)
Querying:
MATCH (:Movie { id: '10' })-[*0..3]-(p:Producer)
WHERE p.id IN ['producer_12', 'producer_18']
RETURN p.id
The result:
╒═════════════╕
│"p.id" │
╞═════════════╡
│"producer_12"│
├─────────────┤
│"producer_18"│
└─────────────┘
Probably your id property of :Movie nodes is not a string but an integer. So try changing your query to:
MATCH (:Movie { id: 10 })-[*0..3]-(p:Producer)
WHERE p.id IN ['producer_12', 'producer_18']
RETURN p.id
That is: change '10' to 10.
Besides I can't find the way to label the relation. This is not
accepted. [r:*0..3].
This is because you are not using a type in the relationship. The : is only used in conjunction with a type (for example, [r:SOME_TYPE*0..3]). So remove the :, this way: [r *0..3].
EDIT:
From comments:
About the last sentence: It's still working but it says that "This
feature is deprecated and will be removed in future versions. Binding
relationships to a list in a variable length pattern is deprecated" –
user732456 3 hours ago
Binding relationships to a list in a variable length pattern is deprecated since 3.2.0-rc1.
According this pull request Cypher queries like:
MATCH (n)-[rs*]-() RETURN rs
will generate a warning and the canonical way to write the same query is:
MATCH p=(n)-[*]-() RETURN relationships(p) AS rs

Related

Neo4j variable as property name

I'm trying to use a variable as a property name in neo4j
MATCH(e: Event {id: "123"})
MATCH(u: User {id: "456"})
MERGE (u)-[a:ACTION]->(e)
ON MATCH
SET e[a.t] = e[a.t] - 1
This gives the error:
Invalid input '[': expected an identifier character, whitespace, '{', 'x/X', node labels, a property map, a relationship pattern, '.', '(', '=' or "+=" (line 5, column 6 (offset: 96))
"SET e[a.t] = e[a.t] - 1"
While according to this forum: it should be possible.
Neo4j forum
What am I doing wrong?
Whenever we want to create a relationship dynamically, and set its properties dynamically, we should use apoc.merge.relationship function. In this function you can dynamically provide startNode, endNode, properties to set onMatch and onCreate for the relationship and some other params as well.
For your use case, you can try something like this:
MATCH(e: Event {id: "123"})
MATCH(u: User {id: "456"})
OPTIONAL MATCH (u)-[rel:ACTION]->(e)
CALL apoc.merge.relationship(
u,
"ACTION",
{},
{},
e,
apoc.map.setKey({}, toString(rel.t), rel.t - 1)
)
YIELD rel
RETURN rel;
Also, note that we have to fetch the relationship using OPTIONAL MATCH so that we can access its properties when calling the apoc function. Finally, you will also need to install APOC library for the above query to work.
The example as you pointed out in the forum uses the node['property'] form in the WHERE clause. It does not work when updating the property like you did.
For example, below query works
match (n:Actor)
where n['name'] = "Neo"
set n.age = 50
return n
Result:
n
(0:Actor {age:50, name:"Neo"})
Query took 14 ms and returned 1 rows.
Updated the graph - set 1 property
BUT THIS WILL NOT WORK:
set n['age'] = 50

Returning multiple columns

Hi All newbie here in Neo4J,
I am trying to return keys or properties using the following simple query in the neo4J broswer environment.
MATCH (n:movies),(m:people)
RETURN properties(n,m)
What I am trying to achieve is to return the properties for both the movies and people nodes
However, I would always get an error
Too many parameters for function 'properties' (line 2, column 9 (offset: 36))
" RETURN properties(n,m)"
I have tried,
MATCH (n:movies),(m:people)
RETURN properties(k) in [n,m]
The error I would get
Variable `k` not defined (line 2, column 20 (offset: 47))
" RETURN properties(k) in [n,m]"
I am trying to pass a list here into k but NEO4J is not permitting me to do so. Is it even possible to pass a list into the function properties() ??
Thank you in advance.
The properties function takes exactly one node or a relationship as input.
MATCH (n:movies),(m:people) RETURN properties(n), properties(m)
will create a Cartesian Product.
i.e. If you have five movies and ten people, you will get a result of all 50 combinations.
If you aren't looking for a cartesian product, you would have to define a specific pattern or restrict the MATCH clause further.
If you want just the individual properties without combining them, consider Union.
MATCH (n:Movie)
RETURN properties(n) as `Properties`
UNION ALL
MATCH (m:Person)
RETURN properties(m) as `Properties`
Why am I using aliases for a seemingly simple query? To avoid this:
All sub queries in an UNION must have the same column names (line 3,
column 1 (offset: 39))
For working with lists:
The collect function lets you create/construct a list from the results while UNWIND expands a list into a sequence of rows.
properties() only takes one argument, you can try
MATCH (n:movies),(m:people) RETURN properties(n) as prop_n, properties(m) as prop_m
or more optimal query would be
MATCH (n:movies) optional match (m:people) RETURN properties(n) as prop_n, properties(m) as prop_m
MATCH (n:movies),(m:people)
RETURN properties(k) in [n,m]
since you have not defined k so you are getting the error. Also according to doc properites() takes "An expression that returns a relationship, a node, or a map" as an argument. Your query is not supported.

How to match Neo4j node or relationship count when filtering on node or relationship property

I have a graph:
CREATE (c1:Contract { id: "V-9087-4321" }),
(c2:Contract { id: "V-8046-2304" }),
(p:Partner {id: "C-4837-4536"}),
(p)-[:POLICY_HOLER]->(c1),
(p)-[:POLICY_HOLER]->(c2)
MATCH (p:Partner) WHERE p.id = "C-4837-4536"
CREATE (c:Claim { id: "ef70", date: "2019-04-27" }),
(p)-[:CLAIMANT {on: "2019-04-27"}]->(c)
MATCH (p:Partner) WHERE p.id = "C-4837-4536"
CREATE (c:Claim { id: "ab90", date: "2019-04-28" }),
(p)-[:CLAIMANT {on: "2019-04-28"}]->(c)
I want to find all partners who have more than one claim after April 1st:
I've tried this:
MATCH (claim:Claim)<-[r:CLAIMANT]-(p:Partner)
WITH count(r) as cnt, p, claim
WHERE cnt > 1 AND claim.date > '2019-04-01'
RETURN cnt, p.id
It doesn't work, because (if I remove the where clause), it is returning the two claims each with cnt=1, rather than actually aggregating them. I've tried selecting based on claim.date as well as r.on.
How can this be done with Cypher?
#cybersam explained about aggregate functions very well. I suggest looking at their answer to understand how aggregate functions work. But their query is not filtering claims based on the count.
You can use the following query to find all partners who have more than one claim after April 1st:
MATCH (claim:Claim)<-[r:CLAIMANT]-(p:Partner)
WHERE claim.date > '2019-04-01'
WITH count(r) as cnt, p
WHERE cnt > 1
RETURN cnt, p.id
The neo4j aggregating functions (like COUNT()) use the non-aggregating terms in the same WITH or RETURN clause as the "grouping keys".
In your example's WITH clause, the grouping keys are p and claim, so the COUNT() function is counting the number of r relationships between each distinct p and claim pair (and so the count will always be 1). What you actually want instead is the number of r relationships for each p.
Assuming every Partner node has a unique id, you can find all partners who have more than one claim after April 1st using this simplified query:
MATCH (claim:Claim)<-[:CLAIMANT]-(p:Partner)
WHERE claim.date > '2019-04-01'
RETURN COUNT(claim) AS cnt, p.id
If you also want to return a list of all the claims for each matching partner:
MATCH (claim:Claim)<-[:CLAIMANT]-(p:Partner)
WHERE claim.date > '2019-04-01'
RETURN COUNT(claim) AS cnt, COLLECT(claim) AS claims, p.id
In addition, faster searches you should create either an index or a uniqueness constraint on :Claim(date).

cypher - relationship traversal vs ordering by property

graph snippet
I have a Shape with a list of Points.
I have the following requirements:
1) retrieve an ordered set of Points;
2) insert/remove a Point and preserve the order of the rest of the Points
I can achieve this by:
A) Point has a sequence integer property that could be used to order;
B) Add a :NEXT relationship between each Point to create a linked list.
I'm new to Neo4j so not sure which approach is preferable to satisfy the requirements?
For the first requirement, I wrote the following queries and found the performance for the traversal to be poor but Im sure its a badly constructed query:
//A) 146 ms
Match (s:Shape {id: "1-700-y11-1.1.I"})-[:POINTS]->(p:Point)
return p
order by p.sequence;
//B) Timeout! Bad query I know, but dont know the right way to go about it!
Match path=(s:Shape {id: "1-700-y11-1.1.I"})-[:POINTS]->(p1:Point)-[:NEXT*]->(p2:Point)
return collect(p1, p2);
To get ordered list of points use a slightly modified version of the second query:
Match path=(s:Shape {id: "1-700-y11-1.1.I"})-[:POINTS]->(P1:Point)
-[:NEXT*]-> (P2:Point)
WHERE NOT (:Point)-[:NEXT]->(P1) AND
NOT (P2)-[:NEXT]->(:Point)
RETURN TAIL( NODES( path) )
And for example query to delete:
WITH "id" as pointToDelete
MATCH (P:Point {id: pointToDelete})
OPTIONAL MATCH (Prev:Point)-[:NEXT]->(P)
OPTIONAL MATCH (P)-[:NEXT]->(Next:Point)
FOREACH (x in CASE WHEN Prev IS NOT NULL THEN [1] ELSE [] END |
MERGE (Prev)-[:NEXT]->(Next)
)
DETACH DELETE P

How to query for multiple OR'ed Neo4j paths?

Anyone know of a fast way to query multiple paths in Neo4j ?
Lets say I have movie nodes that can have a type that I want to match (this is psuedo-code)
MATCH
(m:Movie)<-[:TYPE]-(g:Genre { name:'action' })
OR
(m:Movie)<-[:TYPE]-(x:Genre)<-[:G_TYPE*1..3]-(g:Genre { name:'action' })
(m)-[:SUBGENRE]->(sg:SubGenre {name: 'comedy'})
OR
(m)-[:SUBGENRE]->(x)<-[:SUB_TYPE*1..3]-(sg:SubGenre {name: 'comedy'})
The problem is, the first "m:Movie" nodes to be matched must match one of the paths specified, and the second SubGenre is depenedent on the first match.
I can make a query that works using MATCH and WHERE, but its really slow (30 seconds with a small 20MB dataset).
The problem is, I don't know how to OR match in Neo4j with other OR matches hanging off of the first results.
If I use WHERE, then I have to declare all the nodes used in any of the statements, in the initial MATCH which makes the query slow (since you cannot introduce new nodes in a WHERE)
Anyone know an elegant way to solve this ?? Thanks !
You can try a variable length path with a minimal length of 0:
MATCH
(m:Movie)<-[:TYPE|:SUBGENRE*0..4]-(g)
WHERE g:Genre and g.name = 'action' OR g:SubGenre and g.name='comedy'
For the query to use an index to find your genre / subgenre I recommend a UNION query though.
MATCH
(m:Movie)<-[:TYPE*0..4]-(g:Genre { name:'action' })
RETURN distinct m
UNION
(m:Movie)-[:SUBGENRE]->(x)<-[:SUB_TYPE*1..3]-(sg:SubGenre {name: 'comedy'})
RETURN distinct m
Perhaps the OPTIONAL MATCH clause might help here. OPTIONAL MATCH beavior is similar to the MATCH statement, except that instead of an all-or-none pattern matching approach, any elements of the pattern that do not match the pattern specific in the statement are bound to null.
For example, to match on a movie, its genre and a possible sub-genre:
OPTIONAL MATCH (m:Movie)-[:IS_GENRE]->(g:Genre)<-[:IS_SUBGENRE]-(sub:Genre)
WHERE m.title = "The Matrix"
RETURN m, g, sub
This will return the movie node, the genre node and if it exists, the sub-genre. If there is no sub-genre then it will return null for sub. You can use variable length paths as you have above as well with OPTIONAL MATCH.
[EDITED]
The following MATCH clause should be equivalent to your pseudocode. There is also a USING INDEX clause that assumes you have first created an index on :SubGenre(name), for efficiency. (You could use an index on :Genre(name) instead, if Genre nodes are more numerous than SubGenre nodes.)
MATCH
(m:Movie)<-[:TYPE*0..4]-(g:Genre { name:'action' }),
(m)-[:SUBGENRE]->()<-[:SUB_TYPE*0..3]-(sg:SubGenre { name: 'comedy' })
USING INDEX sg:SubGenre(name)
Here is a console that shows the results for some sample data.

Resources