Recursive neo4j query - neo4j

I have a graph which has categories and sub-categories and sub-sub-categories to indefinite level.
How I can I get all this hierarchical data in one cipher query?
I currently have this query :
START category=node:categoryNameIndex(categoryName = "category")
MATCH path = category <- [rel:parentCategory] - subcategory
RETURN category, collect(subcategory);
Which gives me following result:
| category | collect(subcategory) |
==> +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
==> | Node[26]{categoryName:"Test2",categoryDescription:"testDesc",imageUrl:"testUrl",imageName:"imageName"} | [Node[25]{categoryName:"Test1",categoryDescription:"testDesc",imageUrl:"testUrl",imageName:"imageName"}] |
==> | Node[1]{categoryName:"Test1",categoryDescription:"testDesc",imageUrl:"testUrl",imageName:"imageName"} | [Node[26]{categoryName:"Test2",categoryDescription:"testDesc",imageUrl:"testUrl",imageName:"imageName"},Node[2]{categoryName:"Test2",categoryDescription:"testDesc",imageUrl:"testUrl",imageName:"imageName"}] |
I am using node-neo4j.
I will give an example of what I want in json format.
[{
"categoryName": "Test2",
"categoryDescription": "testDesc",
"imageUrl": "testUrl",
"children": [{
"categoryName": "Test1",
"categoryDescription": "testDesc",
"imageUrl": "testUrl",
"children" : [{
"categoryName": "Test1",
"categoryDescription": "testDesc",
"imageUrl": "testUrl"
}]
}]
}]
I this possible? I know I can always do it programmatically OR by using multiple queries. But it will very helpful if it can be done in a single query.

You can match paths of arbitrary depth by adding a * after the relationship type:
START category=node:categoryNameIndex(categoryName = "category")
MATCH path = category <-[rel:parentCategory*]- subcategory
RETURN category, collect(subcategory);
Optionally, you can also specify a minimum and/or maximum path length:
START category=node:categoryNameIndex(categoryName = "category")
MATCH path = category <-[rel:parentCategory*2..5]- subcategory
RETURN category, collect(subcategory);
See reference here:
http://docs.neo4j.org/chunked/milestone/query-match.html#match-variable-length-relationships

Related

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

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"
}
]
}
}
]

Use EXTRACT(Keys) or other method to get a Nodes properties as key:value pairing with Cypher in Neo4j

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

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) }

Rails PSQL query JSON for nested array and objects

So I have a json (in text field) and I'm using postgresql and I need to query the field but it's nested a bit deep. Here's the format:
[
{
"name":"First Things",
"items":[
{
"name":"Foo Bar Item 1",
"price":"10.00"
},
{
"name":"Foo Item 2",
"price":"20.00"
}
]
},
{
"name":"Second Things",
"items": [
{
"name":"Bar Item 3",
"price":"15.00"
}
]
}
]
And I need to query the name INSIDE the items node. I have tried some queries but to no avail, like:
.where('this_json::JSON #> [{"items": [{"name": ?}]}]', "%#{name}%"). How should I go about here?
I can query normal JSON format like this_json::JSON -> 'key' = ? but need help with this bit.
Here you need to use json_array_elements() twice, as your top level document contains array of json, than items key has array of sub documents. Sample psql query may be the following:
SELECT
item->>'name' AS item_name,
item->>'price' AS item_price
FROM t,
json_array_elements(t.v) js_val,
json_array_elements(js_val->'items') item;
where t - is the name of your table, v - name of your JSON column.

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.

Resources