Neo4j, empty list in first unwind stop second unwind from executing - neo4j

I have two unwinds, that create some relationships and nodes, but if the list for the first unwinds is empty, the second unwind doesn't execute.
How can I fix that?
CALL apoc.periodic.iterate(
"
UNWIND $POSTS as post
RETURN post
",
"
MERGE (p:Post{id: post.id})
WITH p, post
UNWIND post.tags as value
MERGE (t:Tag{tag: value})
MERGE (t)-[:has_tag]->(p)
WITH p, post
UNWIND post.user_mentions as user_mention
MERGE (u1:User{id: user_mention})
MERGE (p)-[:mentions]->(u1)
",
{batchSize: 500, params: {POSTS: $POSTS}, iterateList:true}
)
Example results
Parameters, with non-empty tags
[
{
"id": 123,
"tags": [1],
"user_mentions": [123, 234],
}
]
Graph created in database - Expected result
Parameters, with empty tags
[
{
"id": 123,
"tags": [],
"user_mentions": [123, 234],
}
]
Graph created in the database (Lacking 'mentions' relationships) - Unexpected result

This is expected, UNWIND produces rows and thus if the list is empty no rows are produced and the query doesn't continue.
You need to switch to FOREACH for this use case :
CALL apoc.periodic.iterate(
"
UNWIND $POSTS as post
RETURN post
",
"
MERGE (p:Post{id: post.id})
WITH p, post
FOREACH(value IN post.tags |
MERGE (t:Tag{tag: value})
MERGE (t)-[:has_tag]->(p)
)
WITH p, post
FOREACH(user_mention IN post.user_mentions |
MERGE (u1:User{id: user_mention})
MERGE (p)-[:mentions]->(u1)
)
",
{batchSize: 500, params: {POSTS: $POSTS}, iterateList:true}
)

Related

How to read relations from Json file while using apoc.load.json() in Neo4j?

I want to read relations from my Json file instead of hard coding them.
for example:
instead of
MERGE (arg1)-[:relation]->(arg2)
I want to have something like:
MERGE (arg1)-[:v.relation]->(arg2).
My Json file is as following:
{
"bacf06771e0f4fc5a8e68c30fc77c9c4": {
"arg1": "the Treasury",
"arg2": "details of the November refunding",
"relation": "will announce",
"id": "bacf06771e0f4fc5a8e68c30fc77c9c4",
"linkedContexts": [
{
"targetID": "948eeebd73564adab7dee5c6f177b3b9",
"classification": "CONTRAST"
}
]
},
"948eeebd73564adab7dee5c6f177b3b9": {
"arg1": "the funding",
"arg2": "",
"relation": "will be delayed",
"id": "948eeebd73564adab7dee5c6f177b3b9",
"linkedContexts": [
{
"targetID": "006a71e51295440fab7a8e8c697d2ba6",
"classification": "CONDITION"
}
]
}
}
I tried:
CALL apoc.load.json("files:///example.json") YIELD value
UNWIND [k IN KEYS(value) | value[k]] AS v
MERGE (arg1:Arg1 {subject:v.arg1})
MERGE (arg2:Arg2 {object:v.arg2})
MERGE (arg1)-[:v.relation]->(arg2)
I got this error:
Neo.ClientError.Statement.SyntaxError: Invalid input '.': expected an identifier character, whitespace, '|', a length specification, a property map or ']' (line 13, column 17 (offset: 444))
"merge (arg1)-[:v.relation]->(arg2) "
^
Currently, it's not possible to create relationships dynamically with Cypher.
You can use apoc's apoc.merge.relationship procedure to create nodes/relationships dynamically.
CALL apoc.load.json("files:///example.json") YIELD value
UNWIND [k IN KEYS(value) | value[k]] AS v
MERGE (arg1:Arg1 {subject:v.arg1})
MERGE (arg2:Arg2 {object:v.arg2})
CALL apoc.merge.relationship(arg1,v.relation,{},{},arg2) YIELD rel
RETURN count(*);

UNWIND statement in Cypher Neo4J does not get executed if the previous one did not have any results?

This request below usually works when I have a match on c_from and c_to:
MATCH (u:User {uid: $userId})
WITH u UNWIND $statements as statement
WITH u, statement
UNWIND statement.conceptsRelations as conceptsRelation
MATCH (c_from:Concept{name: conceptsRelation.from})
MATCH (c_to:Concept{name: conceptsRelation.to})
CREATE (c_from)-[:TO {context:conceptsRelation.context,statement:conceptsRelation.statement,user:u.uid,timestamp:conceptsRelation.timestamp, uid:apoc.create.uuid(), gapscan:conceptsRelation.gapscan, weight: conceptsRelation.weight}]->(c_to)
WITH u, statement
UNWIND statement.mentionsRelations as mentionsRelation
MATCH (m_from:Concept{name: mentionsRelation.from})
MATCH (m_to:Concept{name: mentionsRelation.to}) return m_from, m_to
However, as soon as I don't have any match the last part of the query (the last UNWIND) does not get executed. I checked it by removing the 2nd UNWIND and then the 3rd one works again. As in:
MATCH (u:User {uid: $userId})
WITH u UNWIND $statements as statement
WITH u, statement
UNWIND statement.mentionsRelations as mentionsRelation
MATCH (m_from:Concept{name: mentionsRelation.from})
MATCH (m_to:Concept{name: mentionsRelation.to}) return m_from, m_to
Just in case, my params are:
{
"userId": "15229100-b20e-11e3-80d3-6150cb20a1b9",
"contextNames": [
{
"uid": "af8debb0-1f71-11e9-a572-691cc47b060f",
"name": "dsfasdf"
}
],
"statements": [
{
"text": "#submit desire",
"concepts": [
"desire"
],
"mentions": [
"#submit"
],
"timestamp": 15482915128250000,
"name": "#desire ##submit ",
"uid": "2bd1f170-1f73-11e9-a508-0d8a16ad5cf6",
"uniqueconcepts": [
"desire"
],
"conceptsRelations": [],
"mentionsRelations": [
{
"from": "desire",
"to": "#submit",
"context": "af8debb0-1f71-11e9-a572-691cc47b060f",
"statement": "2bd1f170-1f73-11e9-a508-0d8a16ad5cf6",
"user": "15229100-b20e-11e3-80d3-6150cb20a1b9",
"timestamp": 15482915128250000,
"uid": "apoc.create.uuid()",
"gapscan": "1",
"weight": 3
}
],
"uniquementions": [
"#submit"
]
}
],
"timestamp": 15482915128250000
}
This seems like a strange illogical behavior. Any idea why it arises? Thanks!
The use of UNWIND is to turn a list into rows
If you have an empty list you get zero rows.
You could add an element to the list if it's empty:
UNWIND case when size(list) = 0 then [null] else list end
or
UNWIND list + null
And then skip the special value in your 2nd part.

Cypher: using OPTIONAL MATCH in combination with collect returns default item when there is none

I'm using Neo4J to retrieve a person and their skills. This is my Cypher query:
MATCH (p:Person {id: "1"})
OPTIONAL MATCH (p) -[exp:HAS_EXPERIENCE]->(s:Skill)
WITH collect(distinct {id: s.id, name: s.name}) as skills, p
RETURN p.id as id, skills
This is the result:
{
"id": "1",
"skills": [
{
"name": null,
"id": null,
}
]
}
As you can see, the list of skills contains a 'default' item. However, in this particular case the person has no skills.
Why does the result contain an array item? How to I adjust the query so that an empty array is returned?
Using Neo4J 3.1.1.
This should work:
MATCH (p:Person {id: "1"})
OPTIONAL MATCH (p)-[exp:HAS_EXPERIENCE]->(s:Skill)
RETURN p.id AS id,
CASE WHEN s IS NULL THEN [] ELSE COLLECT(distinct {id: s.id, name: s.name}) END as skills;
s would only be NULL if the OPTIONAL MATCH does not match anything.
You may wish to consider the following:
MATCH (p:Person {id: "1"})
OPTIONAL MATCH (p) -[exp:HAS_EXPERIENCE]->(s:Skill)
RETURN p {id: p.id, skills: COLLECT(distinct s {.*})}
Assuming that you wish to get all fields from the Skill. Otherwise
... collect(distinct s {id:s.id, name:s.name})

create relationships in neo4j cypher using case statement

I have a JSON in the next form:
{ "conditions": [ { "id": "123", "type": "a", entities: ["529", "454"] },
{ "id": "124", "type": "b", entities: ["530", "455"] }
]
}
I want to create relation ship between the Condition node with node A Entities or node B entities based on type attribute which can be A/B i Assuming that these entities already exists in neo4j.
I am trying something like the below cypher but that doesn't work.
WITH {json} as data
UNWIND data.conditions as condition
MATCH (c:Condition { conditionId: condition.id})
CASE c.type
WHEN 'a' THEN FOREACH (sid IN condition.entities |
MERGE (s:NodeA {nr_serverId:sid}) MERGE (s)-[:ATTACHED_TO]->(c)
)
WHEN 'b' THEN FOREACH (aid IN condition.entities |
MERGE (a:NodeB {nr_appId: aid}) MERGE (a)-[:ATTACHED_TO]->(c)
)
END;
Can anyone please help me with the correct way of doing this? Thank you.
Since at the moment there is no classical conditional statements in cypher, you can use the famous trick with foreach and case:
WITH {json} as data
UNWIND data.conditions as condition
MATCH (c:Condition { conditionId: condition.id})
FOREACH (ift in CASE WHEN c.type = 'a' THEN [1] ELSE [] END |
FOREACH (sid IN condition.entities |
MERGE (s:NodeA {nr_serverId:sid}) MERGE (s)-[:ATTACHED_TO]->(c)
)
)
FOREACH (ift in CASE WHEN c.type = 'b' THEN [1] ELSE [] END |
FOREACH (aid IN condition.entities |
MERGE (a:NodeB {nr_appId: aid}) MERGE (a)-[:ATTACHED_TO]->(c)
)
)
APOC Procedures just updated with support for conditional cypher execution. You'll need version 3.1.3.7 or greater (if using Neo4j 3.1.x), or version 3.2.0.3 or greater (if using Neo4j 3.2.x).
Here's an example of using these new procedures to execute your query:
WITH {json} as data
UNWIND data.conditions as condition
MATCH (c:Condition { conditionId: condition.id})
CALL apoc.do.case([
c.type = 'a',
"FOREACH (sid IN condition.entities |
MERGE (s:NodeA {nr_serverId:sid}) MERGE (s)-[:ATTACHED_TO]->(c))",
c.type = 'b',
"FOREACH (aid IN condition.entities |
MERGE (a:NodeB {nr_appId: aid}) MERGE (a)-[:ATTACHED_TO]->(c))"
], '', {condition:condition, c:c}) YIELD value

Consolidating & Collecting into one Record

I'm trying to write a cypher query where all related nodes are collected and returned under a key in a map:
{
name: 'chart',
CONTAINTED: [
{
name: 'id'
},
{
name: 'bloodpressure'
},
...
],
FOREIGNKEY: [
{
name: 'clientid'
}
]
}
I've attempted to do this with the following cypher query but it isn't quite right. Using the method below two records are returned rather than just one.
MATCH path=(table:tabPHI {name: 'client'})-[r]-(c)
WITH table as tab, type(r) as rel, collect(c) as col
CALL apoc.map.setKey(tab, rel, col) YIELD value as foo
return foo
MATCH (table:tabPHI {name: 'client'})-[r]-(c)
WITH table as tab, type(r) as rel, collect(c) as col
WITH tab, COLLECT(rel) AS keys, COLLECT(col) AS values
WITH keys(tab) as main_keys, [key IN keys(tab)|tab[key]] AS main_values, keys, values
CALL apoc.map.fromLists(main_keys + keys, main_values + values) YIELD value as foo
return foo
You're creating a single map for each relationship type in your query. You have to COLLECT(rel) at some point to get a single result for each tab.

Resources