My object structure looks like this: there are Containers as a root nodes, which have subcontainers. Each subcontainer has tasks, which also have subtasks. To illustracte this in Cypher MATCH:
(container)-[:HAS_SUBCONTAINER]->(subcont)-[:HAS_TASK]->(task)-[:HAS_TASK]->(subtask)
I want to write a query which returns JSON literal representation for this hierachy:
{
name: 'Main',
subcontainers: [
{
name: 'Subcont',
tasks: [
{
name: 'parent1',
children: [
{
name: 'child1'
}
]
}
]
}
]
}
Is there a way to do this in Cypher with one query? I found solution for one level hierarchy:
MATCH (cnt:Container {name: 'Main'})-[:HAS_SUBCONTAINER]->(subcnt)
RETURN { name: cnt.name, subcontainers: EXTRACT(subc IN collect(subcnt)|{name: subc.name})}
But can't fugure out how to do it for more complex case. Any ideas?
Given you know the depth of your matched pattern, you could do the nesting in a series of collects:
MATCH (container)-[:HAS_SUBCONTAINER]->(subcont)-[:HAS_TASK]->(task)-[:HAS_TASK]->(subtask)
WITH container, subcont, task, collect({name:subtask.name}) AS subtasks
WITH container, subcont, collect({name:task.name, children:subtasks}) AS tasks
WITH container, collect({name:subcont.name, tasks:tasks}) AS subconts
RETURN {
name: container.name,
subcontainers: subconts
} AS result
If the subtasks are option (the question by #jim-biard), then you'd need an optional match and a case statement:
MATCH (container)-[:HAS_SUBCONTAINER]->(subcont)-[:HAS_TASK]->(task)
OPTIONAL MATCH (task)-[:HAS_TASK]->(subtask)
WITH container, subcont, task, collect(
CASE subtask
WHEN NULL THEN NULL
ELSE { name:subtask.name }
END
) AS subtasks
WITH container, subcont, collect({name:task.name, children:subtasks}) AS tasks
WITH container, collect({name:subcont.name, tasks:tasks}) AS subconts
RETURN {
name: container.name,
subcontainers: subconts
} AS result
Related
If I wanted to return a composite object, based on an exisiting node, I know that I could to this: (thanks to this post)
MATCH(...)-[:HAS_DB]->(db: Database {name: "my_database")
WITH { name: db.name,
format: db.format,
} AS database
RETURN database;
This would return an object based on but not exactly my Database node.
However, I would like to return composite objects for a collection of nodes, not just for one node:
I tried this, but it seems like FOREACH is only appropriate for List<T>.
MATCH(...)-[:HAS_DB]->(databases: Database)
FOREACH (db IN databases |
RETURN {
name: db.name,
format: db.format
}
)
How could I do this?
MATCH(...)-[:HAS_DB]->(db: Database {name: "my_database")
WITH DISTINCT db
Return COLLECT( { name: db.name,
format: db.format,
}) AS database
Say I've created a node in Neo4j:
CREATE (:Thing {a:'foo', b:'bar'})
I can write a query to get that node with all of its properties
MATCH (n:Thing {a:'foo'}) RETURN n
which returns
{
"a": "foo",
"b": "bar"
}
but is it possible to match a node and retrieve only a subset of its properties, so that for example, Neo4j would return a node with only
{
"b": "bar"
}
(Not looking for just the property, like you would get via RETURN n.b)
Yes, you can use map projections in Cypher, for eg :
MATCH (n:Thing {a:'foo'}) RETURN n{.a}
will return :
{a:"foo"}
More info in the documentation
I want to use a cypher query to return results in a format that I can use with D3. What I have below is working fine but I want to be able to include the properties of the nodes as "key:value" pairs directly after the labels are printed out. I can't explicitly code this because different nodes can have different properties e.g. I can't just add (prop1: l1.prop1, prop2: l1.prop2 .....).
MATCH (l0) -[r]-> (l1)
WHERE ID(l0) = #
RETURN
[
{
type: "node",
id: id(l0),
labels: labels(l0),
},
{
type: "node",
id: id(l1),
labels: labels(l1)
}
] as nodes,
[
{
startNodeId: ID(startNode(r)),
endNodeId: ID(endNode(r)),
relType: type(r)
}
] as relationship
I came accross this example on the forum which is close to what I want:
MATCH (n) WHERE id(n)=#
RETURN EXTRACT(key IN keys(n) | {key: key, value: n[key]})
This results in the following:
[{"key":"name","value":"Test Node 1"}]
where as I want to have just
[{"name":"Test Node 1"}]
I am using the KOA-NEO4J library so connecting to Neo4j over Bolt if that makes any difference.
Thanks a lot,
Cypher itself does not allow for dynamically creating the key of a map, however you can use the APOC function apoc.map.fromPairs to accomplish this. So your example above becomes:
MATCH (n) WHERE id(n) = #
apoc.map.fromPairs([key IN keys(n) | [key, n[key]]])
And your larger query becomes:
MATCH (l0) -[r]-> (l1)
WHERE ID(l0) = 1
RETURN
[
{
type: "node",
id: id(l0),
labels: labels(l0),
props: apoc.map.fromPairs([key IN keys(l0) | [key, l0[key]]])
},
{
type: "node",
id: id(l1),
labels: labels(l1),
props: apoc.map.fromPairs([key IN keys(l1) | [key, l1[key]]])
}
] as nodes,
[
{
startNodeId: ID(startNode(r)),
endNodeId: ID(endNode(r)),
relType: type(r)
}
] as relationship
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.
I have a nested has_many relationship that I'm trying to map to a json result.
The following example from the blog shows the kind of thing I want to do, except it's not nested
MATCH (a:Person { name: "Andres" })-[:FATHER_OF]->(child)
RETURN
{name:a.name, kids:collect(child.name)} as document
What I want instead is something like this
MATCH (a:Person { name: "Andres" })-[:FATHER_OF]->(child)-[:has_read]->(book)-[:has_chapter]->(chapter)
RETURN
{name:a.name, kids:collect({"name":child.name, has_read:collect(book)})} as document
In this case I would like to return a json object of a structure like this:
{
"name": "Andres"
"kids": [
{
"name":"Bob"
"has_read": [
{
"name":"Lord of the Rings",
"chapters": ["chapter1","chapter2","chapter3"]
},
{
"name":"The Hobbit",
"chapters": ["An unexpected party","Roast mutton"]
}
]
},
{
"name":"George"
"has_read": [
{
"name":"Lord of the Rings",
"chapters": ["chapter1","chapter2","chapter3"]
},
{
"name":"Silmarillion",
"chapters": ["chapter1","chapter2"]
}
]
}
]
}
Can you try:
if you keep the match to the chapter you will need distinct
collect(distinct book.title) otherwise not.
MATCH (a:Person { name: "Andres" })-[:FATHER_OF]->(child),
(child)-[:has_read]->(book)-[:has_chapter]->(chapter)
WITH a,child,collect(distinct book.title) as books
RETURN
{name:a.name,
kids:collect({name:child.name,
has_read:books})} as document
Oh and if you want to include the chapters in the results too, then just add another with :)
MATCH (a:Person { name: "Andres" })-[:FATHER_OF]->(child),
(child)-[:has_read]->(book)-[:has_chapter]->(chapter)
WITH a,child, {title: book.title, chapters: collect(chapter.title)} as book_doc
WITH a,child, collect(book_doc) as books
RETURN
{name:a.name,
kids:collect({name:child.name,
has_read:books})} as document
see:
http://console.neo4j.org/r/kua2pi