I've used first CALL after LOAD CSV for splitting transactions and second for creating relations between two types of nodes - Legal_Entity and Natural_Person. SYSTEM node is my analog for dicts contains associations between numbers - numeric interpretations of relations - and text of these relations.
In relation to is relation type for Legal_Entity or for Natural_Person I need to connect others nodes (a.e., if code is 100 it means LEGAL-LEGAL connection with text "has a division". If code is 110 it means LEGAL-PHYSICAL connection with text "founded by").
Now I need to make a request that would determine which type of node the connection is being built to and build it accordingly.
If it's needed I can add more clarify info for used data.
:auto LOAD CSV WITH HEADERS FROM 'file:///CSVs/связи_фикс_все.csv' as row
call {
with row
match (legal:Legal_Entity {hid_party: row['first_related_hid']})
match (sys:SYSTEM)
with sys, legal,
row,
sys['type_' + tostring(row['id_relation_type'])] as relation_data,
sys[row['second_related_type']] as rel_type
CALL {
WITH rel_type, relation_data, row, legal
WITH rel_type, relation_data, row, legal
WHERE rel_type = 'Legal_Entity'
match (legal_2:Legal_Entity {hid_party : row['second_related_hid']})
CALL apoc.create.relationship(legal, relation_data[1], NULL, legal_2) YIELD rel1
CALL apoc.create.relationship(legal_2, relation_data[3], NULL, legal) YIELD rel2
return rel_type as rel_type_2
UNION
WITH rel_type, relation_data, row, legal
WITH rel_type, relation_data, row, legal
WHERE rel_type = 'Natural_Person'
match (natural:Legal_Entity {hid_party : row['second_related_hid']})
CALL apoc.create.relationship(legal, relation_data[1], NULL, natural) YIELD rel1
CALL apoc.create.relationship(natural, relation_data[3], NULL, legal) YIELD rel2
return rel_type as rel_type_2
} return rel_type_2
} IN TRANSACTIONS of 10 rows
The subqueries seem to be behaving weird when you have them nested. You can simply use PERIODIC COMMIT if you want to import data in batches and get rid of the top-level subquery.
:auto USING PERIODIC COMMIT 10
LOAD CSV WITH HEADERS FROM 'file:///CSVs/связи_фикс_все.csv' as row
with row
MERGE (legal:Legal_Entity {hid_party: row['first_related_hid']})
MERGE (sys:SYSTEM)
with sys, legal,
row,
sys['type_' + tostring(row['id_relation_type'])] as relation_data,
sys[row['second_related_type']] as rel_type
CALL {
WITH rel_type, relation_data, row, legal
WITH rel_type, relation_data, row, legal
WHERE rel_type = 'Legal_Entity'
match (legal_2:Legal_Entity {hid_party : row['second_related_hid']})
CALL apoc.create.relationship(legal, relation_data[1], NULL, legal_2) YIELD rel AS rel1
CALL apoc.create.relationship(legal_2, relation_data[3], NULL, legal) YIELD rel AS rel2
return rel_type as rel_type_2
UNION
WITH rel_type, relation_data, row, legal
WITH rel_type, relation_data, row, legal
WHERE rel_type = 'Natural_Person'
match (natural:Legal_Entity {hid_party : row['second_related_hid']})
CALL apoc.create.relationship(legal, relation_data[1], NULL, natural) YIELD rel AS rel1
CALL apoc.create.relationship(natural, relation_data[3], NULL, legal) YIELD rel AS rel2
return rel_type as rel_type_2
}
RETURN distinct 'done'
Related
I'm trying to implement follow/unfollow in Neo4J. I would like to write a query would toggle the relationship between two nodes.
I currently have the following query:
neoSession.writeTransaction(tx => tx.run('MATCH (me:User), (other:User) WHERE ID(me) = $me AND ID(other) = $other OPTIONAL MATCH (me)-[af:FOLLOWS]->(other) CALL apoc.do.when(af IS NULL, CREATE (me)-[f:FOLLOWS]->(other), DELETE af)', { me: req.user_id, other: req.body.user, datetime: Date.now() }));
Prettified query-only:
MATCH (me:User), (other:User)
WHERE ID(me) = $me AND ID(other) = $other
OPTIONAL MATCH (me)-[af:FOLLOWS]->(other)
CALL
apoc.do.when(
af IS NULL,
CREATE (me)-[f:FOLLOWS]->(other),
DELETE af
)
But this results in the error
Neo4jError: Invalid input '>' (line 1, column 169 (offset: 168))
"MATCH (me:User), (other:User) WHERE ID(me) = $me AND ID(other) = $other OPTIONAL MATCH (me)-[af:FOLLOWS]->(other) CALL apoc.do.when(af IS NULL, CREATE (me)-[f:FOLLOWS]->(other), DELETE af)"
The queries (last two arguments) to apoc.do.when() have to be strings, so quote each of them.
Also, in order for each of those queries to use those variables, you need to pass those variables in a parameter map as a 4th argument.
Each of the conditional queries must RETURN something, otherwise there will be no rows yielded and anything after would be a no-op.
The call must YIELD value, so that needs to be present, and last, a query cannot end with a procedure call, so you need to RETURN something.
This one should work, you can adjust it as needed:
MATCH (me:User), (other:User)
WHERE ID(me) = $me AND ID(other) = $other
OPTIONAL MATCH (me)-[af:FOLLOWS]->(other)
CALL
apoc.do.when(
af IS NULL,
"CREATE (me)-[f:FOLLOWS]->(other) RETURN f",
"DELETE af RETURN null as f",
{me:me, af:af}
) YIELD value
RETURN value.f as f
I have the following params in my Neo4J:
{
"lists": [
{
"from": "someone",
"to": "somebody"
}
]
}
And the following query:
MATCH (c:Concept{name:'infranodus'}) WITH c, $lists AS list
UNWIND CASE WHEN list = [{}] THEN [null] ELSE list END AS l
WITH l
MATCH (cp1:Concept{name:l.from})
WITH cp1
MATCH (cp2:Concept{name:'somebody'})
RETURN cp1,cp2;
The query above will work.
However, if I replace l.from with a non-existent parameter, e.g. l.about, then — as the match doesn't happen — the second cp2 match doesn't fire.
How can I change this behavior and continue executing this query even if cp1 is not found? Maybe there's a way to pass on a dummy variable as a result?
MATCH (c:Concept{name:'infranodus'}) WITH c, $lists AS list
UNWIND CASE WHEN list = [{}] THEN [null] ELSE list END AS l
WITH l
MATCH (cp1:Concept{name:l.about})
WITH cp1
MATCH (cp2:Concept{name:'somebody'})
RETURN cp1,cp2;
Use OPTIONAL MATCH. If there is no match is found, then it will use NULL for the missing part of the pattern. It is similar to outer join in SQL.
NEW:
OPTIONAL MATCH (cp1:Concept{name:l.about})
OLD:
MATCH (cp1:Concept{name:l.about})
You can maybe replace it with an IN predicate ?
For eg :
WITH {from: 'Matt Olg', about: 'Matthew Olg'}
AS l
MATCH (n:Person)
WHERE n.name IN [l.from, l.to]
RETURN n.name
╒══════════╕
│"n.name" │
╞══════════╡
│"Matt Olg"│
└──────────┘
Cannot find answer online for this. I want to do a recursive query upstream and downstream on protein interaction map. If user enters a protein (protein 'C') and depth N=2, I want to return 2 upstream and 2 downstream proteins in the interaction map and the regulation. However if its upstream then protein 'b' on right side of MATCH needs to come first in the return table and if its downstream direction then protein 'a' on left side of match needs to come first in return table. How can I do this?
For instance this is the bidirection but half of the rows are the wrong order in columns 1 and 3.
MATCH p = (a:Protein { name:'C' })<-[:REGULATES*1..2]->(b:Protein)
WITH *, relationships(p) as r
RETURN nodes(p)[length(p)-1].name AS Protein1, r[length(p)-1] as Regulates, b.name AS Protein2
I can only get what I want with two calls and switching order or RETURN columns.
MATCH p = (a:Protein { name:'C' })-[:REGULATES*1..2]->(b:Protein)
WITH *, relationships(p) as r
RETURN nodes(p)[length(p)-1].name AS Protein1, r[length(p)-1] as Regulates, length(p), b.name AS Protein2
MATCH p = (a:Protein { name:'C' })<-[:REGULATES*1..2]-(b:Protein)
WITH *, relationships(p) as r
RETURN b.name AS Protein1, r[length(p)-1] as Regulates, nodes(p)[length(p)-1].name AS Protein2
Figured it out using functions startNode and endNode. The last() and head() functions are also handy.
MATCH p = (n:Protein { name:'C' })<-[:REGULATES*1..3]->(b:Protein)
WITH *, relationships(p) as rs
RETURN startNode(last(rs)).name as Protein1, last(rs).direction as Regulates, endNode(last(rs)).name as Protein2, length(p)
I am running a random walk algorithm to produce a temporal event chain. Each entry in the resulting collection is a node id. An example is:
[43116, 43116, 43116, 43116, 43116, 43116]
[61412, 61416, 4948, 61417, 61419, 61420]
...
For each row that's returned from the stream, how can I unwind the values, perform a algo.getNodeById(node_id) on that element, and pack those results back into a collection?
Cypher query is as follows:
CALL algo.randomWalk.stream(null, 5, 20, {
nodeQuery: "MATCH (a:Asset) RETURN id(a) as id",
relationshipQuery: "MATCH (a1:Asset)<-[:PATIENT]-(e1:EVENT)-[:NEXT]->(e2:EVENT)-[:PATIENT]-(a2:Asset) RETURN id(a1) as source, id(a2) as target",
graph: "cypher" })
YIELD nodeIds
(This second answer from me is based on the clarification in the comments to my first answer.)
You can instead do this (using algo.getNodesById(), which takes a list of node IDs):
CALL algo.randomWalk.stream(null, 5, 20, {
nodeQuery: "MATCH (a:Asset) RETURN id(a) as id",
relationshipQuery: "MATCH (a1:Asset)<-[:PATIENT]-(e1:EVENT)-[:NEXT]->(e2:EVENT)-[:PATIENT]-(a2:Asset) RETURN id(a1) as source, id(a2) as target",
graph: "cypher" })
YIELD nodeIds
UNWIND nodeIds AS nodeId
WITH DISTINCT nodeId
RETURN algo.getNodesById(COLLECT(nodeId)) AS nodes;
Or, you can do the same thing without algo.getNodesById():
CALL algo.randomWalk.stream(null, 5, 20, {
nodeQuery: "MATCH (a:Asset) RETURN id(a) as id",
relationshipQuery: "MATCH (a1:Asset)<-[:PATIENT]-(e1:EVENT)-[:NEXT]->(e2:EVENT)-[:PATIENT]-(a2:Asset) RETURN id(a1) as source, id(a2) as target",
graph: "cypher" })
YIELD nodeIds
UNWIND nodeIds AS nodeId
WITH DISTINCT nodeId
MATCH (n) WHERE ID(n) = nodeId
RETURN COLLECT(n) AS nodes;
In both queries, the WITH DISTINCT nodeId clause is needed to remove duplicates.
Cypher does not delete a collection after UNWIND is executed on it. So, there is no need to recreate the collection.
For example:
WITH [61412, 61416, 4948, 61417, 61419, 61420] AS data
UNWIND data AS d
RETURN data, d;
returns this:
╒════════════════════════════════════╤═════╕
│"data" │"d" │
╞════════════════════════════════════╪═════╡
│[61412,61416,4948,61417,61419,61420]│61412│
├────────────────────────────────────┼─────┤
│[61412,61416,4948,61417,61419,61420]│61416│
├────────────────────────────────────┼─────┤
│[61412,61416,4948,61417,61419,61420]│4948 │
├────────────────────────────────────┼─────┤
│[61412,61416,4948,61417,61419,61420]│61417│
├────────────────────────────────────┼─────┤
│[61412,61416,4948,61417,61419,61420]│61419│
├────────────────────────────────────┼─────┤
│[61412,61416,4948,61417,61419,61420]│61420│
└────────────────────────────────────┴─────┘
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.