Neo4j- APOC trigger fires without any reason - neo4j

I have the following two triggers named 'loadEnrollments' and 'loadDeenrollments'.
CALL apoc.trigger.add('loadEnrollments',
"UNWIND apoc.trigger.nodesByLabel($assignedLabels, 'Enrollment') AS node
MERGE (p1:SPerson { name: node.name, cell: node.cell, created_at: node.created_at})
WITH p1, node
MATCH (n:SPerson)
WITH node, COUNT(n) as size
CALL apoc.do.when(
size>3,
'
MATCH(p1:SPerson),(c:Course)
WHERE p1.name=node.name AND c.name=\"Paradigm Shifting 101\"
CREATE (p1)-[:Waitlist]->(c)
SET p1.status=2
WITH node
RETURN NULL',
'
MATCH(p1:SPerson),(c:Course)
WHERE p1.name=node.name AND c.name=\"Paradigm Shifting 101\"
CREATE (p1)-[:Enrolled]->(c)
SET p1.status=1
WITH node
RETURN NULL', {node:node}) YIELD value
DETACH DELETE node",
{ phase: 'after' })
CALL apoc.trigger.add('loadDeenrollments',
"
UNWIND apoc.trigger.nodesByLabel($assignedLabels, 'Deenrollment') AS node
MATCH (p1:SPerson {name: node.name, cell: node.cell})
MATCH (c:Course {name: 'Paradigm Shifting 101'})
CREATE (p1)-[:Deenrolled]->(c)
SET p1.status=3
WITH p1, node,c
MATCH (p1:SPerson {name: node.name, cell: node.cell})-[r:Enrolled]->(c)
DELETE r
DETACH DELETE node
WITH p1,c
MATCH (p1)-[r:Enrolled]->(c)
WITH COUNT(r) as k
CALL apoc.do.when(
k<3, '
MATCH (p1:SPerson)-[:Waitlist]->(c:Course)
WITH min(p1.created_at) AS min
MATCH (p1:SPerson),(c:Course)
WHERE p1.created_at = min
CREATE (p1)-[:Random]->(c)
RETURN p1,c',
'
MATCH (n:SPerson) RETURN n
',{k:k}
) YIELD value
RETURN NULL",
{ phase: 'after' })
When I load both of them and create a 'Enrollment' nodes by the following commands. The 'loadenrollment' trigger works as desired and create SPerson nodes for cat1,cat2 and cat3 and creates an 'enrolled' relationship with the 'course' node.
CREATE (:Enrollment { name: "cat1", cell: "123", created_at: TIMESTAMP()});
CREATE (:Enrollment { name: "cat2", cell: "123", created_at: TIMESTAMP()});
CREATE (:Enrollment { name: "cat3", cell: "123", created_at: TIMESTAMP()});
The Problem occurs when I create the 4th node
CREATE (:Enrollment { name: "cat4", cell: "123", created_at: TIMESTAMP()});
Ideally, it should create a SPerson node for cat4 and add a 'waitlist' relationship with the 'course' node.
But for some reason when I create that node it adds the 'waitlist' relationship but also adds the 'Random' relationship that I defined in the second trigger('loadDeenrollments'). This should never happen as this trigger would only trigger when I create a 'Deenrollment' node but for some reason it is executing that trigger.
Also, I tried adding just the 'loadEnrollments' trigger and it works as desired(obviously, as there is no 'loadDeenrollments' trigger) i.e. creating the four 'SPerson' nodes and with three nodes having 'enrolled' relationship and one having 'waitlist' relationship.
I don't know whats's wrong. Any help is appreciated!

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.

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;

When I UNWIND an empty array, the parent item doesn't MERGE. How do I get this query to work?

UNWIND { newGames } as gameItem
UNWIND gameItem.release_dates as releaseDateItem
UNWIND gameItem.publishersWithName as publisherItem
UNWIND gameItem.developersWithName as developerItem
MERGE (game:Game {id: gameItem.id})
ON CREATE SET game = {${gameItemTemplate}}
ON MATCH SET game = {${gameItemTemplate}}
MERGE (platform:Platform {name: releaseDateItem.platform})
MERGE (publisher:GameCompany {name: publisherItem.name})
MERGE (developer:GameCompany {name: developerItem.name})
MERGE (game)-[:RELEASED {date: releaseDateItem.date}]->(platform)
MERGE (publisher)-[:PUBLISHED]->(game)
MERGE (developer)-[:DEVELOPED]->(game)
gameItem.publishersWithName and gameItem.developersWithName can potentially be empty. In such cases, the Game doesn't get added.
When I remove all the publisher and developer stuff (or split the queries in 2, but then I have to UNWIND newGames twice...), they are added successfully:
UNWIND { newGames } as gameItem
UNWIND gameItem.release_dates as releaseDateItem
MERGE (game:Game {id: gameItem.id})
ON CREATE SET game = {${gameItemTemplate}}
ON MATCH SET game = {${gameItemTemplate}}
MERGE (platform:Platform {name: releaseDateItem.platform})
I'd like to add the Game even if the gameItem.publishersWithName or gameItem.developersWithName is [].
UNWIND turns an empty array into 0 rows, that's why the query doesn't continue.
2 solutions :
a) use FOREACH instead
b) use a CASE :
UNWIND CASE length({yourVar}) WHEN 0 THEN [null] ELSE {yourVar} END
AS it
// continue query
NB: will be addressed in APOC

match property on nodes in path of cypher shortestPath

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)

Resources