What I'm trying to get is nodes having certain property values for any property name (key) and not having some other values for any property.
So in short, pseudo-google query be like:
+Tom +val1 +val2 (...) -Cruise -valX -valY (...)
and Cypher query be like:
MATCH (n) WHERE (
ANY ( p in KEYS(n) WHERE n[p] CONTAINS 'Tom' ) AND
NONE ( p in KEYS(n) WHERE n[p] CONTAINS 'Cruise')
)
RETURN n
But the test result with movie database (:play movie graph) was just an empty list, while there are other actors named 'Tom' in the database, such as Tom Hanks.
(match (n) where (any( p in KEYS(n) WHERE n[p] contains 'Tom')) return n
gives [Tom Tykwer, Tom Hanks, Tom Cruise, Tom Skerritt])
So I experimented with 'om' instead of 'Tom', and this time, the result is a incomplete list of 'om's:
match (n) where (
any( p in KEYS(n) WHERE n[p] contains 'om') and
none( p in Keys(n) WHERE n[p] contains 'Cruise')
)
return n
gives
[Romantic (genre), Naomie Harris, James Thompson, Jessica Thompson]
(No Tom's -- why?)
Also tried NOT ANY() in place of NONE() and had same results.
Where does this inconsistency come from?
#stdob-- offers an accurate explanation of the issue.
But there are simpler workarounds. For instance, you can use the COALESCE function() to force a NULL value to be treated as FALSE:
MATCH (n)
WHERE
ANY ( p in KEYS(n) WHERE n[p] CONTAINS 'Tom' ) AND
NONE( p in KEYS(n) WHERE COALESCE(n[p] CONTAINS 'Cruise', FALSE))
RETURN n
The problem is that nodes have properties with a type other than string. And for them, NONE-verification gives null, which gives an error for where entirely. For example, this query return nothing:
WITH {k1: 1, k2: '2'} AS test
WHERE NONE(key IN keys(test) WHERE test[key] CONTAINS '1')
RETURN test
So in this case you need to check the type of the property. Since there is no native type-checking function, you can use the function from the APOC library:
MATCH (n) WHERE (
ANY(p in KEYS(n) WHERE apoc.meta.cypher.type(n[p]) = 'STRING' AND n[p] CONTAINS 'Tom') AND
NONE(p in KEYS(n) WHERE apoc.meta.cypher.type(n[p]) = 'STRING' AND n[p] CONTAINS 'Cruise')
)
RETURN n
Related
So as a complication to this question, I basically want to do
MATCH (n:TEST) OPTIONAL MATCH (n)-[r]->() RETURN DISTINCT n, r
And I want to return n and r as one column with no repeat values. However, running
MATCH (n:TEST) OPTIONAL MATCH (n)-[r]->() UNWIND n+r AS x RETURN DISTINCT x
gives a "Type mismatch: expected List but was Relationship (line 1, column 47)" error. And this query
MATCH (n:TEST) RETURN DISTINCT n UNION MATCH ()-[n]->() RETURN DISTINCT n
Puts nodes and relationships in the same column, but the context from the first match is lost in the second half.
So how can I return all matched nodes and relationships as one minimal list?
UPDATE:
This is the final modified version of the answer query I am using
MATCH (n:TEST)
OPTIONAL MATCH (n)-[r]->()
RETURN n {.*, rels:collect(r {properties:properties(r), id:id(r), type:type(r), startNode:id(startNode(r)), endNode:id(endNode(r))})} as n
There are a couple ways to handle this, depending on if you want to hold these within lists, or within maps, or if you want a map projection of a node to include its relationships.
If you're using Neo4j 3.1 or newer, then map projection is probably the easiest approach. Using this, we can output the properties of a node and include its relationships as a collected property:
MATCH (n:TEST)
OPTIONAL MATCH (n)-[r]->()
RETURN n {.*, rels:collect(r)} as n
Here's what you might do if you wanted each row to be its own pairing of a node and a single one of its relationships as a list:
...
RETURN [n, r] as pair
And as a map:
...
RETURN {node:n, rel:r} as pair
EDIT
As far as returning more data from each relationship, if you check the Code results tab, you'll see that the id, relationship type, and start and end node ids are included, and accessible from your back-end code.
However, if you want to explicitly return this data, then we just need to include it in the query, using another map projection for each relationship:
MATCH (n:TEST)
OPTIONAL MATCH (n)-[r]->()
RETURN n {.*, rels:collect(r {.*, id:id(r), type:type(r), startNode:startNode(r), endNode:endNode(r)})} as n
For the following cypher query , how to limit path length by giving "cnt" as a parameter which is the output of previous query passed to next query using "with" clause.
match ()-[r:contents|next_seq]->(n:word) where r.seqid={seqid}
with count(distinct n) as cnt
match p=((a:word)-[rels:next_seq*cnt]->(b:word))
WHERE ALL( rt in rels WHERE rt.seqid={seqid}
return b.name
At this time, the cypher does not allow to use a variable as the path length.
If you use a version of neo4j >= 3 you can use apoc path expander:
match ()-[r:contents|next_seq]->(n:word) where r.seqid={seqid}
with count(distinct n) as cnt
match (a:word)-[rels:next_seq {seqid: {seqid}}]->(:word))
with distinct a
call apoc.path.expand( a, 'next_seq', '+word', 1, cnt ) yield path
with path WHERE ALL( rt in relationships(path) where rt.seqid={seqid} )
return last(nodes(path)).name as name
I opened this question here: How to find specific subgraph in Neo4j using where clause to find a path of a certain criteria. Yet when I try to do things like get the relationship type I cannot.
For example I tried MATCH p = (n:Root)-[rs1*]->()
WHERE ALL(rel in rs1 WHERE rel.relevance is null)
RETURN nodes(p), TYPE(relationships(p))
But I get the error:
Type mismatch: expected Relationship but was Collection<Relationship>
I think I need to use a WITH clause but not sure.
Similarly I wanted the ID of a node but that also failed.
The problem is that relationships returns a collection and the type function only works on a single relationship. There are two main approaches to solve this.
Use UNWIND to get a separate row for each relationship:
MATCH p = (n:Root)-[rs1*]->()
WHERE ALL(rel in rs1 WHERE rel.relevance is null)
WITH relationships(p) AS rs
UNWIND n, rs AS r
RETURN n, type(r)
Use extract to get the results in a list (in a single row per root node):
MATCH p = (n:Root)-[rs1*]->()
WHERE ALL(rel in rs1 WHERE rel.relevance is null)
WITH n, relationships(p) AS rs
RETURN n, extract(r IN rs | type(r))
Or even shorter:
MATCH p = (n:Root)-[rs1*]->()
WHERE ALL(rel in rs1 WHERE rel.relevance is null)
RETURN n, extract(r IN relationships(p) | type(r))
I have nodes with an "id" property array:
node 1: {id:[1,2,3]}
node 2: {id:[3,4,5]}
node 4: {id:[6,7,8]}
I want a query to match the node pairs that have at least one common value in the ID property array; for example the query I'm looking for would return only node 1, node 2 (they have the value "3" in common).
I've tried this, but it didn't work for me:
MATCH (n), (m) where FILTER(x IN n.id WHERE x IN m.id) return n,m;
Thanks!
Actually, your original query should have returned some results.
Here is an improved version of that query:
MATCH (n), (m)
WHERE ID(n) < ID(m) AND ANY(x IN n.id WHERE x IN m.id)
RETURN n, m;
It avoids duplicate results by ordering the nodes by ID.
It use the ANY function, which exits as soon as a match is found.
See this console.
This is a bit convoluted, but it seems to work:
MATCH n, m
WHERE ID(n)< ID(m)
WITH n, n.id AS n_ids, m, m.id AS m_ids
UNWIND n_ids AS n_id
UNWIND m_ids AS m_id
WITH n, m, n_id, m_id
WHERE n_id = m_id
RETURN n, m
If that doesn't make sense to you, I'd suggest you try changing each WITH to a RETURN and removing everything afterwards to see the results at each step.
EDIT: You can also make this a bit shorter thusly:
MATCH n, m
WHERE ID(n)< ID(m)
WITH n, n.id AS n_ids, m, m.id AS m_ids
UNWIND n_ids AS n_id
WITH n, m, n_id, m_ids
WHERE n_id IN m_ids
RETURN n, m
(You might need a DISTINCT in there at the end for a larger dataset)
In cypher id like to return all paths containing only a known set of valid relationship names using something like
MATCH (i:VALID_RELATIONSHIPS), p=(n:MY_DOMAIN)-[rels*1..5]-(m)
WHERE n.NAME='start_node' AND
ALL(t in rels WHERE type(t) IN extract(x IN i | x.RELATIONSHIP_NAME) )
RETURN nodes(p);
so ALL t IN rels should be contained in the collection of VALID_RELATIONSHIPS
however this returns an exception i already defined with conflicting node type collection<any>
Am I misunderstanding something here? The algorithm should loop through all rels (which I assume is a collection) in the path, and for each one; check that the type() is contained in the valid relationships collection i
This one is of type Node: i
Why don't you pass in the valid relationship names as a parameter?
MATCH p=(n:MY_DOMAIN)-[rels*1..5]-(m)
WHERE n.NAME='start_node' AND
ALL(t in rels WHERE type(t) IN {valid_rel_names} )
RETURN nodes(p);
Otherwise you have to aggregate your i's into a collection first
MATCH (i:VALID_RELATIONSHIPS)
WITH collect(i) as valid_rels
MATCH p=(n:MY_DOMAIN)-[rels*1..5]-(m)
WHERE n.NAME='start_node' AND
ALL(t in rels WHERE type(t) IN extract(x IN valid_rels | x.RELATIONSHIP_NAME) )
RETURN nodes(p);
or already extract the rels upfront
MATCH (i:VALID_RELATIONSHIPS)
WITH collect(i.RELATIONSHIP_NAME) as valid_rel_names
MATCH p=(n:MY_DOMAIN)-[rels*1..5]-(m)
WHERE n.NAME='start_node' AND
ALL(t in rels WHERE type(t) IN valid_rel_names )
RETURN nodes(p);