Neo4j apoc.convert.toTree is not showing duplicate children with unique relationship - neo4j

I have two nodes A and B (A->B) connected via two relationships. Those 2 relationships are unique. For example, you can think of the two rels as two paths from A to B at two different times. Converting them to one relationship is not viable at this time.
For displaying in UI, query result should be a tree structure. Used following query to get the tree
MATCH p=(n:Label1 {name:'main'})-[:calls*..2]->(m)
WITH COLLECT(p) AS ps
CALL apoc.convert.toTree(ps) yield value
RETURN value;
the resulting tree is
Preferred result is
How can I get a tree with duplicate children is more than one relationship exists between same two nodes?
Thanks.

I offer a hack on this SO question. Although it is not exactly the resulting nested document as the apoc function but I think it can server your purpose.
MATCH p=(n:Label1 {name:'main'})-[:calls*..2]->(m)
// each path will be returned
WITH COLLECT(p) AS ps, p, n
CALL apoc.convert.toTree(ps) yield value
// return node n and it's relationships as a nested document
RETURN {_id: ID(n), _type: labels(n), node:n, relationships: collect(value['calls'][0])} as result
Sample result:
[
{
"result": {
"_id": 31,
"_type": [
"Label1"
],
"node": {
"name": "main"
},
"relationships": [
{
"calls.y": "prop y",
"_type": "Main",
"_id": 32
},
{
"_type": "Main",
"_id": 32,
"calls.x": "prop x"
}
]
}
}
]

Related

Returning Neo4j map projection with WebFlux

I have the nodes user and game with some relationships between them.
My REST API should return all relationships between the games and 1 user.
The cypher query i use is:
MATCH (u:User {id: '1234'} ) -[rel]- (game:Game) return game{.*, relationships: collect(DISTINCT rel)}
In my Neo4j Browser, everything works as expected and i see all properties i need.
But the GetMapping retuns everything except the relationship properties.
Neo4j Browser
{
"relationships": [
{
"identity": 54,
"start": 9,
"end": 8,
"type": "OWNED",
"properties": {
"ownedDate": "2021-07-03"
}
},
{
"identity": 45,
"start": 9,
"end": 8,
"type": "PLAYED",
"properties": {
"times": 5
}
}
],
"name": "Blood Rage",
"state": "ACTIVE",
"id": "1c152c91-4044-41f0-9208-0c436d6f6480",
"gameUrl": "https://asmodee.de/blood-rage"
}
GetMapping result (As you can see, the relationships are empty, but i have more empty JsonObjects, when there are more Relationships
{
"game": {
"relationships": [
{},
{}
],
"name": "Blood Rage",
"gameUrl": "https://asmodee.de/blood-rage",
"state": "ACTIVE",
"id": "1c152c91-4044-41f0-9208-0c436d6f6480"
}
}
The GetMapping is:
...
final ReactiveNeo4jClient client;
...
...
...
#GetMapping(value = { "/{id}/games"})
#RolesAllowed({"user", "admin"})
Flux<Map<String, Object>> findGamesByUser(#PathVariable String id){
String query = "MATCH (uuser:User {id: '" + id + "'} ) -[rel]- (game:Game) return game{.*, relationships: collect(DISTINCT rel)}";
return client.query(query).fetch().all();
}
A RelationshipProperty-Example
#RelationshipProperties
#Data
#Builder
public class PlayedGame {
#Id
#GeneratedValue
private Long relationshipId;
#Property
int times = 0;
#TargetNode private GameEntity game;
public int addPlay(){
this.times = this.times + 1;
return this.times;
}
}
What do i have to change in my GetMapping to show the relationship-properties?
Thank you,
Kevin
You need to return the actual nodes and relationships, otherwise you're missing the id-mapping.
There should be examples in the SDN docs.
Best if you have a small reproducible example (e.g. with the default movies graph).
Not sure if there is something off in your SDN setup, in general for such simple queries you should be able to just use a repository and not need to write cypher queries by hand.
The general information given by Michael is correct but there is more in you question:
First of all the meta domain model is completely ignored if you are using the Neo4jClient. It does not automatically map anything back but uses the driver's types.
As a result you will end up with an (current state of this answer) InternalRelationship which does not have any getter-methods.
I assume that you are serializing the result in the application with Jackson. This is the reason why you see objects that represent the relationships but without any content within.
If you want to get things mapped for you, create also the domain objects properly and use (at least) the Neo4jTemplate with your query.
If you model User, Game, and the relationship properties like PlayedGame correctly, a
neo4jTemplate.findAll("MATCH (u:User)<-[rel]-(g:Game) return u, collect(rel), collect(g)", User.class)
will map the results properly. Also if this is all you have, you could also skip the custom query at all and use
neo4jTemplate.findAll(User.class)
or
neo4jTemplate.findById(useId, User.class)

Create relationship with multipl values

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

Neo4j return a node with an array of nodes as propery or seperate array

I have four nodes that -[belongTo]-> (ContainerNode)
I want the json to return as a single container node which contains an array of all the nodes that link to it. For example:
"nodes": [
{
"id": "240",
"name":"MyNodeContainer",
"Type": "ContainerNode"
"SubNodes": [
{
"id": "1",
"name":"MyNodeA",
"Type": "node"
},
{
"id": "2",
"name":"MyNodeB",
"Type": "node"
}
]
},
It seems simple but all i can get is the default of all nodes being returned as equal. I want the result to make it clear that the container node is separate from the rest. An array property seems most intuitive but i would also be content with two lists - one for the single nodeContainer and one for the contained nodes
Does something like this steer you towards your end goal? It builds a collection of the contained nodes and then returns it as a property of the ContainerNode.
MATCH (c:ContainerNode)<-[:BELONGS_TO]-(n:Node)
WITH c, collect({ id: id(n), name: n.name, type: labels(n)[0] }) AS nodes
WITH { id: id(c), name: c.name, type: labels(c)[0], SubNodes: nodes } AS containerNode
RETURN {nodes: collect(containerNode) }

Neocons Cypher tquery and accessing values from keys

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.

neo4j return root node once, and all relationships and end-nodes in json

I'm trying to make a html view of everything known about a node.
So, if i were to select that node n and RETURN n, i'd get a json object with n: { stuff ... }, and if i also return its relationships and connected nodes RETURN n,r,n2, now i get a really verbose json object:
[
{
n: { originating node details },
r: { relationship details },
n2: { connected node details }
},
{
n: { originating node details },
r: { relationship details },
n2: { connected node details }
},
{
n: { originating node details },
r: { relationship details },
n2: { connected node details }
}
]
Where the same exact data is in n a bunch of times. So, in theory, that'd be OK, and I could say var seedNodeData = results[0].n and use that as a way to get the main node data, before then separately iterating over all objects to list out the connecting node data. But, this seems like a waste of data transferred.
Currently, to limit that data, I just RETURN r,n2 since the first one is always the same, but then I"d need to do a separate call to get n's data.
Is there a way to make one call to get the info for n once, and then [r]-(n2) as many times as necessary and have it wrapped up into one cypher query?
You could aggregate the relationships and relatives into a collection.
Does something like this work for you?
match n-[r]->n2
with n, [type(r), n2] as relative
return { root: n, relatives: collect(relative) }
I would use a collection of maps to describe each of the other nodes and the relationships that connect them.
MATCH (n)-[r]-(o) WHERE ...
RETURN n AS node, labels(n) AS node_labels, COLLECT({rel: r, rel_type: type(r), other: o, other_labels: labels(o)}) AS relations
Using this RETURN statement, you only get your subject node (and its properties and labels) back once, along with a list of maps that describes the relationships (both type and properties included) and related nodes (both labels and properties included).
The output will be just one "row", structured like:
[
{"subject_prop1": 1, "subject_prop2": 2, ...},
["SubjectLabel1", "SubjectLabel2", ...],
[
{
"rel": {"rel_prop1": 1, "rel_prop2": 2, ...},
"rel_type": "RELTYPE",
"other": {"other_prop1": 1, "other_prop2": 2, ...},
"other_labels": ["Label1", "Label2", ...],
},
{
...
}
]
]
(I excluded the Neo4j boilerplate from the structure.)

Resources