Only show nodes that have a specific properties in relationship - neo4j

I'm new to neo4j and im struggling with the task to build a simple filter.
I played around and found the in operator but it only list me every "Person" where atleast one match is found. I want to only list "Person" that have all the properties included.
MATCH (p:Person)-[l:LIKES]->(f:Food) WHERE f.name in ["Spaghetti","Cheese","Chicken","Eggs"]
RETURN p
Result: Show only "Person" that like "Spaghetti","Cheese","Chicken","Eggs", ...

We have a knowledge base article on performing match intersection that should address this.
Applied to your case, here's one of the approaches you can use:
WITH ["Spaghetti","Cheese","Chicken","Eggs"] as foods
MATCH (p:Person)-[:LIKES]->(f:Food)
WHERE f.name in foods
WITH p, foods, count(f) as foodsLiked
WHERE foodsLiked = size(foods)
RETURN p

Related

find the particular node which fit three criteria neo4j

I would like to find the name node which trade with three fruits only.
I tried to use the following code in neo4j.
match (s:good)-[r:TRADES]-(n:Name)-[:TRADES]-(p:good)
WHERE (s.good = 'Apple' or s.good='Orange') and p.stock ='Grapes'
return s,n,p
where it returns the query as below.
However, I just want the following. Just the one who trade Grapes, Orange and Apple only.
I don't know which part of the cypher is incorrect. thank you for helping
We have a knowledge base article on match intersection, what you're trying to do here, however your other restriction is that these are the only 3 connected nodes, so we have some additional work to do.
Using the first approach in the article, we just have to add an additional predicate to ensure the degree of :TRADES relationships equals the size of the collection:
WITH ['Apple', 'Orange', 'Grapes'] as names
MATCH (g:good)<-[:TRADES]-(n:Name)
WHERE g.good in names AND size((n)-[:TRADES]->()) = size(names)
WITH n, size(names) as inputCnt, count(DISTINCT g) as cnt
WHERE cnt = inputCnt
RETURN n

Neo4j query node property.

I have database with entities person (name,age) and project (name).
can I query the database in cypher that specifies me it is person or project?
for example consider I have these two instances for each :
Node (name = Alice, age= 20)
Node (name = Bob, age = 31)
Node (name = project1)
Node (name = project2)
-I want to know, is there any way that I just say project1 and it tells me that this is a project.
-or I query Alice and it says me this is a person?
Thanks
So your use case is to search things by name, and those things can be of several types instead of a single type.
Just to note, in general, this is not what Neo4j is built for. Typically in Neo4j queries you know the type of the thing you're searching for, and you're exploring relationships between that thing (or things) to figure out associations or data derived from that.
That said, there are ways to do this, though it's worth going through the rest of your use cases and seeing if Neo4j is really the best tool for what you're trying to do
Whenever you're querying by a property, you either want a unique constraint on the label/property, or an index on the label/property. Note that you need a combination of a label and a property for this; you cannot blindly ask for a node with a property without specifying a label and get good performance, as it will have to do a scan of all nodes in your database (there are some older manual indexes in Neo4j, but I'm not sure if these will continue to be supported; the schema indexes are recommended by the developers).
There is a workaround to this, as Neo4j allows multiple labels on the same node. If you only expect to query certain types by name (for example, only projects and people), you might create a :Named label, and set that label on all :Project and :Person nodes (and any other labels where it should apply). You can then create an index on :Named.name. That way your query would be something like:
MATCH (n:Named)
WHERE n.name = 'blah'
WITH LABELS(n) as types
WITH FILTER(type in types WHERE type <> 'Named') as labels
RETURN labels
Keep in mind that you haven't specified if a name should be unique among node types, so it could be possible for a :Person or a :Project or multiple :Persons to have the same name, unsure how that affects what should happen on your end. If every named thing ought to have a unique name, you should create a unique constraint on :Named.name (though again, it's on you to ensure that every node you create that ought to be :Named has the :Named label applied on creation).
You should use node labels (like Person and Project) to represent node "types".
For example, to create a person and a project:
CREATE (:Person {name: 'Alice', age: 20})
CREATE (:Project {name: 'project1'})
To find the project(s) named 'Fred':
MATCH (p:Project {name: 'Fred'})
RETURN p;
To get a collection of the labels of node n, you can invoke the LABELS(n) function. You can then look in that collection to see if the label you are looking for is in there. For example, if your Cypher query somehow obtains a node n, then this snippet would return n if and only if it has the Person label:
.
.
.
WHERE 'Person' IN LABELS(n)
RETURN n;
[UPDATED]
If you want to find all nodes with the name property value of "Fred":
MATCH (n {name: 'Fred'})
...
If you want to find all relationships with the name property value of "Fred":
MATCH ()-[r {name: 'Fred'})-()
...
If you want to match both in a single query, you have many ways to do that, depending on your exact use case. For example, if you want a cartesian product of the matching nodes and relationships:
OPTIONAL MATCH (n {name: 'Fred'})
OPTIONAL MATCH ()-[r {name: 'Fred'})-()
...

Using Where clause in Multiple relationships in Neo4j

Apparently it seems like the following WHERE clause will not work because we have two relationships (WorksAt and ResponsibleFor) in our query. If there was only one relationship then this would work like magic. Here in the query below the query returns all the courses in teh department science but it does not filter out courses NOT taught by Maria Smith. All i want to do is get only the courses taught by Maria Smith who works in Science Department.
I came across WITH and Start Clause that seem to be potential candidate clauses make it work where you could filter out one part of the query before sending it to another.
http://neo4j.com/docs/stable/query-with.html
but i havent been able to grasp the concept yet. Anyone up for help?
MATCH (d:Department)<-[w:WorksAt]-(t:Tutor)-[r:ResponsibleFor]->(c:Courses)
WHERE d.name='Science'
AND t.name='Maria Smith'
return c,r
There are a number of ways to skin this particular cat. Let's break it down.
Find the tutor whose name is 'Maria Smith' that works in the 'Science' department
MATCH (d:Department)<-[:WorksAt]-(t:Tutor)
WHERE d.name = 'Science' AND t.name = 'Maria Smith'
RETURN t
Find the courses that a tutor teaches
MATCH (t:Tutor)-[:ResponsibleFor]->(c:Courses)
RETURN t.name, c
Bring these two together to get the courses that Maria Smith from the Scence department teaches
MATCH (d:Department)<-[:WorksAt]-(t:Tutor)
WHERE d.name = 'Science' AND t.name = 'Maria Smith'
WITH t
MATCH (t)-[r:ResponsibleFor]->(c:Courses)
RETURN t.name, r, c
This can also be written as
MATCH (d:Department { name : 'Science' })<-[:WorksAt]-(t:Tutor { name : 'Maria Smith' })
WITH t
MATCH (t)-[r:ResponsibleFor]->(c:Courses)
RETURN t.name, r, c
To maximise query performance you can use schema indexes to quickly locate your Department and Tutor nodes. Are you doing this? To create the indexes use
CREATE INDEX ON :Department(name)
CREATE INDEX ON :Tutor(name)
Run these lines separately.
As an aside were you to want to list the courses that each tutor taught, as suggested above in the second query, you could use the following query to aggregate the courses for each tutor.
MATCH (t:Tutor)-[:ResponsibleFor]->(c:Courses)
RETURN t.name as CourseTutor, collect(c.name) as CourseName
Hope this helps.
Nice breakdown. For performance details on this type of query, refer to Wes Freeman's Pragmatic Cypher Optimization. In setting up the match, start with the smaller node set and work toward the larger (Wes's Rule 4).

Grouping nodes by their relationship

I've built a simple graph of one node type and two relationship types: IS and ISNOT. IS relationships means that the node pair belongs to the same group, and obviouslly ISNOT represents the not belonging rel.
When I have to get the groups of related nodes I run the following query:
"MATCH (a:Item)-[r1:IS*1..20]-(b:Item) RETURN a,b"
So this returns a lot of a is b results and I added some code to group them after.
What I'd like is to group them modifying the query above, but given my rookie level I haven't yet figured it out. What I'd like is to get one row per group like:
(node1, node3, node5)
(node2,node4,node6)
(node7,node8)
I assume what you call groups are nodes present in a path where all these nodes are connected with a :IS relationship.
I think this query is what you want :
MATCH p=(a:Item)-[r1:IS*1..20]-(b:Item)
RETURN nodes(p) as nodes
Where p is a path describing your pattern, then you return all the nodes present in the path in a collection.
Note that, a simple graph (http://console.neo4j.org/r/ukblc0) :
(item1)-[:IS]-(item2)-[:IS]-(item3)
will return already 6 paths, because you use undericted relationships in the pattern, so there are two possible paths between item1 and item2 for eg.

Cypher query to find nodes that have 3 relationships

I figured out how to write this query when I am looking for 2 relationships, but not sure how to add more relationships to the query.
Assume you have a book club database with 'reader' and 'book' as nodes. The 'book' nodes have a 'genre' attribute (to define that the book is a Fiction, Non-Fiction, Biography, Reference, etc.) There is a Relationship "HasRead" between 'reader' nodes and 'book' nodes where someone has read a particular book.
If I want to find readers that have read both Fiction AND Non-Fiction books, I could execute this Cypher query:
Start b1=node:MyBookIndex('Genre:Fiction'),
b2=node:MyBookIndex('Genre:Non-Fiction')
Match b1-[:HadRead]-r-[:HasRead]-b2
Return r.ReaderName
The key to the above query is the Match clause that has the two book aliases feeding into the r alias for the 'reader' nodes.
Question: How would I write the query to find users that have read Fiction AND Non-Fiction AND Reference books? I'm stuck with how you would write the Match clause when you have more than 2 things you are looking for.
You can have multiple line specified in a single MATCH clause, separated by commas. For example, the following two MATCH clauses are semantically equivalent (and will be evaluated identically by the engine):
//these mean the same thing!
match a--b--c
match a--b, b--c
You can have any number of these matches. So, plugging that into your query, you get this:
start b1=node:MyBookIndex('Genre:Fiction'),
b2=node:MyBookIndex('Genre:Non-Fiction'),
b3=node:MyBookIndex('Genre:Reference')
match b1-[:HasRead]-r,
b2-[:HasRead]-r,
b3-[:HasRead]-r
return r.ReaderName
You can user cypher 'with' clause -
start b1=node:MyBookIndex('Genre:Fiction'),
b2=node:MyBookIndex('Genre:Non-Fiction'),
b3=node:MyBookIndex('Genre:Reference')
match b1-[:HasRead]-r-[:HasRead]-b2
with b3, r
match b3-[:HasRead]-r
return r.ReaderName

Resources