I have the following schema:
(Node a and b are identified by id and are the same in both relationships)
(a)-[r:RelType {comment:'a comment'} ]-(b)
(a)-[r:RelType {comment:'another comment'} ]-(b)
So i have 2 nodes, and an arbitrary number of relationships between them. I want to delete just one of the relationships, and i don t care which one. How can i do this?
I have tried this, but it does not work:
match (a {id:'aaa'})-[r:RelType]-(b {id:'bbb'}) where count(r)=1 delete r;
Any ideas?
Here is the real-world query:
match (order:Order {id:'order1'}),(produs:Product {id:'supa'}),
(order)-[r:ordprod {status:'altered'}]->(produs) with r limit 1 set r.status='alteredAgain'
return (r);
The problem is Chypher says
Set 1 property, returned 1 row in 219 ms
, but when i inspect the database, it turns out all relationships have been updated.
Use the following:
match (a {id:'aaa'})-[r:RelType]-(b {id:'bbb'})
with r
limit 1
delete r
I tried to implement data structure like your's (Mihai's). and gone with both solutions; i.e., Stefan's and Sumit's. Stefan's solution is working at my side. Mihai, are you still facing any problems?
Hope this helps(As per my understanding you are trying to modify a relationship between two given nodes)
MATCH (order:Order {id:'order1'})-[r:ordprod {status:'altered'}]->(produs:Product {id:'supa'})
WITH order,r,produs
LIMIT 1
DELETE r
WITH order,produs
CREATE (order:Order {id:'order1'})-[r:ordprod {status:'alteredAgain'}]->(produs:Product {id:'supa'})
return (r);
And the reason for all your relationships getting updated is that after your WITH clause you are passing only r ie the relationship which may be same between all such nodes of label Order and Product. So when you do r.status = 'alteredagain' it changes all the relationships instead of changing between those two specific nodes that you matched in the beginning of your cypher query. Pass them too in the WITH and it will work fine!
Related
I have a graph with multiple levels, and I need to create a query that would extract all direct child-parent relationships in a table.
For example, if I have a graph like this:
I want as an output, the following table:
Parent Child
1 2
1 3
2 4
2 5
3 6
For now, I am extracting these relations separately in multiple MATCH queries, but I'm sure there is a better way out there. Any help would be very much appreciated! Thank you!
Answer from norihide.shimatani is also valid. As an another option, you can try below one with union all.
MATCH (parent {name:'x'})-->(child)
RETURN parent.number as parentNumber, child.number as childNumber
UNION ALL
MATCH (parent {name:'x'})-->(child1)-->(child2)
RETURN child1.number as parentNumber, child2.number as childNumber
How about this?
MATCH(parent)-->(child)
RETURN DISTINCT
parent.id_of_node, child.id_of_node
ORDER BY
parent.id_of_node, child.id_of_node
;
second answer is
/* collect id_of_node of target nodes by the condition you want */
MATCH path=(parent)-[*0..]->(child)
WHERE parent.name = 'X'
WITH NODES(path) as nodes
UNWIND nodes AS node
WITH COLLECT(DISTINCT node.id_of_node) as ids_of_node
/* using ids_of_node as a condition, get final answer */
MATCH(parent)-->(child)
WHERE parent.id_of_node IN ids_of_node OR child.id_of_node IN ids_of_node
RETURN DISTINCT
parent.id_of_node , child.id_of_node
ORDER BY
parent.id_of_node , child.id_of_node
;
I need to group the data from a neo4j database and then to filter out everything except the top n records of every group.
Example:
I have two node types : Order and Article. Between them there is an "ADDED" relationship. "ADDED" relationship has a timestamp property. What I want to know (for every article) is how many times it was among the first two articles added to an order. What I tried is the following approach:
get all the Order-[ADDED]-Article
sort the result from step 1 by order id as first sorting key and then by timestamp of ADDED relationship as second sorting key;
for every subgroup from step 2 representing one order, keep only the top 2 rows;
Count distinct article ids in the output of step 3;
My problem is that I got stuck at step 3. Is it possible to get top 2 rows for every subgroup representing an order?
Thanks,
Tiberiu
Try
MATCH (o:Order)-[r:ADDED]->(a:Article)
WITH o, r, a
ORDER BY o.oid, r.t
WITH o, COLLECT(a)[..2] AS topArticlesByOrder UNWIND topArticlesByOrder AS a
RETURN a.aid AS articleId, COUNT(*) AS count
Results look like
articleId count
8 6
2 2
4 5
7 2
3 3
6 5
0 7
on this sample graph created with
FOREACH(opar IN RANGE(1,15) |
MERGE (o:Order {oid:opar})
FOREACH(apar IN RANGE(1,5) |
MERGE (a:Article {aid:TOINT(RAND()*10)})
CREATE o-[:ADDED {t:timestamp() - TOINT(RAND()*1000)}]->a
)
)
Use LIMIT combined with ORDER BY to get the top N of anything. For example, the top 5 scores would be:
MATCH (node:MyScoreNode)
RETURN node
ORDER BY node.score DESC
LIMIT 5;
The ORDER BY part ensures the highest scores show up first. The LIMIT gives you only the first 5, which since they're sorted, are always the highest.
I tried to achieve your desired results and failed.
So, my guess - this one is impossible with pure cypher.
What is the problem? Cypher is considering everything as a paths. And actually is doing traverse.
Trying to group results and then execute filter on each group means that cypher should somehow branch it traversing at some points. But Cypher executed filter on all results, because they are considered as collection of different paths.
My suggestion - create several queries, that achieves desired functionality, and implement some client-side logic.
I had a query of this kind, which would basically find a specific node "Statement", find all the nodes connected to it with an :OF relation, find all their connections to one another, as well all the relations of the node "Statement" to other nodes and the node "Statement" itself:
MATCH (s:Statement{uid:"e63cf470-ade4-11e3-bc66-2d7f9b2c7878"}),
c1-[:OF]->s<-[:OF]-c2, c1-[to:TO]->c2
WITH DISTINCT to, c1, c2, s
MATCH c1-[by:BY]->u, c2-[at:AT]->ctx
WHERE to.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
AND by.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
AND at.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
DELETE s,rel,to,by,at;
This worked OK for when there was 3 nodes connected to the node "Statement", but when there's a 100, it crashes the database.
I tried playing around passing different nodes and relationships with a WITH, but it didn't help.
The closest to a solution that I could get was to set up automatic indexing on relationship properties and then execute the deletion with two queries:
MATCH (s:Statement{uid:"e63cf470-ade4-11e3-bc66-2d7f9b2c7878"}),
s-[by:BY]->u, s-[in:IN]->ctx, c-[of:OF]->s DELETE by,in,of,s;
START rel=relationship:relationship_auto_index
(statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878")
DELETE rel;
2 Questions:
1) I know that the first query took too long because there were too many iterations. How to avoid that?
2) Do you know how to combine the two faster queries above into one so that it works fast and preferably without using the relationship index and START clause?
Thank you!
For this statement
You must not separate the condition on to from the match. Then Cypher will find all matches first and only filter after it is done with that.
MATCH (s:Statement{uid:"e63cf470-ade4-11e3-bc66-2d7f9b2c7878"}),
c1-[:OF]->s<-[:OF]-c2, c1-[to:TO]->c2
WHERE to.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
WITH DISTINCT to, c1, c2, s
MATCH c1-[by:BY]->u, c2-[at:AT]->ctx
WHERE by.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
AND at.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
DELETE s,rel,to,by,at;
Also I'm not sure if this c1-[:OF]->s<-[:OF]-c2, c1-[to:TO]->c2 doesn't span up a cross-product.
Just do this:
MATCH (s:Statement{uid:"e63cf470-ade4-11e3-bc66-2d7f9b2c7878"}),
c1-[:OF]->s<-[:OF]-c2, c1-[to:TO]->c2
WHERE to.statement="e63cf470-ade4-11e3-bc66-2d7f9b2c7878"
RETURN count(*),count(distinct c1), count(distinct c2), count(distinct to)
to see some numbers.
You also don't seem to use (u) and (ctx) in the result? So might be an option to convert that into a condition. (Have to try), then you might even be able to leave of the with (if the cardinality with distinct is not much smaller than without.
....
WHERE c2-[:AT {at.statement:"e63cf470-ade4-11e3-bc66-2d7f9b2c7878"}]->()
AND c1-[:BY {statement:"e63cf470-ade4-11e3-bc66-2d7f9b2c7878"}]->()
DELETE s,rel,to,b
HTH
Would love to get the dataset to try it out.
I'm passing in a group of ids to Neo4J to be looked up through an index. I then want to find those common relationships between them, where 1 node in the collection of found nodes points to another. I'm trying to retrieve the path to do this, but I'd also like the option of just retrieving relationship counts (hence the r on the relationship). So far, I've got something like this:
start n = node:idx("MyID:(1 2 3 10000 1500)")
MATCH p=(n)<-[r:RELATED_TO]->(n)
RETURN p
However, this always returns nothing, even though I am sure there are common relationships here.
I have looked around but I can't find any examples of anyone doing this in Cypher. How would I go about achieving this? Or is there a better way of doing this?
Thanks.
But you are creating a cartesian product between the two index lookups.
So if those return more than just 5 nodes, it will grow exponentially.
Your query already does 25 path checks (5x5).
START n = node:idx("MyID:(1 2 3 10000 1500)")
MATCH p=(n)-[r:RELATED_TO]-(m)
WHERE m.MyId IN [1,2,3,10000,1500]
RETURN p
Depending on the number of paths this might be cheaper.
You have to use the index lookup twice:
start n = node:idx("MyID:(1 2 3 10000 1500)"),
m = node:idx("MyID:(1 2 3 10000 1500)")
MATCH p=(n)-[r:RELATED_TO]-(m)
RETURN p
I have a scenario where I have more than 2 random nodes.
I need to get all possible paths connecting all three nodes. I do not know the direction of relation and the relationship type.
Example : I have in the graph database with three nodes person->Purchase->Product.
I need to get the path connecting these three nodes. But I do not know the order in which I need to query, for example if I give the query as person-Product-Purchase, it will return no rows as the order is incorrect.
So in this case how should I frame the query?
In a nutshell I need to find the path between more than two nodes where the match clause may be mentioned in what ever order the user knows.
You could list all of the nodes in multiple bound identifiers in the start, and then your match would find the ones that match, in any order. And you could do this for N items, if needed. For example, here is a query for 3 items:
start a=node:node_auto_index('name:(person product purchase)'),
b=node:node_auto_index('name:(person product purchase)'),
c=node:node_auto_index('name:(person product purchase)')
match p=a-->b-->c
return p;
http://console.neo4j.org/r/tbwu2d
I actually just made a blog post about how start works, which might help:
http://wes.skeweredrook.com/cypher-it-all-starts-with-the-start/
Wouldn't be acceptable to make several queries ? In your case you'd automatically generate 6 queries with all the possible combinations (factorial on the number of variables)
A possible solution would be to first get three sets of nodes (s,m,e). These sets may be the same as in the question (or contain partially or completely different nodes). The sets are important, because starting, middle and end node are not fixed.
Here is the code for the Matrix example with added nodes.
match (s) where s.name in ["Oracle", "Neo", "Cypher"]
match (m) where m.name in ["Oracle", "Neo", "Cypher"] and s <> m
match (e) where e.name in ["Oracle", "Neo", "Cypher"] and s <> e and m <> e
match rel=(s)-[r1*1..]-(m)-[r2*1..]-(e)
return s, r1, m, r2, e, rel;
The additional where clause makes sure the same node is not used twice in one result row.
The relations are matched with one or more edges (*1..) or hops between the nodes s and m or m and e respectively and disregarding the directions.
Note that cypher 3 syntax is used here.