How can I do multiple Nodes update in neo4j cypher?
Now I'm trying to do like this:
MATCH (user151325288158:User{userId:151325288158}),
(user88245:User{userId:88245})
SET user151325288158.balance=2902833.4219789803
SET user88245.balance=146701.0299999991
RETURN user151325288158.balance,user88245.balance;
But here I have problem that if such user is absent in DB, nobody will be updated.
Other problem is performance, such queries are slow.
Is there some method to do such bulk updates?
Assuming you have pairs of userIds and new balance values in an array of maps/dicts, like this:
[
{
"userId": 151325288158,
"balance": 146701.09
},
{
"userId": 887436512344,
"balance": 22453.34
},
{
"userId": 873927654232,
"balance": 300002.22
}
]
You can pass this array as a parameter to a Cypher query to MATCH on the userId and update the balance property like this:
WITH {data} AS pairs
UNWIND pairs AS p
MATCH (u:User) WHERE u.userId = p.userId
SET u.balance = p.balance
By following William Lyon's answer, I was able to implement a batch update of multiple properties as follows:
UNWIND [{id: 123456, name: "John", age: 27}, {id: 789012, name: "Jane", age: 24}] AS updateUser
MATCH (user: User)
WHERE user.id = updateUser.id
SET user += updateUser
RETURN user
Related
I have three arrays of objects as shown below:
const org = [
{
id: "orgId1",
name: "first organization"
},
{
id: "orgId2",
name: "second organization"
}
]
const location = [
{
id: "loc1",
name: "Texas"
},
{
id: "loc2",
name: "New York"
}
]
const contacts = [
{
id: "contact1",
name: "James"
},
{
id: "contact2",
name: "John"
}
]
What is the optimal way to add relationships between them? Note that the arrays are of the same length.
I need a Cypher query that can loop through a range from 0 to orgs.length, and add corresponding relationships between each element at i. e.g org[i], contacts[i], location[i]
I tried the following, but it gives me an explosive combination where the first org maps to all the entries in location array and contact array, when I want is a one-to-one mapping.
UNWIND $orgs as orgs
UNWIND $locations as locs
UNWIND $contacts as contacts
FOREACH (i IN range(0, size(orgs) - 1)
| MERGE (:Organization { id: orgs[i].id })-[r:LOCATED_AT]->(:Location {id: locs[i].id})
| MERGE (:Organization { id: orgs[i].id })-[r:CONTACT_AT]->(:Contact {id: contacts[i].id})
)
Any help would be appreciated. Thanks in advance.
I don't think you need to UNWIND all arrays
WITH $orgs AS orgs,
$locs AS locs,
$contacts AS contacts
UNWIND $orgs as orgs
FOREACH (i IN range(0, size(orgs) - 1) |
MERGE (org:Organization { id: orgs[i].id })
MERGE (loc:Location {id: locs[i].id})
MERGE (contact:Contact {id: contacts[i].id})
MERGE (org)-[:LOCATED_AT]->(loc)
MERGE (org)-[:CONTACT_AT]->(contact)
)
should do it
A solution was posted in the official Neo4j site. I'm sharing here as well.
UNWIND range(0, size($orgs) - 1) as i
with i
MERGE (o:Organization { id: $orgs[i].id })-[r:LOCATED_AT]->(l:Location {id: $locs[i].id})
with o, i
MERGE (o)-[r2:CONTACT_AT]->(l2:Contact {id: $contacts[i].id})
Link to original answer
how can I create an relationship in NEO4J from one node to another node which has multiple vales.
The first node has unique values for the identifier. For example:
Data of the first NodeA:
{
"c": "11037",
"b": 15.4,
"a": 10.0,
"id": 11137100
}
The second NodeB look like this:
{
"text": "some text",
"prio": 1,
"id": 11137100,
"value": 0.1
}
But here we have data which has the same id like here:
{
"text": "some other text",
"prio": 2,
"id": 11137100,
"value": 2.1
}
Now want to create a relationship between both nodes. But if I do things like:
MATCH (p:NodeA),(h:NodeB)
WHERE h.id = p.id
CREATE (p)-[dr:Contains{prio:h.prio}]->(h)
RETURN (dr)
I get multiple relationships. I want one NodeA with two Outputs to NodeB.
How can I do it?
The CREATE statement will create a new node/relationship, irrespective if one already exists.
If the intent is to only create a relationship if one does not already exist, I would suggest you do a pre-filter query first, e.g.
MATCH (p:NodeA), (h:NodeB)
WHERE h.id = p.id AND NOT (p)-[:Contains{prio:h.prio}]->(h)
//continue your query here
I have a graph like this:
({id: 1, name: 'Winston'}) --[{name: 'Bill'}]--> ({id: 2, name: 'Max'})
({id: 3, name: 'Steve'})
I want to match nodes with id = 2 or 3 and replace property name in them by name from relationship with node which has id = 1:
{
"id": 2,
"name": "Bill"
}
{
"id": 3,
"name": "Steve"
}
How can i do this?
For one, you should be using labels on your nodes. For example's sake let's use :Person. As your graph gets larger you should also be adding indexes on the labels/properties used for node lookup.
For this case, only adding/replacing the name property of nodes based on a relationship property when connected to a starting node with id:1, we can do the following:
MATCH (:Person {id: 1})-[r]->(u:Person)
SET u.name = r.name
Sorry for the title. I couldn't find a better one. My question is hard for me to explain in words so I'll try to give an example.
Say I have a relation named "FOO" and "FOO" has two property named "id" and "bar".
And there is a graph like this:
(n1)-[:FOO {id: "1", bar:"8"}]->(n2)-[:FOO {id: "1", bar:"27"}]->(n3)-[:FOO {id: "1", bar:"50"}]->(n4)
(m1)-[:FOO {id: "4", bar:"2"}]->(m2)-[:FOO {id: "4", bar:"12"}]->(n2)-[:FOO {id: "4", bar:"25"}]->(m3)
(n1)-[:FOO {id: "7", bar:"11"}]->(m2)-[:FOO {id: "7", bar:"50"}]->(o3)-[:FOO {id: "7", bar:"14"}]->(o5)
And so on...
What I want to do is getting all ids (or nodes and relations related to those ids) for the relation series doesn't have any "bar" property equal to 50.
In above graph, the result I want is second one, id: "4":
(m1)-[:FOO {id: "4", bar:"2"}]->(m2)-[:FOO {id: "4", bar:"12"}]->(m3)-[:FOO {id: "4", bar:"25"}]->(m3)
Plus to this query how can I get the all ids (or nodes and relations related to those ids) for the relation series whose last relations bar property is not equal to 50. In this one, what I would like to get is first and third one, id: "4" and id: "7" :
(m1)-[:FOO {id: "4", bar:"2"}]->(m2)-[:FOO {id: "4", bar:"12"}]->(n2)-[:FOO {id: "4", bar:"25"}]->(m3)
(n1)-[:FOO {id: "7", bar:"11"}]->(m2)-[:FOO {id: "7", bar:"50"}]->(o3)-[:FOO {id: "7", bar:"14"}]->(o5)
UNWIND [1, 4, 7] AS rel_id
MATCH path = () - [:FOO*3 {id:rel_id}] -> ()
WHERE NOT ANY(x IN RELATIONSHIPS(path) WHERE x.bar = '50')
RETURN path
And for number 2:
UNWIND [1, 4, 7] AS rel_id
MATCH path = () - [:FOO*3 {id: rel_id}] -> ()
WHERE NOT LAST(RELATIONSHIPS(path))['bar'] = '50'
RETURN RELATIONSHIPS(path)[0]['id']
This is basically the minimal framework to answer your problem. If you need to narrow down the possible paths, you can prefix the queries with something else to alias the nodes or relationships that you want to build the paths on.
EDIT: To get all possible ID's instead of passing the list in manually, replace the first line with:
MATCH () - [r:FOO] - ()
WITH r.id AS r_id
WITH COLLECT(DISTINCT r_id) AS rel_ids
UNWIND rel_ids AS rel_id
I don't know this is the correct way to do it but I managed to get first results I wanted like this:
MATCH () - [r:FOO] - ()
WHERE r.bar = "50"
WITH COLLECT(DISTINCT r.id) as bar50_ids
MATCH () - [r:FOO] - ()
WITH COLLECT(DISTINCT r.id) as all_ids, bar50_ids
RETURN [x IN all_ids WHERE NOT(x IN bar50_ids)] AS barnot50_ids
(Getting the DISTINCT id's whose in all its relation at least one .bar = "50", and getting their difference from all DISTINC id's will give the all id's that has not any relation contains bar = "50")
I don't know how to get the second one I wanted.
After calling neo4j with neocons (cy/tquery conn node-query {:_nodeid _nodeid}), how do you perform accessing functions to get property values from the keys that are returned from the neo4j datastore response?
For example if this object was the response from the neo4j datastore, what neocons syntax do I use to access the value stored in key "attributes"?
[ {
"id": "letter-a",
"name": "Letter A",
"attributes": [ ... ]
}]
Currently I can only get so far as (first _response) but (get-in (first _response) [:attributes]) is giving me back nil
******************* EDIT *******************************
Here is the cypher query string I use as an argument to invoke the tquery function:
(def node-query "MATCH (n)-[attributelist:RELATIONSHIPTYPE]->(target)
RETURN n.id AS id,
n.name AS name,
COLLECT({
target : target.id
}) AS attributes;")
I don't understand what type of variable tquery returns? It looks like this object when the client displays it all the way in the browser:
[
{
"id": "node-999990a0a0a0sa0",
"name": "Node Name",
"attributes": [
{
"target": "node-id-one"
},
{
"target": "node-id-two"
},
{
"target": "node-id-two"
},
{
"target": "node-id-two"
},
{
"target": "node-id-three"
}
]
}
]
But I want to intercept what is returned from the tquery before the clojure server delivers it to the client and I want to manipulate the array value of the key "attributes" so I can run a reduce (tally) report before I deliver a rebuilt object response to the client.
{
...
"attributes" : {
"node-id-one" : 1,
"node-id-two" : 3,
"node-id-three" : 1
}
}
But I am having trouble because I do not know the syntax to access the "attributes" key from the object that is returned from the tquery
Sorry I don't understand your question. Which query did you run with tquery?
Usually you return the data you're interested in directly from the query.
e.g
MATCH (p:Person)-[:ACTED_IN]->(m)
WHERE p.name = "Tom Hanks"
RETURN m.title, m.released`
Otherwise you'd have to requery using a label+unique-property
MATCH (m:Movie)
WHERE m.title = "The Matrix"
RETURN m.title, m.released`
or node-id match.
MATCH (m:Movie)
WHERE id(m) = 123
RETURN m.title, m.released`
You usually would use parameters instead of literal values, i.e. {name}, {title}, {id}.
Update
I think for intercepting you would have to look into the neocons implementation.
Note: there is no clojure server, it's a Neo4j server with an http endpoint.
You should be able to do what you want (almost) in cypher.
MATCH (n)-[:RELATIONSHIPTYPE]->(target)
WITH n, target.id as target, count(*) as c
RETURN n.id as id, n.name as name, collect([target,c]) as targets;
Unfortunately right now there are no dynamic map-keys in Cypher, so a tuple-collection will have to do.
PS
You should use a label at least for your n (and optionally target) nodes.