Going through a list in Neo4j - neo4j

I'm having trouble with lists in neo4j. I need to go through a list and compare each element with a property of another node. I tried something like this
MATCH (C:Courses),(S:Student)
WHERE
C.courseid = "8" AND
S.userid = FOREACH(l IN SPLIT(C.students,",")
CREATE (C)<-[E:enrolled]-(S)
RETURN E
where C.students is a string of values separated with ",". FOREACH was the only function that I found to do this, but I don't think I can use it that way. Any ideas of how I can do that?

This query should do what you intended:
MATCH (c:Courses)
WHERE c.courseid = '8'
MATCH (s:Student)
WHERE s.userid IN SPLIT(c.students, ',')
CREATE (c)<-[e:enrolled]-(s)
RETURN e
If you have a significant number of courses or students, then you should also create indexes for :Courses(courseid) and/or :Student(userid).

Related

Cypher - How to query multiple Neo4j Node property fragments with "STARTS WITH"

I'm looking for a way to combine the Cypher "IN" and "STARTS WITH" query. In other words I'm looking for a way to look up nodes that start with specific string sequences that are provided as Array using IN.
The goal is to have the query run in as less as possible calls against the DB.
I browsed the documentation and played around with Neo4j a bit but wasn't able to combine the following two queries into one:
MATCH (a:Node_type_A)-[]->(b:Node_type_B)
WHERE a.prop_A IN [...Array of Strings]
RETURN a.prop_A, COLLECT ({result_b: b.prop_B})
and
MATCH (a:Node_type_A)-[]->(b:Node_type_B)
WHERE a.prop_A STARTS WITH 'String'
RETURN a.prop_A, b.prop_B
Is there a way to combine these two approaches?
Any help is greatly appreciated.
Krid
You'll want to make sure there is an index or unique constraint (whichever is appropriate) on your :Node_type_A(prop_A) to speed up your lookups.
If I'm reading your requirements right, this query may work for you, adding your input strings as appropriate (parameterize them if you can).
WITH [...] as inputs
UNWIND inputs as input
// each string in inputs is now on its own row
MATCH (a:Node_type_A)
WHERE a.prop_A STARTS WITH input
// should be an fast index lookup for each input string
WITH a
MATCH (a)-[]->(b:Node_type_B)
RETURN a.prop_A, COLLECT ({result_b: b.prop_B})
Something like this should work:
MATCH (a:Node_type_A)-[]->(b:Node_type_B)
WITH a.prop_A AS pa, b.prop_B AS pb
WITH pa, pb,
REDUCE(s = [], x IN ['a','b','c'] |
CASE WHEN pa STARTS WITH x THEN s + pb ELSE s END) AS pbs
RETURN pa, pbs;

Find words missing from a Neo4j database

I have a Neo4j database containing words from a dictionary. If a query is made using a word not in the dictionary (such as a mis-spelled word), I want the query to return just the missing words.
I can do the opposite. This query returns words from the database that are correctly spelled:
MATCH (w:Word)
WHERE w.spelling IN ["good","words","plus","twoo","that","are","mispleled"]
RETURN w.spelling AS word
Can I write a query that will return something like ["twoo","mispleled"]? Or do I have to use a query like the one above, and check in the application itself which words have not been matched?
You can do this without OPTIONAL MATCH:
WITH ["good", "words", "plus", "twoo", "that", "are", "mispleled"] AS words
MATCH (word:Word) WHERE word.spelling IN words
WITH words, COLLECT(word.spelling) AS matched
RETURN [x IN words WHERE NOT x IN matched];
The advice about creating an index still applies.
[EDITED]
This might work for you. {input_words} is a parameter that contains an array of the words you want to check.
UNWIND {input_words} AS in_word
OPTIONAL MATCH (w:Word { spelling: in_word })
RETURN REDUCE(s = [], x IN COLLECT({ w: w, in_word: in_word }) |
CASE WHEN x.w IS NULL THEN s + x.in_word ELSE s END) AS missing_word;
For efficiency, you should first create an index on :Word(spelling):
CREATE INDEX ON :Word(spelling);

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 - 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...

Iterating through a collection with MATCH and CREATE clauses

I want to do something like this in cypher:
MATCH (n:node) WHERE n.ID = x //x is an integer value
FOREACH (num in n.IDs:
MATCH (p:node) WHERE p.ID = num
CREATE (n)-[:LINK]->(p) )
where num is an array of integer values referring to the IDs of nodes that need to be linked to the node matched in the first line.
When I run this query, I get the error: Invalid use of MATCH inside FOREACH.
I'm in the early stages of teaching myself both Cypher and Neo4j. How can I achieve my desired functionality here? Or am I barking up the wrong tree - am I failing to grasp something that makes it unnecessary for me to do so?
This is not allowed, instead use the top-level MATCH like http://gist.neo4j.org/?8332363
MATCH (n:node), (p:node)
WHERE n.ID = 1 AND p.ID in [2,3,4]
CREATE (n)-[:LINK]->(p)

Resources