Using `WITH` or `MATCH` clause while extracting paths - neo4j

Before I explain the problem I am facing, I think it would be better if I explain the structure of my graph database a bit. Following are the Nodes, Relationships and Properties in my neo4j graph database:
Nodes:
User
Email
Phone
Book
BlogPost
Relationships:
HAS (used as : user -[:HAS]-> (e:Email|Phone))
READ (used as : user - [:READ] -> (b:Book|BlogPost))
WROTE (used as: user - [:WROTE] -> (b:Book|BlogPost))
Properties:
uid (associated with nodes User, Book, BlogPost, Email, Phone)
name (associated with node User)
title (associated with nodes Book, BlogPost)
email (associated with node Email)
phone (associeated with node Phone)
Before I actually put up my question I would like to state that I am pretty new to the neo4j technology so I do understand that solution to my problem might be basic.
What I am trying to achieve ?
I want to get all the shortest paths between two users and then for each node between those two users in a path I would like to get:
The email address and/or phone number if the node is a User node.
Title of the node if it's a Book or a BlogPost node.
Where am I stuck ?
I can get the paths using the following cypher query:
match (u1: User {uid: '0001'}),
(u2: User{uid: '0002'}),
paths = allShortestPaths((u1)-[*..4]-(u2))
return paths
After getting the paths I can get the properties of the nodes, relationships in the path using the extract, nodes, relationships functions if I ammend the above query as follows:
match (u1: User {uid: '0001'}),
(u2: User{uid: '0002'}),
paths= allShortestPaths((u1)-[*..4]-(u2))
with extract(n in nodes(paths) | [n.name]) as nds,
extract(r in relationships(paths)| [type(r)]) as rels
return nds,rels
The problem
But what I really want to get is the email and phone properties off Email and Phone nodes that are connected to any nodes within a given path.
I have gone through the official documentation and a lot of blog posts but could not find any solution or a similar problem for that matter.
How can I achieve the above mentioned ?
PS: I would like to achieve this in a single query
Thanks

Does something like this meet your needs?
Major edits below:
In the return statement below:
Get the list of nodes in the path excluding the first and last nodes
Use labels to inspect only user, book and blog posts
Filter out the intermediate user nodes into one collection
Filter out the intermediate book/blog posts and extract the titles into another collection
Iterate over the intermediate user nodes to find the associated email and or phone nodes (credit #FrobberOfBits)
Here is the cypher:
// match the user nodes and the shortest intermediate paths between them
match (u1:User {uid: '0001'})
, (u2:User {uid: '0002'})
, path = allShortestPaths((u1)-[*..4]-(u2))
// filter out the intermediate user nodes into one collection
with filter(n IN nodes(path)[1..length(nodes(path))-1]
where any(x in labels(n) where x = 'User'))
as intermediate_users
// filter out the intermediate book titles into another collection
, [n IN nodes(path)[1..length(nodes(path))-1]
where any(x in labels(n) where x in ['Book','BlogPost'])
| n.title ]
as intermediate_books_posts
// iterate through the intermediate user nodes
// and optionally match email and phone nodes
unwind intermediate_users as u
optional match (e:Email)<--u-->(p:Phone)
return u.name, e.address, p.number, intermediate_books_posts

Related

Neo4j return subgraph based on certain id

My database stores family relationships.
What I am trying to do is to return the whole family tree of a person with a certain ID.
I have tried the following query:
MATCH (p:Person {id:'1887'})-[r*1..3]-(k:Person)
RETURN distinct(p), r, k
but I didn't get the result that I wanted.
To make it more clear, I would like to get all the relationships that a person with specified ID has, along with the relationships of all the other nodes that he is connected to.
To explain further, here is an example:
Boris is a parent of Mike and Anna. Apart of seeing Boris' relationship to them, I also want to see Mike and Anna's further relationships with their subgraphs.
I believe you should try returning the complete path, like this:
MATCH path = (p:Person {id:'1887'})-[r*1..3]-(k:Person)
RETURN path
This query will store each the path between a person with id = 1887 and another person with depth 1 to 3 and return it. Or if you are interested only in the nodes you can extract it with the nodes() function:
MATCH path = (p:Person {id:'1887'})-[r*1..3]-(k:Person)
RETURN nodes(path)

Duplicate object in a list when I try to map all related entities

According to this post I tried to map all related entities in a list.
I used the same query into the post with a condition to return a list of User but it returns duplicate object
MATCH (user:User) WHERE <complex conditions>
WITH user, calculatedValue
MATCH p=(user)-[r*0..1]-() RETURN user, calculatedValue, nodes(p), rels(p)
Is it a bug? I'm using SDN 4.2.4.RELEASE with neo4j 3.2.1
Not a bug.
Keep in mind a MATCH in Neo4j will find all occurrences of a given pattern. Let's look at your last MATCH:
MATCH p=(user)-[r*0..1]-()
Because you have a variable match of *0..1, this will always return at least one row with just the user itself (with rels(p) empty and nodes(p) containing only the user), and then you'll get a row for every connected node (user will always be present on that row, and in the nodes(p) collection, along with the other connected node).
In the end, when you have a single user node and n directly connected nodes, you will get n + 1 rows. You can run the query in the Neo4j browser, looking at the table results, to confirm.
A better match might be something like:
...
OPTIONAL MATCH (user)-[r]-(b)
RETURN user, calculatedValue, collect(r) as rels, collect(b) as connectedNodes
Because we aggregate on all relationships and connected nodes (rather than just the relationships and nodes for each path), you'll get a single row result per user node.

Cypher query to exclude results based upon relationship

I'm trying to out how to exclude nodes from a query. My graph consists of users, skills, skill scoring, questions and endo
user has skills
skill has scorings in relation with questions ((skill)-->(scorings)-->(questions))
endo is a relationship for users and skills scorings ((user)-->(endos)-->(scorings))
I would like to find all question for user, but exclude those questions who user has already endo relationship
I thought I could do :
MATCH (u:`User`),
u<-[rel1:`USERS`]-(s:`Skill`),
s-[rel2:`SKILLS`]->(k:`SkillScoring`),
k-[rel3:`ANSWER`]->(q:`Question`),
u<-[rel4:`ENDO`]-(e:`Endo`)
WHERE NOT((e)-->(u)) AND (u.id='1')
RETURN u, e, k, q
UPDATE:
endo nodes are connected like this.
blue is user node
purple is journaled endorsement node (created at)
green is skill scoring node
In fact the relationship "ENDORSEMENT" has a node (journalised) which connects the skill scoring nodes
UPDATE:
When I execute this query, it returns me the question in connection with the user
MATCH (u:User),
u<-[rel1:USERS]-(s:SoftSkill),
s-[rel2:SOFT_SKILLS]->(k:SkillScoring),
k-[rel3:ANSWER]->(q:Question),
u<-[:ENDO]-()<-[:ENDO]->(k)
WHERE u.id='1'
RETURN q, u
by cons when I execute this query to exclude the question, the query returns me questions but also question that I don't want
MATCH (u:User),
u<-[rel1:USERS]-(s:SoftSkill),
s-[rel2:SOFT_SKILLS]->(k:SkillScoring),
k-[rel3:ANSWER]->(q:Question)
WHERE u.id='1' AND NOT u<-[:ENDO]-()<-[:ENDO]->(k)
RETURN q, u
What's wrong? Any suggestions?
Thanks
Your query first says that u has :ENDO relationship from e, then that e has no relationship to u, but that can't be.
How are endo nodes connected to scorings/questions in those cases you want to exclude? Could you try something like
MATCH (u:User)-[rel1:USERS]->(s:Skill)-[rel2:SKILLS]->(k:SkillScoring)-[rel3:ANSWER]->(q:Question)
WHERE u.id = '1' AND NOT u<-[:ENDO]-()-[:??]->k
and fill in the relationship type at ?? for how the endo node (anonymous node () above) is connected?
If you want you can create an example graph at http://console.neo4j.org, that would help make your intention clear.
Also, it's fine to use backticks around labels and relationships, but you don't need to unless they contain whitespace or some other uncommon characters.

How to find a collection of nodes where there is no relation to another node neo4j

I'm trying to get the nodes that are not related to a node and return them. For example, there is an event node that has pictures, I want to show the user only the pictures that he hasn't seen.
I'm matching the event with pictures relation
Match the user and the relation to pictures of that event that he has seen
Where I'm having problems is how to query the difference between these two matches so I will have the images that have no relation to the user.
MATCH photo=(i)-[r:EVENT_IMAGES]-(e{uuid:'ed3f4785-fc58-4d78-9ae1-ae738814a34a'})
MATCH user=(u{uuid:'4f731ba1-b15d-4a3f-85bd-446057c84cbc'})
RETURN photo, user
You can use a WHERE NOT clause with a pattern to filter out matches for a given pattern. For example:
MATCH (p:Photo)<-[r:EVENT_IMAGES]-(e:Event {uuid:'ed3f4785-fc58-4d78-9ae1-ae738814a34a'})
MATCH (u:User {uuid:'4f731ba1-b15d-4a3f-85bd-446057c84cbc'})
WITH p, u WHERE NOT (u)-[:VIEWED]->(p)
RETURN p, u

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.

Resources