Neo4j: Cypher query returns wrong json result - neo4j

I have a problem with my cypher query.
Situation explained:
A user is able to connect to other CONTACT nodes, but he can also connect to EVENT nodes. Other users can also connect to these event nodes. We expect to retrieve the nodes we are connected to (CONTACT & EVENT) but we also need to retrieve the event nodes of the CONTACT nodes that we are connected to.
This is the graph we want to see when we retrieve the connected nodes from the bottom center CONTACT node:
But we receive this json output:
{
"_type": "Node",
"_id": 1,
"nodeType": "EVENT",
"nodeId": 1,
"connected_with": [
{
"_type": "Node",
"_id": 0,
"nodeType": "CONTACT",
"nodeId": 1
},
{
"_type": "Node",
"_id": 2,
"nodeType": "CONTACT",
"nodeId": 2,
"connected_with": [
{
"_type": "Node",
"_id": 0,
"nodeType": "CONTACT",
"nodeId": 1
}
]
}
]
}
We want to go 2 levels deep, meaning we want to see
contacts that we are connected to but also contacts we
"met" at an event hence the reason we want to go 2 levels deep.
We currently have this cypher query running but as previously mentioned, it's not working.
MATCH path = (n:Node {nodeId: 1})<-[:CONNECTED_WITH*]-(nodes)
WITH collect(path) as paths
CALL apoc.convert.toTree(paths) yield value as json
RETURN json
Any help would be appreciated!

Your results seem to match what you say you want, except that it is in tree form (which you asked for).
You state that you do not "see" what you expected (presumably in the neo4j Browser). This is because the results you asked for are not plain nodes, relationships, and/or paths.
Try this, instead (note also the upper bound of 2 on the depth of the variable-length path pattern):
MATCH path = (n:Node {nodeId: 1})<-[:CONNECTED_WITH*..2]-(nodes)
RETURN path
Aside: Having just a single node label, Node, with a nodeType property that specifies the exact "type" of node is not generally the right way to model things. It makes it harder to understand the DB, tends to complicate your code, and makes it harder to take advantage of indexing. You probably want to have separate labels (say, Person and Event). You may also want to have different relationship types as well.

Related

Cypher: how do I check that at least one of the nodes in a set matches a given property?

I have a data model in neo4j where a Person node may be "merged" with another — not literally merged, just a relation in the form:
(a:Person)-[:MERGED]-(other:Person)
And, of course, b can be merged with someone else, in a potentially endless path.
I have a query to return a list of persons, with the 'merged' persons — that is, anyone in the :MERGED path — embedded as a property.
MATCH (a:Person)
CALL {
WITH a
MATCH path = (a)-[:MERGED*]-(other)
RETURN COLLECT(other{.label}) as b
}
RETURN a{.label, merged_items:b}
This returns, for example, something like:
{
"label": "John Smith",
"merged_items": [
{
"label": "Toby Jones"
},
{
"label": "Seamus McGibbon"
},
{
"label": "Aaron Drew"
}
]
}
for each of the Persons in this chain of merges (so actually the full result has four items, with each of the connected people being a — this is precisely what I want).
Now, I want to be able to filter the results by the Person.label, but any one of the Persons in the chain could match (either a OR any of the others).
Any idea how I might go about this?
I've tried a lot of different things (any(), for example) but can't get it to work.
The syntax for any() is WHERE any(e IN list WHERE predicate(e))
So in your case, this should work.
WITH COLLECT(other{.label}) as b
WHERE any(e IN b WHERE e.label = a.label)
RETURN b
You could in principle already apply it to the path before you collect. The tail(list) is so that it excludes a which would be the first node of the path.
MATCH ...
WHERE any(n in tail(nodes(path)) WHERE n.label = a.label)

loose nodes properties while using apoc.nodes.group()

I have a set of nodes which look like this :
dalle {
"ident": "A-1-1-1",
"networkId": 1,
"numberId": 1,
"floor": 1,
"room": 1,
"building": "A",
"buildingId": 1
}
I want to group my nodes so I do this command :
CALL apoc.nodes.group(['dalle'], ['building', 'floor', 'room'])
YIELD nodes, relationships
RETURN nodes, relationships
The result I got is really nice except one detail, I loose some properties, my nodes are now :
{
"floor": 3,
"count_*": 1,
"building": "C",
"room": 1
}
Why do I loose properties ?
I tried to update the nodes to set somes properties back like this :
CALL apoc.nodes.group(['dalle'], ['building', 'floor', 'room'])
YIELD nodes, relationships
FOREACH(n IN nodes | SET n.ident=n.building+n.floor)
RETURN nodes, relationships
but it changes nothing to my query result.
thanks !
You should read the documentation for the apoc.nodes.group procedure to see if it is actually the right thing for you to be using.
That procedure returns virtual nodes and relationships. Since virtual nodes and relationships are not actually in the DB, the SET clause does not work on them.
If you want more properties to show up in the virtual nodes, then you'd have to add them to the properties list you pass to the procedure. For example: ['building', 'floor', 'room', 'ident'].

How to Create One to Many Relationships From JSON with Neo4j Cypher

I would like to create one to many relationships from JSON items in a file. Specifically, each JSON item contains an author and the id of books they have published. I have author nodes and book nodes that already exist in the database.
The data looks like:
{"id": "1", "name": "Dr. Suess", "books": [{"i": "100", "i": "101"}]}
{"id": "2", "name": "Shell Silverstein", "books": [{"i": "200", "i": "201"}]}
I am trying to import the nodes with the following code:
CALL apoc.load.json('file:/data.txt') YIELD value AS q
MATCH (a:Author {{id:q.id}})
UNWIND q.books as books
WITH a, books
MATCH (b:Books {{id:books.i}})
CREATE (a)-[:AUTHORED]->(b)
However, this is importing a fraction of the nodes I am expecting. Any suggestions on how to approach this problem would be greatly appreciated!
Well if you say that not all the authors and books are imported it means that the two MATCH statements don't find what they are looking for.
One possible scenario is that you have the IDs stored as an integer, but now you are trying to match them as a string. With the provided information, it is hard to assume anything else.
I would change the MATCH into MERGE statements to see if that is the problem.
CALL apoc.load.json('file:/data.txt') YIELD value AS q
MERGE (a:Author {{id:q.id}})
UNWIND q.books as books
WITH a, books
MERGE (b:Books {{id:books.i}})
CREATE (a)-[:AUTHORED]->(b)

How to transform an UNWIND query to FOREACH in Neo4J Cypher?

I have the following Neo4J Cypher query:
UNWIND $mentionsRelations as mention
MATCH (u:User{name:mention.from})
RETURN u.uid;
The params are:
{
"mentionsRelations": [
{
"from": "a",
"to": "b"
},
{
"from": "c",
"to": "d"
}
]
}
Is it possible to rewrite it to get the same results using the FOREACH query?
I know it doesn't accept the MATCH parameter, but I'm just curious if there's a workaround to get the same results?
Basically what I want is to reiterate through the mentionsRelations using FOREACH and to then output any matches in the database it uncovers.
Thanks!
Not currently possible, FOREACH is only for write operations and can't be used for just matching to nodes.
Is there some reason UNWIND won't work for you?
You can also do a match based upon list membership, which should work similarly, though you'd have to extract out the values to use for the property match:
WITH [mention in $mentionsRelationships | mention.from] as froms
MATCH (u:User)
WHERE u.name in froms
RETURN u.uid;

Creating unique relationship property in a FOREACH in neo4j

I am trying to accomplish something quite simple in python but not so in Neo4j.
I'd appreciate any comment and suggestions to improve the procedure!
Within Python script, I am trying to create a relationship as well as its property for every pair of two nodes. From a data analysis (not a csv file), I ended up having a dataframe with three columns as following:
name1 name2 points
===========================
Jack Sara 0.3
Jack Sam 0.4
Jack Jill 0.2
Mike Jack 0.4
Mike Sara 0.5
...
From this point, I would like to create all nodes for the people: Jack, Sara, Sam, Mike, etc and as well as their relationship with a property name points.
First I tried to match all nodes and then use "FOREACH" to update the relationship property one at a time.
tx = graph.cypher.begin()
qs2 = "MATCH (p1:person {name:"Jack"}), (p2:person)
WHERE p2.name IN ["Sara","Jill","Mike"]
FOREACH (r IN range(10) |
CREATE (p1)-[:OWES TO {score:{score_list}[r]}]->(p2))"
Above statement does not return what I expected. Instead of matching one node to another, it calls all nodes in p2 and create the relationship between the paris, resulting multiple copies of the same information.
Is there a notation to indicate one node at a time? If you think there is a better approach than above, please do share with me. Thank you!
The easiest approach would be to export the data to be imported into csv file and use then the LOAD CSV command in cypher.
LOAD CSV WITH HEADERS FROM <url> AS csvLine
MATCH (p1:Person {name:csvLine.name1}), (p2:Person {name:csvLine.name2})
CREATE (p1)-[:OWES_TO {score:csvLine.points}]->(p2)
In case you cannot use that approach you can use a parameterized Cypher statement using the transactional http endpoint. The parameter is a single element map containing an array of your data structure. On http level the request body would look like:
{
"statements": [
{
"parameters": {
"data": [
{
"name1": "Jack", "name2": "Sara", "points": 0.3
},
{
"name1": "Jack", "name2": "Sam", "points": 0.4
},
{
"name1": "Jack", "name2": "Jill", "points": 0.2
} // ...
]
},
"statement": "UNWIND {data} AS row
MATCH (p1:Person {name:row.name1}), (p2:Person {name:row.name2})
CREATE (p1)-[:OWES_TO {row.points}]->(p2)"
}
]
}
update regarding comment below
Q: How can I create the parameters from pyhton?
A: use the python json module
import json
json.dumps({'data':[{'name1':'Jack', 'name2':'Sara', 'points':0.3},{'name1':'Jack', 'name2':'Sam', 'points':0.4}]})

Resources