Return node if relationship is not present
Asked Answered
E

7

110

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.

Expand answered 8/6, 2012 at 16:4 Comment(2)
Check here for information relevant to v3: #25673723Sickening
For future users; can use exists in a WHERE clause (also negate it as well), neo4j.com/developer/subqueries/#existential-subqueries for more information.Riccio
G
179

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.

Gatecrasher answered 10/1, 2013 at 9:9 Comment(6)
In Neo4j 2.0, use OPTIONAL MATCH to match optional relationships, i.e the first example would look like OPTIONAL MATCH (source)-[r:someType]-(target) RETURN source, rEggert
I am trying to have a labeled node in WHERE NOT, it does not work. Like: MATCH a WHERE NOT (a)-[:LOVES]->(Stranger), in this 'Stranger' is a node label. I am using neo4j version 2.1.2Jollity
Nevermind, I understand why you'd want to show the progression to reach this answer: MATCH a WHERE NOT (a)-[:LOVES]->()Mussolini
Good day to you too !Sevenup
The MATCH a... example should now be MATCH (a) WHERE NOT (a)-[:LOVES]->()Furred
@gil-stal Why I can't use node name with this query like this. MATCH a WHERE NOT (a)-[:LOVES]->(b:SomeLabel) . If I don't use node name then it works.Gritty
E
28

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.

Elfin answered 8/12, 2014 at 7:31 Comment(5)
MATCH (player) WHERE NOT (player)-[r]-() RETURN player gives Variable r not defined error. How can i define r?Bubonocele
to fix this, either specify a relationship (e.g. (player -[:rel]- ()) or leave empty for any relation (player -[]- ()Revision
MATCH (player) WHERE NOT (player)-[]-() RETURN player - It works fineFusilier
Your first query is actually wrong. The MATCH pattern itself always returns only existing relationships, with none of them NULL. So your WHERE line has nothing to filter from.Sik
@CristiS. Thanks for letting me know. I have updated the query it should workElfin
V
8

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

Vercelli answered 8/5, 2015 at 13:25 Comment(2)
Pretty sure you need parens around the p in line 2, like OPTIONAL MATCH (p)--(c: Communication)Daubery
@Daubery I don't know about the current version, but in the version specified you didn't need to.Vercelli
M
2

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.

Mensal answered 9/6, 2012 at 8:49 Comment(1)
Yes, but it does not return the desired ingredient. It returns what the chef already has in common with the recipe, I want to find out the difference.Expand
E
2

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.

Eggert answered 23/2, 2014 at 14:13 Comment(0)
E
1

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.

Expand answered 11/6, 2012 at 7:12 Comment(0)
R
1

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;
Reprehend answered 17/8, 2022 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.