neo4j match node in array order by input - neo4j

I am trying to implement https://neo4j.com/blog/moving-relationships-neo4j/ pointer functionality for using it as a team order machine.See http://imgur.com/a/MViF0 for a model. I am using this cypher query.
MERGE (list:LIST)
WITH list
MATCH (u) WHERE ID(u) IN [421, 419, 420]
MERGE (team:TEAM{name: u.name})
MERGE (team)-[:PARTOF]->(list)
WITH collect(team)as elems,list
FOREACH (n IN RANGE(0, LENGTH(elems)-2) |
FOREACH (prec IN [elems[n]] |
FOREACH (next IN [elems[n+1]] |
MERGE (prec)-[:NEXT]->(next))))
with list
MATCH (elem:TEAM) WHERE NOT (elem)<-[:NEXT]-()
MERGE (list)-[:POINTER]->(elem)
Now this works quite nicely, but I have only one problem. This line:
MATCH (u) WHERE ID(u) IN [421, 419, 420]
returns my original teams ordered by id, but I would like to define my order by the pattern in the [421,419,420] pattern, like a function that
return * order by my array input.
Keep in mind that it should work for any amount of teams,this is just an example. And that my original team node isn't labeled a team but something else, so we make a duplicate every time. Any input appreciated, thanks.

Try to use the statement "unwind":
MERGE (list:LIST)
WITH list
UNWIND [421, 419, 420] as uid
MATCH (u) WHERE id(u) = uid
MERGE (team:TEAM{name: u.name})
...
[Update] Of course, it is possible to know the order manually for each node:
MERGE (list:LIST)
WITH list, [3871013, 3871011, 3871012] as ids
MATCH (u) WHERE ID(u) IN ids
WITH list, u,
FILTER(x in RANGE(0,size(ids)-1) WHERE ids[x] = id(u)) as orderIndex
ORDER BY orderIndex[0] // Sort by node position in the array of identifiers
MERGE (team:TEAM{name: u.name})
...

Related

Cyper Merge on Optional Match

I am trying to see if node :Customer exists based on a relationship in my optional match. I then want to create some relationships to my customer if they have an order. I am not sure what is the correct syntax for this.
MERGE (o:Order {account: 'j593jfsh', id: '35353'})
OPTIONAL MATCH (c:Customer)-[:HAS_ORDER]->(o)
MERGE (c)-[:HAS_SESSION]->(s)
MERGE (c)-[:HAS_ORDER]->(o)
WHERE c IS NOT NULL"
One way you could do this is to use pattern comprehension in place of the OPTIONAL MATCH. This would collect all customers having orders into a list, and then you could use FOREACH to MERGE the relationships. If there are no customers, the list will be empty, and FOREACH will have nothing to process.
...
MERGE (o:Order {account: 'j593jfsh', id: '35353'})
WITH o, s, [(c:Customer)-[:HAS_ORDER]->(o) | c] as customers
FOREACH (c in customers |
MERGE (c)-[:HAS_SESSION]->(s)
MERGE (c)-[:HAS_ORDER]->(o)
)
...

How to merge several Lists into one List

To find a node's neighbors,I use query sentence as below
MATCH (self:Person {id:"13619240353"})-[r*1..2]-(N) return collect(r)
Then I get the result like this
enter image description here
Here r is a list of relations,thus collect(r) is a list of lists,but I expect to
return a list of relations including all the relations in the collect(r),and without the duplicates.How to write the query?
Since with the variable length of the pattern, the named result is an list, then you need to UNWIND it and use a DISTINCT to remove duplicates:
MATCH (self:Person {id:"13619240353"})-[rs*1..2]-(N)
UNWIND rs AS r
RETURN collect(DISTINCT r)

Neo4J/Cypher Filter nodes based on multiple relationships

Using Neo4J and Cypher:
Given the diagram below, I want to be able to start at node 'A' and get all the children that have a 'ChildOf' relationship with 'A', but not an 'InactiveChildOf' relationship. So, in this example, I would get back A, C and G. Also, a node can get a new parent ('H' in the diagram) and if I ask for the children of 'H', I should get B, D and E.
I have tried
match (p:Item{name:'A'}) -[:ChildOf*]-(c:Item) where NOT (p)-[:InactiveChildOf]-(c) return p,c
however, that also returns D and E.
Also tried:
match (p:Item{name:'A'}) -[rels*]-(c:Item) where None (r in rels where type(r) = 'InactiveChildOf') return p,c
But that returns all.
Hopefully, this is easy for Neo4J and I am just missing something obvious. Appreciate the help!
Example data: MERGE (a:Item {name:'A'}) MERGE (b:Item {name:'B'}) MERGE (c:Item {name:'C'}) MERGE (d:Item {name:'D'}) MERGE (e:Item {name:'E'}) MERGE (f:Item {name:'F'}) MERGE (g:Item {name:'G'}) MERGE (h:Item {name:'H'}) MERGE (b)-[:ChildOf]->(a) MERGE (b)- [:InactiveChildOf] ->(a) MERGE (c)-[:ChildOf]->(a) MERGE (d)-[:ChildOf]->(b) MERGE (e)-[:ChildOf]->(b) MERGE (f)-[:ChildOf]->(c) MERGE (f)- [:InactiveChildOf] ->(c) MERGE (g)-[:ChildOf]->(c) MERGE (b)-[:ChildOf]->(h)
Note, I understand that I could simply put an "isActive" property on the ChildOf relationship or remove the relationship, but I am exploring options and trying to understand if this concept would work.
If a query interpreted as: find all the nodes, the path to which passes through the nodes unrelated by InactiveChildOf to the previous node, the request might be something like this:
match path = (p:Item{name:'A'})<-[:ChildOf*]-(c:Item)
with nodes(path) as nds
unwind range(0,size(nds)-2) as i
with nds,
nds[i] as i1,
nds[i+1] as i2
where not (i1)-[:InactiveChildOf]-(i2)
with nds,
count(i1) as test
where test = size(nds)-1
return head(nds),
last(nds)
Update: I think that this version is better (check that between two nodes there is no path that will contain at least one non-active type of relationship):
match path = (p:Item {name:'A'})<-[:ChildOf|InactiveChildOf*]-(c)
with p, c,
collect( filter( r in rels(path)
where type(r) = 'InactiveChildOf'
)
) as test
where all( t in test where size(t) = 0 )
return p, c
By reading and examining the graph, correct me if I'm wrong but the actual text representation of the cypher query should be
Find me nodes in a path to A, all nodes in that path cannot have an outgoing
InactiveChildOf relationship.
So, in Cypher it would be :
MATCH p=(i:Item {name:"A"})<-[:ChildOf*]-(x)
WHERE NONE( x IN nodes(p) WHERE (x)-[:InactiveChildOf]->() )
UNWIND nodes(p) AS n
RETURN distinct n
Which returns

Order list without scanning every node

When using LIMIT with ORDER BY, every node with the selected label still gets scanned (even with index).
For example, let's say I have the following:
MERGE (:Test {name:'b'})
MERGE (:Test {name:'c'})
MERGE (:Test {name:'a'})
MERGE (:Test {name:'d'})
Running the following gets us :Test {name: 'a'}, however using PROFILE we can see the entire list get scanned, which obviously will not scale well.
MATCH (n:Node)
RETURN n
ORDER BY n.name
LIMIT 1
I have a few sorting options available for this label. the order of nodes within these sorts should not change often, however, I can't cache these lists because each list is personalized for a user, i.e. a user may have hidden :Test {name:'b'}
Is there a golden rule for something like this? Would creating pointers from node to node for each sort option be a good option here? Something like
(n {name:'a'})-[:ABC_NEXT]->(n {name:'b'})-[:ABC_NEXT]->(n {name:'c'})-...
Would I be able to have multiple sort pointers? Would that be overkill?
Ref:
https://neo4j.com/blog/moving-relationships-neo4j/
http://www.markhneedham.com/blog/2014/04/19/neo4j-cypher-creating-relationships-between-a-collection-of-nodes-invalid-input/
Here's what I ended up doing for anyone interested:
// connect nodes
MATCH (n:Test)
WITH n
ORDER BY n.name
WITH COLLECT(n) AS nodes
FOREACH(i in RANGE(0, length(nodes)-2) |
FOREACH(node1 in [nodes[i]] |
FOREACH(node2 in [nodes[i+1]] |
CREATE UNIQUE (node1)-[:IN_ORDER_NAME]->(node2))))
// create list, point first item to list
CREATE (l:List { name: 'name' })
WITH l
MATCH (n:Test) WHERE NOT (m)<-[:IN_ORDER_NAME]-()
MERGE (l)-[:IN_ORDER_NAME]->(n)
// getting 10 nodes sorted alphabetically
MATCH (:List { name: 'name' })-[:IN_ORDER_NAME*]->(n)
RETURN n
LIMIT 10

Cant use Merge inside foreach for existed nodes

I am expecting he following query to create nodes (only if exits) and relations by a given source node (1) and a list(2) this way:
MERGE (p1:C9{userId: '1'}) WITH p1, [{userId:"2"}] AS users
FOREACH (user IN users | MERGE
((p1)-[r1:follow]->(:C9 {userId: user.userId})))
Thats the outcome:
Now if I am executing this query again by switching the node id's this way:
MERGE (p1:C9{userId: '2'}) WITH p1, [{userId:"1"}] AS users
FOREACH (user IN users | MERGE
((p1)-[r1:follow]->(:C9 {userId: user.userId})))
We got this:
neo4j duplicated for me the node with id=1. I want it to merge in case of existed nodes.
I expected to see only two nodes connected to each other by merging existed nodes.
any idea what I should fix?
Thanks,
ray.
I normally avoid FOREACH when I can use an UNWIND, so I would start with something like this:
MERGE (p1:C9 {userId: '1'})
WITH p1, [{userId:"2"}] AS users
UNWIND users AS user
MERGE (p1)-[r1:follow]->(:C9 {userId: user.userId})
Sometimes you also want to separate your node creation from your relationship creation. If you do both at the same time, I think that Neo4j can think that you want a unique combination of node (with properties) and relationship.
MERGE (p1:C9 {userId: '1'})
WITH p1, [{userId:"2"}] AS users
UNWIND users AS user
MERGE (p2:C9 {userId: user.userId})
MERGE (p1)-[r1:follow]->(p2)
You can use MERGE within FOREACH.
But you have to understand the semantics of MERGE. It tries to MATCH a full pattern and if it does not find it it will fully CREATE that pattern.
You in your case you try to find a pattern within the context of p1 and not globally and if not found it will create it within the context of p1.
So if you change your query to:
MERGE (p1:C9{userId: '2'})
WITH p1, [{userId:"1"}] AS users
FOREACH (user IN users |
MERGE (p2:C9 {userId: user.userId})
MERGE (p1)-[r1:follow]->(p2)
)
I.e. create p2 first and then MERGE the relationship, it will work.

Resources