match property on nodes in path of cypher shortestPath - neo4j

For simplicity sake, let's say I have a graph like this (using actors/movies as an example):
Nodes:
Actor(id:1)
Actor(id:2)
Actor(id:3)
Movie(id:4,rating:'PG-13')
Movie(id:5,rating:'PG-13')
Relationships:
Actor(id:1) APPEARS_IN Movie(id:4)
Actor(id:2) APPEARS_IN Movie(id:4)
Actor(id:2) APPEARS_IN Movie(id:5)
Actor(id:3) APPEARS_IN Movie(id:5)
Cypher to create the sample graph:
create (a1:Actor {id: 1, name: 'Actor 1'})
create (a2:Actor {id: 2, name: 'Actor 2'})
create (a3:Actor {id: 3, name: 'Actor 3'})
create (m1:Movie {id: 4, rating:'PG-13', name: 'Movie 1' } )
create (m2:Movie {id: 5, rating:'PG-13', name: 'Movie 2' } )
create (a1)-[:APPEARS_IN]->(m1)
create (a2)-[:APPEARS_IN]->(m1)
create (a2)-[:APPEARS_IN]->(m2)
create (a3)-[:APPEARS_IN]->(m2)
return *
So now we have want to find the shortest path between Actor(id:1) and Actor(id:3). That's easy enough, we might try a cypher query like:
MATCH p=shortestPath((a:Actor { id: 1 })-[:APPEARS_IN*0..14]-(b:Actor { id: 3 })) RETURN NODES(p)
And we would get a result.
This is where my question comes in How can I put a requirement on the nodes in between (specifically the movie nodes) to only include rating:'R' as part of its path?

This query will only return the nodes of the shortest path between the two actors where all of the movies are R rated.
It filters out the Movie nodes and then checks to make sure that the rating of each movie node in the collection is R.
MATCH p=shortestPath((a:Actor { id: 1 })-[:APPEARS_IN*0..14]-(b:Actor { id: 3 }))
WHERE all ( movie in filter( m in nodes(p) where 'Movie' in labels(m)) where movie.rating = 'R')
RETURN nodes(p)

Related

Shortest Path using Neo4j, with relationships of Start and End Node

I'm fairly new to Neo4j. I'm trying to get the shortest path for default movie database.
However, along with it I also need the start and end node(of type Person) to display there first level relation to any other node and if any person node on the path have a relation to the start and end node via some other node. Its better to explain with below pictures.
Normal shortestpath will look, something like this.
MATCH (p1:Person { name: 'Kevin Bacon' }),(p2:Person { name: 'Meg Ryan' }),
p = shortestPath((p1)-[*..15]-(p2))
return p
However my desired output is this one.
I tried below cypher. However i cannot understand, how to do it. I'm getting below result, which is incorrect.
MATCH (p1:Person { name: 'Kevin Bacon' }),(p2:Person { name: 'Meg Ryan' }),
p = shortestPath((p1)-[*..15]-(p2))
MATCH (p1:Person)-[r]-(b) // here i need foreach node on path for type person, get there relationships? However can't do that
return p,p1,r,b
Appreciate any help. Thanks.
You can get all the movies in the start and end nodes of shortest path. Then add the movies with the path.
MATCH p = shortestPath((p1:Person { name: 'Kevin Bacon' })-[*..15]-(p2:Person { name: 'Meg Ryan' }))
WITH p1, p2, p
MATCH (p1)-[:ACTED_IN]->(m1)
MATCH (p2)-[:ACTED_IN]->(m2)
RETURN p, m1, m2
Thanks for the reply Jose. It's close. I don''t want to restrict the relation just to "ACTED_IN" with (Movie). I just did a small modification.
MATCH p = shortestPath((p1:Person { name: 'Kevin Bacon' })-[*..15]-
(p2:Person { name: 'Meg Ryan' }))
UNWIND nodes(p) as n
MATCH (n)-[*]->(q)
RETURN n, q
However, i don't need the nodes highlighted in the box as they are not related to Meg and Kevin. Not sure how to exclude them.

Adding relationships between an array of array of objects in Neo4j Cypher

I have three arrays of objects as shown below:
const org = [
{
id: "orgId1",
name: "first organization"
},
{
id: "orgId2",
name: "second organization"
}
]
const location = [
{
id: "loc1",
name: "Texas"
},
{
id: "loc2",
name: "New York"
}
]
const contacts = [
{
id: "contact1",
name: "James"
},
{
id: "contact2",
name: "John"
}
]
What is the optimal way to add relationships between them? Note that the arrays are of the same length.
I need a Cypher query that can loop through a range from 0 to orgs.length, and add corresponding relationships between each element at i. e.g org[i], contacts[i], location[i]
I tried the following, but it gives me an explosive combination where the first org maps to all the entries in location array and contact array, when I want is a one-to-one mapping.
UNWIND $orgs as orgs
UNWIND $locations as locs
UNWIND $contacts as contacts
FOREACH (i IN range(0, size(orgs) - 1)
| MERGE (:Organization { id: orgs[i].id })-[r:LOCATED_AT]->(:Location {id: locs[i].id})
| MERGE (:Organization { id: orgs[i].id })-[r:CONTACT_AT]->(:Contact {id: contacts[i].id})
)
Any help would be appreciated. Thanks in advance.
I don't think you need to UNWIND all arrays
WITH $orgs AS orgs,
$locs AS locs,
$contacts AS contacts
UNWIND $orgs as orgs
FOREACH (i IN range(0, size(orgs) - 1) |
MERGE (org:Organization { id: orgs[i].id })
MERGE (loc:Location {id: locs[i].id})
MERGE (contact:Contact {id: contacts[i].id})
MERGE (org)-[:LOCATED_AT]->(loc)
MERGE (org)-[:CONTACT_AT]->(contact)
)
should do it
A solution was posted in the official Neo4j site. I'm sharing here as well.
UNWIND range(0, size($orgs) - 1) as i
with i
MERGE (o:Organization { id: $orgs[i].id })-[r:LOCATED_AT]->(l:Location {id: $locs[i].id})
with o, i
MERGE (o)-[r2:CONTACT_AT]->(l2:Contact {id: $contacts[i].id})
Link to original answer

Return relations as child nodes in tree (Neo4j Cypher)

Is there a way to retrieve relations as child elements of a tree?
This is the basic data i have:
CREATE (:Customer {id:1, name:'Customer 1'})<-[:CREATED_BY]-(c:Category {id:1, name:'Category 1'})
WITH c as category, range(2, 7) as subCatIds
FOREACH (s IN subCatIds | CREATE (category)<-[:SUBCATEGORY_OF]-(:Category {id:s, name:'SubCategory '+s}))
WITH category, range(1, 5) as attTypeIds
FOREACH (a IN attTypeIds | CREATE (category)-[:HAS_ATTRIBUTE {name:'Attribute '+a, required: (a%2=0)}]->(:AttributeType {id:a, name:'AttributeType '+a}))
WITH category
MATCH p = (:AttributeType)<-[:HAS_ATTRIBUTE]-(category)<-[:SUBCATEGORY_OF]-(:Category)
RETURN p
So this query returns correctly the tree structure:
MATCH (:Customer {id:1})<-[:CREATED_BY]-(c:Category {id:1}),
p = (c)<-[:SUBCATEGORY_OF*0..1]-(:Category)
WITH COLLECT(p) as category
CALL apoc.convert.toTree(category) yield value
RETURN value
How do i add the relationships [:HAS_ATTRIBUTE] as child nodes to this query?
I've tried already:
MATCH (:Customer {id:1})<-[:CREATED_BY]-(c:Category {id:1}),
SubCatsP = (c)<-[:SUBCATEGORY_OF*0..1]-(:Category),
AttP = (c)-[:HAS_ATTRIBUTE]->(att:AttributeType)
WITH COLLECT(SubCatsP) as category, RELATIONSHIPS(AttP) as attributes
CALL apoc.convert.toTree(category) yield value
RETURN value, attributes
But this returns 5 records (1 for each relationship [:HAS_ATTRIBUTE]) with the Category-Subcategories tree repeated.
I expect the result to be:
{
id: 1,
name: 'Category 1',
SUBCATEGORY_OF:[
{id:2, name: 'Subcategory 2'}, ...
]
HAS_ATTRIBUTE:[
{name: 'Attribute 1', required: false, att: {name:'AttributeType 5',id:5}}, ...
<OR>
{name: 'Attribute 1', required: false, att.name:'AttributeType 5',att.id:5}, ...
]
}
Is this even possible or do you consider a better approach to perform 2 separate queries?
You can try combining the paths into a single list:
MATCH (:Customer {id:1})<-[:CREATED_BY]-(c:Category {id:1}),
SubCatsP = (c)<-[:SUBCATEGORY_OF*0..1]-(:Category),
AttP = (c)-[:HAS_ATTRIBUTE]->(att:AttributeType)
WITH COLLECT(SubCatsP) + COLLECT(AttP) as category
CALL apoc.convert.toTree(category) yield value
RETURN value

How to return all properties of a node with distinct on one property using Cypher

I am new to cypher, and I want to get data after using 'DISTINCT', but I could only get the value of 'DISTINCT' property, for example:
CREATE (n:person {name: "a", age: 22})
CREATE (n:person {name: "a", age: 23})
CREATE (n:person {name: "a", age: 24})
I want to get only one node with label "person" whose name is "a", so I try query like this
MATCH (n:person) RETURN DISTINCT n.name
This only return "a", but I want all properties and values of the node, which is {name: "a", age:22}, what should I do?
You can try this query :
MATCH (n:person)
WITH n.name, collect(n) AS persons
RETURN persons[0]
collect is an aggregate function, so in it you will have all the node aggreagted by n.name, and I return the first element.
To get just one person node with the name "a":
MATCH (n:person {name: "a"})
RETURN n
LIMIT 1;

How to report named properties in JSON format from a cypher query?

I have graph in which nodes are linked to other nodes containing properties. For instance:
(:Person {Name: 'A'}) -[:OWNS]-> (:Object {Name: 'Car', Value: 2})
(:Person {Name: 'A'}) -[:OWNS]-> (:Object {Name: 'Computer', Value: 1})
Is it possible (how) to report tagged data from a cypher query. Expected format would preferably be:
{Person: 'A', owns: {Car: 2, Computer: 1}}
My current query is:
match (p:Person) -[:OWNS]-> (o:Object)
return {Person: p.Name, owns: collect({name: o.Name, value: o.Value})
this returns
{Person: 'A', owns: [{name: 'Car', value: 2}, {name: 'Computer', value: 1}]}
Unfortunately, I can't write something like:
return {Person: p.Name, owns: collect({o.Name: o.Value})
Assuming that a person can have any number of cars and computers (including 0):
OPTIONAL MATCH (p:Person)-[:OWNS]->(car:Object { Name: 'Car' })
OPTIONAL MATCH (p)-[:OWNS]->(comp:Object { Name: 'Computer' })
RETURN { Person: p.Name, owns: { Cars: COLLECT(car.Value), Computers: COLLECT(comp.Value)}} AS result;
Here is a sample result for 2 cars and 0 computers for person A:
{Person:"A", owns:{"Cars":[2,3],"Computers":[]}}
1: Fetch all nodes: - START n=node (*) RETURN n; or match (n) return n
2: Displays the nodes and the relationships: - MATCH (n) MATCH (n)-[r]-() RETURN n,r Or START n=node(*) MATCH (n)-[r]->(m) RETURN n,r,m
3: Match nodes and relationships: - MATCH (a:Policy)-[:APPLIES_TO]-(Cluster) WHERE a.name = "pol-1nils" RETURN a, Cluster
4: Get all object of particular nodes: - MATCH (list:Policy) return list
5: bound to the entities between two nodes: - match (a:WorkLoad)-[b:APPLIES_TO]->(c:Policy) where c.name = "shamshad" return a,b,c;

Resources