Cypher: Match All Elements In A Collection - neo4j

I have a collection of element ids. I am currently using an UNWIND to find nodes that match at least one element.
UNWIND ids as id
MATCH (e:Element)-[]-(f:Foo {id:id})
RETURN DISTINCT e
I wanted to find out how I could find the elements that match ALL the ids. Such that if I have 5 ids, the elements that have a relation to all 5 matching nodes and exclude nodes that only match 1 or 2.
I was thinking I could do something with COUNT and pass in the number of elements as a query parameter, but that seemed pretty hacky.
Does anyone know how to do return nodes that match all elements in a collection?

Is there only a single relationship between Element and Foo nodes? If so, how about comparing the number of ids you have to the number of relationships connecting each distinct Element to Foo nodes:
UNWIND ids AS id
MATCH (e:Element)-[r]->(f:Foo {id: id})
WITH e, COUNT(r) AS num WHERE num = SIZE(ids)
RETURN e

Related

Do not return set of nodes from a specific path in Cypher

I am trying to return a set of a node from 2 sessions with a condition that returned node should not be present in another session (third session). I am using the following code but it is not working as intended.
MATCH (:Session {session_id: 'abc3'})-[:HAS_PRODUCT]->(p:Product)
UNWIND ['abc1', 'abc2'] as session_id
MATCH (target:Session {session_id: session_id})-[r:HAS_PRODUCT]->(product:Product)
where p<>product
WITH distinct product.products_id as products_id, r
RETURN products_id, count(r) as score
ORDER BY score desc
This query was supposed to return all nodes present in abc1 & abc2 but not in abc3. This query is not excluding all products present in abc3. Is there any way I can get it working?
UPDATE 1:
I tried to simplify it without UNWIND as this
match (:Session {session_id: 'abc3'})-[:HAS_PRODUCT]->(p:Product)
MATCH (target:Session {session_id: 'abc1'})-[r:HAS_PRODUCT]->(product:Product)
where product <> p
WITH distinct product.products_id as products_id
RETURN products_id
Even this is also not working. It is returning all items present in abc1 without removing those which are already in abc3. Seems like where product <> p is not working correctly.
I would suggest it would be best to check if the nodes are in a list, and to prove out the approach, start with a very simple example.
Here is a simple cypher showing one way to do it. This approach can then be extended into the complex query,
// get first two product IDs as a list
MATCH (p:Product)
WITH p LIMIT 2
WITH COLLECT(ID(p)) as list
RETURN list
// now show two more product IDs which not in that list
MATCH (p:Product)
WITH p LIMIT 2
WITH COLLECT(ID(p)) as list
MATCH (p2:Product)
WHERE NOT ID(p2) in list
RETURN ID(p2) LIMIT 2
Note: I'm using the ID() of the nodes instead of the entire node, same dbhits but may be more performant...

Update nodes by a list of ids and values in one cypher query

I've got a list of id's and a list of values. I want to catch each node with the id and set a property by the value.
With just one Node that is super basic:
MATCH (n) WHERE n.id='node1' SET n.name='value1'
But i have a list of id's ['node1', 'node2', 'node3'] and same amount of values ['value1', 'value2', 'value3'] (For simplicity i used a pattern but values and id's vary a lot). My first approach was to use the query above and just call the database each time. But nowadays this isn't appropriate since i got thousand of id's which would result in thousand of requests.
I came up with this approach that I iterate over each entry in both lists and set the values. The first node from the node list has to get the first value from the value list and so on.
MATCH (n) WHERE n.id IN["node1", "node2"]
WITH n, COLLECT(n) as nodeList, COLLECT(["value1","value2"]) as valueList
UNWIND nodeList as nodes
UNWIND valueList as values
FOREACH (index IN RANGE(0, size(nodeList)) | SET nodes.name=values[index])
RETURN nodes, values
The problem with this query is that every node gets the same value (the last of the value list). The reason is in the last part SET nodes.name=values[index] I can't use the index on the left side nodes[index].name - doesn't work and the database throws error if i would do so. I tried to do it with the nodeList, node and n. Nothing worked out well. I'm not sure if this is the right way to achieve the goal maybe there is a more elegant way.
Create pairs from the ids and values first, then use UNWIND and simple MATCH .. SET query:
// THe first line will likely come from parameters instead
WITH ['node1', 'node2', 'node3'] AS ids,['value1', 'value2', 'value3'] AS values
WITH [i in range(0, size(ids)) | {id:ids[i], value:values[i]}] as pairs
UNWIND pairs AS pair
MATCH (n:Node) WHERE n.id = pair.id
SET n.value = pair.value
The line
WITH [i in range(0, size(ids)) | {id:ids[i], value:values[i]}] as pairs
combines two concepts - list comprehensions and maps. Using the list comprehension (with omitted WHERE clause) it converts list of indexes into a list of maps with id,value keys.

Cypher query help: Order query results by content of property array

I have a bunch of venues in my Neo4J DB. Each venue object has the property 'catIds' that is an array and contains the Ids for the type of venue it is. I want to query the database so that I get all Venues but they are ordered where their catIds match or contain some off a list of Ids that I give the query. I hope that makes sense :)
Please, could someone point me in the direction of how to write this query?
Since you're working in a graph database you could think about modeling your data in the graph, not in a property where it's hard to get at it. For example, in this case you might create a bunch of (v:venue) nodes and a bunch of (t:type) nodes, then link them by an [:is] relation. Each venue is linked to one or more type nodes. Each type node has an 'id' property: {id:'t1'}, {id:'t2'}, etc.
Then you could do a query like this:
match (v:venue)-[r:is]->(:type) return v, count(r) as n order by n desc;
This finds all your venues, along with ALL their type relations and returns them ordered by how many type-relations they have.
If you only want to get nodes of particular venue types on your list:
match (v:venue)-[r:is]-(t:type) where t.id in ['t1','t2'] return v, count(r) as n order by n desc;
And if you want ALL venues but rank ordered according to how well they fit your list, as I think you were looking for:
match (v:venue) optional match (v)-[r:is]->(t:type) where t.id in ['t1','t2'] return v, count(r) as n order by n desc;
The match will get all your venues; the optional match will find relations on your list if the node has any. If a node has no links on your list, the optional match will fail and return null for count(r) and should sort to the bottom.

Match nodes where there is any intersection between its array property and other array

Let's say we have nodes that has an array property.
Node 1
fruits = ['apple','mango']
Node 2
fruits = ['apple']
Node 3
fruits = ['tomato']
and we want to find all nodes wherein one of their fruits exists in Maria's basket.
Maria's basket = ['orange','grape','apple']
So our end result would be : Node 1 and Node 2.
My approach would be matching all nodes whose elements of its fruits array exists with Maria's basket. But I couldn't get it to work
match (n) where x in n.fruits in ['orange','grape','apple'] return n
I tried the query above and returns syntax error since x is not defined. How do we properly approach this problem?
The second approach I'm thinking is, match all nodes if there is a UNION that exists between a node's fruits and Maria's fruits.
If you want to find nodes where exactly one fruit matches:
MATCH (n)
WHERE single(x IN n.fruits WHERE x IN ['orange', 'grape', 'apple'])
RETURN n;
If you want to find nodes where >= 1 fruits match:
MATCH (n)
WHERE any(x IN n.fruits WHERE x IN ['orange', 'grape', 'apple'])
RETURN n;
I wasn't sure which one you wanted based on your wording.

How to find out connection between a set of nodes?

I have a scenario where I know IDs of a list of nodes.
I need to get connection(if exists) between these nodes given their IDs.
Is there any way to achieve this?
Update:
I am using node id property not the neo4j's internal ID(using like match (n:Person{id:3}))
You can use the IN clause to select from a list of values:
MATCH (n)-[r*..2]-(m)
WHERE ID(n) IN [0,1,2] AND ID(m) IN [2,3,4]
RETURN r
I've limited the path length to 2 hops of indeterminate relationship type here, and arbitrarily picked some IDs.
To return the path instead:
MATCH p=(n)-[r*..2]-(m)
WHERE ID(n) IN [0,1,2] AND ID(m) IN [2,3,4]
RETURN p
START n=node(1,2,3,4,5,6) //your IDs of a list of nodes
MATCH p=n-[r]-m //the connection for 1 hop. for multiple hops do n-[r*]-m
WHERE Id(m) in [1,2,3,4,5,6] //your IDs of a list of nodes
RETURN p

Resources