Match several node property values in Cypher / Neo4J - neo4j

using Cypher 2 I want to find all the nodes of a certain label (Context), which are called either "health" or "opinion".
The query that works is:
MATCH (c:Context) WHERE c.name="health" OR c.name="opinion" RETURN c;
But I'm wondering if Cypher has a syntax that I could put it into the first MATCH part, something like this:
MATCH (c:Context{name:"health"|name:"opinion})
The example above doesn't work, but I'm just showing it to let you know what I mean.
Thank you!

Alternatively, you can do this:
MATCH (c:Context) WHERE c.name IN ['health', 'opinion'] RETURN c
Still not in the "MATCH" statement, but a little easier as your list of possible values grows.

You could do
MATCH (c:Context {name:"health"}), (d:Context { name:"opinion"})
RETURN c,d

Related

Neo4J, Match node with "OR"

Helo,
i want to match a graph where a node can be typeX or typeY
my first thought was:
match (:typeX|typeY)-[]-(z) return z
But this doesn´t work :(
Is there any way without typing the query twice?
Like this:
match (:typeX)-[]-(z), (:typeY)-[]-(z) return z
Can someone help me?
thank you in advance :)
One way is
MATCH (n) WHERE labels(n) IN ['typeX','typeY']
WITH n
MATCH (n)-[]-(z)
RETURN z
However, if "either typeX or typeY" are queried frequently and share some common purpose in your domain, you could add another common label to them like "commonXY" and query using that label instead.
Unfortunately there's not a good efficient way to do this without sacrificing performance. All other current answers are forced to scan all nodes and then filter on their labels, which isn't performant with large numbers of nodes (PROFILE the queries). All the efficient means I know of are more verbose.
You can perform a UNION of the two queries to return nodes one hop from all :typeX and :typeY nodes.
match (:typeX)--(z)
return z
union
match (:typeY)--(z)
return z
This query will work even if n has multiple labels:
MATCH (n)
WHERE ANY(lab IN labels(n) WHERE lab IN ['typeX', 'typeY'])
MATCH (n)--(z)
RETURN z
there is a n predicate n:Label
MATCH (n)--(z)
WHERE n:typeX OR n:typeY
RETURN z

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.

Neo4J/Cypher: is it possibile to filter the length of a path in the where clause?

Let's suppose we have this simple pattern:
p=(a)-[r]-(b)
where nodes a and b have their own properties already set in the WHERE clause (e.g. a:Movie AND a.title = "The Matrix" AND b:Movie).
I'd like to add another condition in the WHERE clause like
LEGHT(p) =2 OR LENGTH(p)>6
(not the correct syntax, I know)
As far as I know, it is possibile to specify the length of a path in the MATCH clause with the syntax [r*min..max] but that doesn't cover the case I'm looking for.
Any help would be appreciated =)
Yes, that does work in neo4j, exactly as you've specified.
Sample data:
create (a:Foo)-[:stuff]->(b:Foo)-[:stuff]->(c:Foo);
Then this query:
MATCH p=(x:Foo)-[:stuff*]->(y:Foo)
WHERE length(p)>1 RETURN p;
Returns only the 2-step path from a->b->c and not either one-step path (a->b or b->c)
Does this work for you?
MATCH p=(a)-[r*2..]-(b)
WHERE LENGTH(r) = 2 OR LENGTH(r) > 6
RETURN p
Note that with a large DB this query can take a very long time, or not finish, since the MATCH clause does not set an upper bound on the path length.

Neo4j/Cypher - find nodes that link to all given nodes - the right way?

I would like to filter nodes that link to all given nodes.
I've tried this query which returns every page that has one of ['passionate', 'eye', 'ear'] :
MATCH (includeWord:Word) WHERE includeWord.Text IN ['passionate', 'eye', 'ear']
MATCH (p:Page)-[:CONTAINS]->(includeWord:Word)
// WHERE p LINKS TO ALL includeWord AT LEAST ONCE ?
RETURN DISTINCT p
This query didn't do the job either :
MATCH (p:Page)-[:CONTAINS]->(includeWord:Word)
WHERE includeWord.Text = 'passionate'
AND includeWord.Text = 'eye'
AND includeWord.Text = 'ear'
RETURN DISTINCT p
And programmatically speaking it's sort of ugly and unsecure, and I'm sure some of you out there know a better answer.
Maybe something like
MATCH (p:Page)-[:CONTAINS]->(includeWord:Word)
WHERE includeWord.Text IN_ALL ['passionate', 'eye', 'ear']
RETURN DISTINCT p
?
Thanks !
Oops,
I found this question neo4j cypher - how to find all nodes that have a relationship to list of nodes :
And for my query it works perfectly.
It looks like this :
MATCH (p:Page)-[:CONTAINS]->(word:Word)
WITH p, collect(word.Text) as words
WHERE ALL (v IN ['passionate', 'eye', 'ear'] WHERE v IN words)
RETURN p
It need a bit of reading before understanding !
I'm actually a bit surprised that that second query works. It's my understanding that a MATCH in cypher works by considering one subgraph at a time, so that would mean that it's impossible for any given :Word to have a Text that is both 'passionate' and 'eye' at the same time. I would expect that you would need to do something like:
MATCH
(p:Page),
p-[:CONTAINS]->(word1:Word),
p-[:CONTAINS]->(word2:Word),
p-[:CONTAINS]->(word3:Word)
WHERE
word1.Text = 'passionate',
word2.Text = 'eye',
word3.Text = 'ear'
RETURN p
Obviously that's a more complicated query, though...

Neo4j Match multiple labels

There is the situation in which i need to match any one of the label of node.
We can do it for relationship types like
(n)-[:KNOWS|LOVES]->(m)
Can we match node labels like this?
eg.
MATCH (c:computer)<-[:belongs_to]-(comp:HP|IBM)
return comp
Currently I have tried this and it gives results, Is there any simpler way?
MATCH (c:computer)<-[:belongs_to]-(comp)
WHERE 'HP' IN labels(comp) OR 'IBM' IN labels(comp)
return comp
I Think
WHERE 'HP' IN labels(comp) OR 'IBM' IN labels(comp)
AND
WHERE comp:HP OR comp:IBM
Will work with same manner second one is simple to use
This form of your last query is at least simpler to write and more easily understood:
MATCH (c:computer)<-[:belongs_to]-(comp)
WHERE comp:HP OR comp:IBM
return comp;
Currently facing this same problem.
Since I have quite a few labels to match on (revealing a bit of a flaw in my architecture!) I found the following to solve this problem concisely:
MATCH (n:computer)
WHERE any(label in labels(n) WHERE label in ['HP', 'IBM'])
RETURN n

Resources