0

I have a following part of Cypher query:

MATCH (ch:Characteristic) WHERE id(ch) = {characteristicId} WITH ch OPTIONAL MATCH (ch)<-[:SET_ON]-(v:Value)...

first of all I'm looking for (ch:Characteristic) by characteristicId and then applying required logic for this variable at the rest of my query.

my Characteristic can also have(or not) a child Characteristic nodes, like:

(ch:Characteristic)-[:CONTAINS]->(childCh)

Please help to extend my query in order to collect ch and childCh into a list of Characteristic thus I'll be able at the rest of my query to apply required logic to all Characteristic at this list.

UPDATED - possible solution #2

This is my current working query:

MATCH (chparent:Characteristic) 
WHERE id(chparent) = {characteristicId} 
OPTIONAL MATCH (chparent)-[:CONTAINS*]->(chchild:Characteristic) 
WITH chparent, collect(distinct(chchild)) as childs 
WITH childs + chparent as nodes 
UNWIND nodes as ch 
OPTIONAL MATCH (ch)<-[:SET_ON]-(v:Value)-[:SET_FOR]->(Decision) 
OPTIONAL MATCH (v)-[:CONTAINS]->(vE) OPTIONAL MATCH (vE)-[:CONTAINS]->(vEE) 
OPTIONAL MATCH (ch)-[:CONTAINS]->(cho:CharacteristicOption) 
OPTIONAL MATCH (cho)-[:CONTAINS]->(choE) OPTIONAL MATCH (ch)-[:CONTAINS]->(chE) 
DETACH DELETE choE, cho, ch, vEE, vE, v, chE

This is an attempt to simplify the query above:

MATCH (ch:Characteristic) 
WHERE (:Characteristic {id: {characteristicId}})-[:CONTAINS*]->(ch) 
OPTIONAL MATCH (ch)<-[:SET_ON]-(v:Value)-[:SET_FOR]->(Decision) 
OPTIONAL MATCH (v)-[:CONTAINS]->(vE) 
OPTIONAL MATCH (vE)-[:CONTAINS]->(vEE) 
OPTIONAL MATCH (ch)-[:CONTAINS]->(cho:CharacteristicOption) 
OPTIONAL MATCH (cho)-[:CONTAINS]->(choE) 
OPTIONAL MATCH (ch)-[:CONTAINS]->(chE) 
DETACH DELETE choE, cho, ch, vEE, vE, v, chE

but this query doesn't delete required Characteristic nodes and my tests fail. What am I doing wrong at the last query ?

alexanoid
  • 24,051
  • 54
  • 210
  • 410
  • 1
    try WITH chparent + childs as ch UNWIND ch as nodes OPTIONAL MATCH (nodes)<-[:SET_ON].... Basically you get a list of nodes with `collect` and now you have to `UNWIND` the list, which is like a `.map` in JS, or like forEach, where you do a couple of optimal matches for every node in the list(collection). – Tomaž Bratanič May 21 '17 at 11:33
  • Thank you ! Now it works as expected. – alexanoid May 21 '17 at 12:44
  • In your attempt to simplify the query, you're trying to match on the `id` property, but this is not the same as the internal neo4j id in the working query. You have to use the `id()` function to get or compare the internal neo4j ids. – InverseFalcon May 22 '17 at 07:45
  • Thanks, this way we are returning to the query like this one `MATCH (chparent)-[:CONTAINS*0..]->(ch:Characteristic) WHERE id(chparent) = {characteristicId}` and it fails with an error that I have showed as a comment to your answer - `Caused by: org.neo4j.kernel.api.exceptions.EntityNotFoundException: Unable to load NODE with id 827.` :( – alexanoid May 22 '17 at 07:55

4 Answers4

2

You can try something like this with apoc:

MATCH (chparent:Characteristic {characteristicId: <someid>})
OPTIONAL MATCH (chparent)-[:CONTAINS]->(chchild:Characteristic)
WITH apoc.coll.union(chparent,chchild) as distinctList
...

With pure cypher you can try something like this:

MATCH (chparent:Characteristic {characteristicId: <someid>})
OPTIONAL MATCH (chparent)-[:CONTAINS]->(chchild:Characteristic)
WITH chparent,collect(distinct(chchild)) as childs
WITH chparent + childs as list
....

Not really sure if you need distinct in collect, but I added just so you know you can do this to filter out duplicates.

Tomaž Bratanič
  • 6,319
  • 2
  • 18
  • 31
  • Thanks. I never used apoc function before. I tried to execute the query on my Neo4j Embedded 3.1.3 and it failed with a following error: `Unknown function 'apoc.coll.union'` – alexanoid May 21 '17 at 11:02
  • well it does not come with Neo4j, you have to manually "install" it... there is a way to do this with cypher only, let me find it and update answer – Tomaž Bratanič May 21 '17 at 11:10
  • I have also tried to add ` org.neo4j.procedure apoc 3.1.3.7 test ` into my Maven pom.xml but it still doesn't work – alexanoid May 21 '17 at 11:12
  • check out this http://stackoverflow.com/questions/43965481/how-to-configure-neo4j-embedded-to-run-apoc-procedures – Tomaž Bratanič May 21 '17 at 11:14
1

You can actually do this easily by using a variable-length relationship match of 0..1, as it will let you match on your root :Characteristic node and any of its children.

MATCH (chparent:Characteristic)-[:CONTAINS*0..1]->(ch:Characteristic)
WHERE id(chparent) = {characteristicId} 
// ch contains both the parent and children, no need for a list
...
InverseFalcon
  • 29,576
  • 4
  • 38
  • 51
  • Thanks. Will it return at least chparent node in case of no any children(ch)? – alexanoid May 21 '17 at 16:18
  • Yes, since if the `chparent` node exists, it will at least be able to match to itself. – InverseFalcon May 21 '17 at 16:40
  • Sorry for one more silly question but will this query return any descendant nodes of any depth or only direct children of chparent ? – alexanoid May 21 '17 at 16:45
  • I have changed the query to `MATCH (chparent)-[:CONTAINS*0..1]->(ch:Characteristic) WHERE id(chparent) = {characteristicId} OPTIONAL MATCH (ch)<-[:SET_ON]-(v:Value)...` and it fails right now with `Caused by: org.neo4j.cypher.EntityNotFoundException: Node with id 5203 has been deleted in this transaction` – alexanoid May 21 '17 at 16:53
  • That's a new one. You don't have any `DETACH DELETEs` in your query before that? What version of Neo4j are you using, and are you querying through the browser or from some other system? – InverseFalcon May 21 '17 at 17:13
  • And as far as depth, `*0..1` is only the node itself or those immediately connected. You can specify a different upper bound if you want this at a deeper depth, or use `*0..` for no upper bound. – InverseFalcon May 21 '17 at 17:15
  • Thanks. I have only DETACH DELETE at the end of my query and I'm using Neo4j 3.1.3. Right now I'm testing it through SDN 4/OGM Neo4j Embedded from my tests. – alexanoid May 21 '17 at 17:21
  • I have updated my question with more details - current working query vs attempt to simplify it. What am I doing wrong ? – alexanoid May 22 '17 at 07:38
1

A more simplified query.

MATCH (c:Characteristics) WHERE (:Characteristics {id: 123})-[:CONTAINS*0..1]->(c) return c;

Matches all Characteristics including the root node that (optionally) have incoming relationships of type CONTAINS from the node specified with id 123.

bjornbergq
  • 31
  • 3
  • Thanks. I need to delete all Characteristics from parent to unlimited depth.. so I have adapted your approach: `MATCH (ch:Characteristic) WHERE (:Characteristic {id: {characteristicId}})-[:CONTAINS*]->(ch) OPTIONAL MATCH (ch)<-[:SET_ON]-(v:Value)...` but right now it doesn't delete any `Characteristic`.. my test fails. – alexanoid May 22 '17 at 06:57
  • I realized that the id you are referring to is the neo4j node id, right? You have to use the id-function to match on that id. `MATCH (rn), (ch:Characteristic) WHERE id(rn) = {characteristicId} AND (rn)-[:CONTAINS*]->(ch) OPTIONAL MATCH (ch)<-[:SET_ON]-(v:Value)-[:SET_FOR]->(Decision) OPTIONAL MATCH (v)-[:CONTAINS]->(vE) OPTIONAL MATCH (vE)-[:CONTAINS]->(vEE) OPTIONAL MATCH (ch)-[:CONTAINS]->(cho:CharacteristicOption) OPTIONAL MATCH (cho)-[:CONTAINS]->(choE) OPTIONAL MATCH (ch)-[:CONTAINS]->(chE) DETACH DELETE choE, cho, ch, vEE, vE, v, chE` – bjornbergq May 22 '17 at 19:17
  • Also, if you are using the neo4j internal id, make sure to change that and never use the internal id in your application. :) – bjornbergq May 22 '17 at 19:18
  • Thanks. Your query works with no errors but my test fails because it delete all Characteristics except 1.. I think except the Parent Characteristic.. but I need to process and delete it also – alexanoid May 22 '17 at 19:28
  • Also, there is a property at Neo4j that prevents Neo4j from internal ID reusing.. so looks like it is safe to use these ids with this property – alexanoid May 22 '17 at 19:29
  • this one https://neo4j.com/docs/operations-manual/current/reference/configuration-settings/#config_dbms.ids.reuse.types.override – alexanoid May 22 '17 at 19:39
  • 1
    Perhaps change [:CONTAINS*] to [:CONTAINS*0..1] to include the root node. – bjornbergq May 23 '17 at 12:38
  • Thanks ! looks like this one is working [:CONTAINS*0..] for unlimited depth – alexanoid May 23 '17 at 13:46
0

I assume all the children of Characteristic will also have the label Characteristic. Another assumption I made is, you need characteristicId which is defined by you, not the internal id defined by neo4J. id(ch) fetching the internal id instead of user defined ID. You might want to pass the characteristicId variable like I gave here.

MATCH (chparent:Characteristic {characteristicId: <someid>})
WITH chparent
OPTIONAL MATCH (chparent)-[:CONTAINS]->(chchild:Characteristic)
WITH chchild
<your operation>
hhsecond
  • 62
  • 7
  • Thanks for your answer. How to combine `chparent` and `chchild` into a single variable(list) in order to not work separately with two different variables? – alexanoid May 21 '17 at 08:52
  • check out `apoc.coll.union` for distinct list and `apoc.coll.unionAll`in the docs. https://neo4j-contrib.github.io/neo4j-apoc-procedures/#_collection_functions – Tomaž Bratanič May 21 '17 at 10:54