Return node if relationship is not present - neo4j

I'm trying to create a query using cypher that will "Find" missing ingredients that a chef might have, My graph is set up like so:
(ingredient_value)-[:is_part_of]->(ingredient)
(ingredient) would have a key/value of name="dye colors". (ingredient_value) could have a key/value of value="red" and "is part of" the (ingredient, name="dye colors").
(chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)
I'm using this query to get all the ingredients, but not their actual values, that a recipe requires, but I would like the return only the ingredients that the chef does not have, instead of all the ingredients each recipe requires. I tried
(chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)<-[:has_ingredient*0..0]-chef
but this returned nothing.
Is this something that can be accomplished by cypher/neo4j or is this something that is best handled by returning all ingredients and sorted through them myself?
Bonus: Also is there a way to use cypher to match all values that a chef has to all values that a recipe requires. So far I've only returned all partial matches that are returned by a chef-[:has_value]->ingredient_value<-[:requires_value]-recipe and aggregating the results myself.

Update 01/10/2013:
Came across this in the Neo4j 2.0 reference:
Try not to use optional relationships.
Above all,
don’t use them like this:
MATCH a-[r?:LOVES]->() WHERE r IS NULL where you just make sure that they don’t exist.
Instead do this like so:
MATCH (a) WHERE NOT (a)-[:LOVES]->()
Using cypher for checking if relationship doesn't exist:
...
MATCH source-[r?:someType]-target
WHERE r is null
RETURN source
The ? mark makes the relationship optional.
OR
In neo4j 2 do:
...
OPTIONAL MATCH source-[r:someType]-target
WHERE r is null
RETURN source
Now you can check for non-existing (null) relationship.

For fetching nodes with not any relationship
This is the good option to check relationship is exist or not
MATCH (player)
WHERE NOT(player)-[:played]->()
RETURN player
You can also check multiple conditions for this
It will return all nodes, which not having "played" Or "notPlayed" Relationship.
MATCH (player)
WHERE NOT (player)-[:played|notPlayed]->()
RETURN player
To fetch nodes which not having any realtionship
MATCH (player)
WHERE NOT (player)-[r]-()
RETURN player
It will check node not having any incoming/outgoing relationship.

If you need "conditional exclude" semantic, you can achieve it this way.
As of neo4j 2.2.1, you can use OPTIONAL MATCH clause and filter out the unmatched(NULL) nodes.
It is also important to use WITH clause between the OPTIONAL MATCH and WHERE clauses, so that the first WHERE defines a condition for the optional match and the second WHERE behaves like a filter.
Assuming we have 2 types of nodes: Person and Communication. If I want to get all Persons which have never communicated by the telephone, but may have communicated other ways, I would make this query:
MATCH (p: Person)
OPTIONAL MATCH p--(c: Communication)
WHERE c.way = 'telephone'
WITH p, c
WHERE c IS NULL
RETURN p
The match pattern will match all Persons with their communications where c will be NULL for non-telephone Communications. Then the filter(WHERE after WITH) will filter out telephone Communications leaving all others.
References:
http://neo4j.com/docs/stable/query-optional-match.html#_introduction_3
http://java.dzone.com/articles/new-neo4j-optional

I wrote a gist showing how this can be done quite naturally using Cypher 2.0
http://gist.neo4j.org/?9171581
The key point is to use optional match to available ingredients and then compare to filter for missing (null) ingredients or ingredients with the wrong value.
Note that the notion is declarative and doesn't need to describe an algorithm, you just write down what you need.

The last query should be:
START chef = node(..)
MATCH (chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)
WHERE (ingredient)<-[:has_ingredient]-chef
RETURN ingredient
This pattern: (ingredient)<-[:has_ingredient*0..0]-chef
Is the reason it didn't return anything. *0..0 means that the length of the relationships must be zero, which means that ingredient and chef must be the same node, which they are not.

I completed this task using gremlin. I did
x=[]
g.idx('Chef')[[name:'chef1']].as('chef')
.out('has_ingredient').as('alreadyHas').aggregate(x).back('chef')
.out('has_value').as('values')
.in('requires_value').as('recipes')
.out('requires_ingredient').as('ingredients').except(x).path()
This returned the paths of all the missing ingredients. I was unable to formulate this in the cypher language, at least for version 1.7.

For new versions of Neo4j, you'll have this error:
MATCH (ingredient:Ingredient)
WHERE NOT (:Chef)-[:HAS_INGREDIENT]->(ingredient)
RETURN * LIMIT 100;
This feature is deprecated and will be removed in future versions.
Coercion of list to boolean is deprecated. Please consider using NOT isEmpty(...) instead.
To fix it:
MATCH (ingredient:Ingredient)
WHERE NOT EXISTS((:Chef)-[:HAS_INGREDIENT]->(ingredient))
RETURN * LIMIT 100;

Related

Match relationships if a certain parameter exists

I've been using neo4j for a while and recently i got stuck with a query that i don't seem to be able to succesfully run.
My goal: I have a type of relationship called HAS_RELATIONSHIP. this type of rel sometimes hasa property called verified. I want to get a subgraph of those relationships that don't have this property so i can afterwards add the property.
What I have done so far:
Match (a)-[r:HAS_RELATIONSHIP]-(b)
where r.verified=False
set r.verified=True
LIMIT 5
return r, a, b
the part that is not working is where r.verified=False it should be something like exists(r)=verified but t doesn't seem to exist this kind of query. I have checked on OPTIONAL MATCH, but it seems it is neither the solution.
Any ideas?
You can use the NOT operator together with the predicate function exists() for this problem:
MATCH (r) WHERE NOT exists(r.verified) RETURN r

Cypher Query not returning nonexistent relationships

I have a graph database where there are user and interest nodes which are connected by IS_INTERESTED relationship. I want to find interests which are not selected by a user. I wrote this query and it is not working
OPTIONAL MATCH (u:User{userId : 1})-[r:IS_INTERESTED] -(i:Interest)
WHERE r is NULL
Return i.name as interest
According to answers to similar questions on SO (like this one), the above query is supposed to work.However,in this case it returns null. But when running the following query it works as expected:
MATCH (u:User{userId : 1}), (i:Interest)
WHERE NOT (u) -[:IS_INTERESTED] -(i)
return i.name as interest
The reason I don't want to run the above query is because Neo4j gives a warning:
This query builds a cartesian product between disconnected patterns.
If a part of a query contains multiple disconnected patterns, this
will build a cartesian product between all those parts. This may
produce a large amount of data and slow down query processing. While
occasionally intended, it may often be possible to reformulate the
query that avoids the use of this cross product, perhaps by adding a
relationship between the different parts or by using OPTIONAL MATCH
(identifier is: (i))
What am I doing wrong in the first query where I use OPTIONAL MATCH to find nonexistent relationships?
1) MATCH is looking for the pattern as a whole, and if can not find it in its entirety - does not return anything.
2) I think that this query will be effective:
// Take all user interests
MATCH (u:User{userId: 1})-[r:IS_INTERESTED]-(i:Interest)
WITH collect(i) as interests
// Check what interests are not included
MATCH (ni:Interest) WHERE NOT ni IN interests
RETURN ni.name
When your OPTIONAL MATCH query does not find a match, then both r AND i must be NULL. After all, since there is no relationship, there is no way get the nodes that it points to.
A WHERE directly after the OPTIONAL MATCH is pulled into the evaluation.
If you want to post-filter you have to use a WITH in between.
MATCH (u:User{userId : 1})
OPTIONAL MATCH (u)-[r:IS_INTERESTED] -(i:Interest)
WITH r,i
WHERE r is NULL
Return i.name as interest

Cypher query returning null values even when the pattern is available in database

Is something wrong with this cypher query
MATCH (owner:SidNode)<-[:OWNED_BY]-(acl:AclNode)-[:SECURES]->(class:ClassNode)
OPTIONAL MATCH (acl)<-[:COMPOSES]-(ace:AceNode)-[:AUTHORIZES]->(sid:SidNode)
WITH acl, ace, owner, sid, class
WHERE (acl.objectIdIdentity = {objectIdIdentity1} AND class.className = {className1})
RETURN
owner.principal AS aclPrincipal,
owner.sid AS aclSid,
acl.objectIdIdentity AS objectIdIdentity,
ace.aceOrder AS aceOrder,
ID(acl) AS aclId,
acl.parentObject AS parentObject,
acl.entriesInheriting AS entriesInheriting,
ID(ace) AS aceId, ace.mask AS mask,
ace.granting AS granting,
ace.auditSuccess AS auditSuccess,
ace.auditFailure AS auditFailure,
sid.principal AS acePrincipal,
sid.sid AS aceSid,
class.className AS className
ORDER BY acl.objectIdIdentity ASC, ace.aceOrder ASC
It is returning null values for ace nodes even though there are multiple nodes available in graph db.
But some times it is returning proper values like 4 rows if there are 4 ace nodes in db.
code i am writing is about spring security acl
reference link:
https://github.com/shazin/spring-security-acl-neo4j/blob/master/src/main/java/org/springframework/security/acls/neo4j/Neo4jLookupStrategy.java
Please suggest modifications.
Your problem comes from OPTIONAL MATCH, according to Neo4j's documentation, OPTIONAL MATCH returns NULLif the property or the element is not found.
You are getting NULL values because of this. If your acl node doesn't have any ace node related to him, the node will be replaced with NULL
You bind the ace nodes in an optional match. When that optional match fails to match something, ace will be null.
If you think the optional match ought to be successful in cases when it is not, maybe you could provide an example. A good way to do so is to create a small sample graph at http://console.neo4j.org
The conditions in your WHERE clause look like they belong with your MATCH clause. You might want to move the WHERE clause up and you can then remove the WITH altogether. It won't affect nulls, but it will make your query more efficient and more readable. (Also, you don't need parentheses in your WHERE clause.)
MATCH (owner:SidNode)<-[:OWNED_BY]-(acl:AclNode)-[:SECURES]->(class:ClassNode)
WHERE acl.objectIdIdentity = {objectIdIdentity1} AND class.className = {className1}
OPTIONAL MATCH (acl)<-[:COMPOSES]-(ace:AceNode)-[:AUTHORIZES]->(sid:SidNode)
RETURN ...

Neo4j Cypher - Excluding relationships by type in cypher query?

Single relationships can be excluded by types using
Match (n:Person)-[r]-(m:Person) where type(r)<>"FRIENDS" return n,r,m
Is there a way to exclude multilevel relationships with cypher?
Match (n:Person)-[r*2..4]-(h:Hobby) where type(r)<>"FRIENDS" return n,r,h
Sure, you can do that. Like this:
Match (n:Person)-[r*2..4]-(h:Hobby)
where NONE( rel in r WHERE type(rel)="FRIENDS")
return n,r,h
Your query doesn't work because with multi-level paths, your r is a collection of relationships rather than a single one. So you can use any of the predicates on collections to do the filtering that you want.
Here, I chose NONE with type(rel)=FRIENDS, which means that you'll only get the result back if NONE of the relationships are of type FRIENDS. But you might instead want to use ANY or ALL, depending on what your query is supposed to mean.
Anyway, the main point here is to use a predicate function to turn a collection of things into a single boolean value.
You can use the ALL or ANY predicates for this:
MATCH p=(n:Person)-[*2..4]-(h:Hobby)
WHERE ALL(x in relationships(p) WHERE type(x) <> "FRIENDS")
RETURN n,r,h
Use the ALL predicate to make sure every relationship along that path is not a FRIEND. Use ANY to make sure you have at least one relationship not being FRIEND.

Single Cypher query to choose between 2 different paths

I have the following scenario:
At some point in my path (in a node that lies a few links away from my start node),
I have the possibility of going down one path or another, for example:
If S is my startnode,
S-[]->..->(B)-[first:FIRST_WAY]->(...) ,
and
S-[]->..->(B)-[second:SECOND_WAY]->(...)
At the junction point, I will need to go down one path only (first or second)
Ideally, I would like to follow and include results from the second relationship, only if the first one is not present (regardless of what exists afterwards).
Is this possible with Cypher 1.9.7, in a single query?
One way would be to an optional match to match the patterns separately. Example:
MATCH (n:Object) OPTIONAL MATCH (n)-[r1:FIRST_WAY]->(:Object)-->(f1:Object) OPTIONAL MATCH (n)-[r2:SECOND_WAY]->()-->(f2:Object) RETURN coalesce(f2, f1)
This query will match both conditionally and the coalesce function will return the first result which is not null.
AFAIK, OPTIONAL_MATCH was introduced in 2.0 so you can't use that clause in 1.9, but there is an alternate syntax:
CYPHER 1.9 START n=node(*) MATCH (n)-[r1?:FIRST_WAY]->()-->(f1), (n)-[r2?:SECOND_WAY]->()-->(f2) RETURN coalesce(f2, f1)
I'm sure there are other ways to do this, probably using the OR operator for relationship matching, i.e. ()-[r:FIRST_WAY|SECOND_WAY]->(), and then examining the patterns matched to discard some of the result paths based on the relationship type.

Resources