Find subtree using recursive query - neo4j

Given graph:
A
____|_____
(1)/ (2)| (3)\
AA AB AC
____|____
(1)/ (2)| (3)\
AAA AAB AAC
Order stored in relationships.
create
(a:Node {title:"A", ordered:false, active: true}),
(aa:Node {title:"AA", ordered: true, active: true}),
(ab:Node {title:"AB", ordered: false, active: true}),
(ac:Node {title:"AC", ordered: false, active: false}),
(aaa:Node {title:"AAA", ordered: false, active: true}),
(aab:Node {title:"AAB", ordered: false, active: true}),
(aac:Node {title:"AAC", ordered: false, active: true}),
(a)-[:RELATED_WITH { order: 1 }]->(aa),
(a)-[:RELATED_WITH { order: 2 }]->(ab),
(a)-[:RELATED_WITH { order: 3 }]->(ac),
(aa)-[:RELATED_WITH{ order: 1 }]->(aaa),
(aa)-[:RELATED_WITH{ order: 2 }]->(aab),
(aa)-[:RELATED_WITH{ order: 3 }]->(aac)
return *;
For a given start node (for example "A"), I need to find all subnodes, but I can only go down to active nodes (active = true). Also, if a node has the "ordered = true" property set, then I only need to descend to the first active node.
For example, for the graph above, a subtree is expected to be found:
A
/ \
AA AB
|
AAA
How to make a select (match) query to get this result?

Try this query:
MATCH path = (a:Node{title: 'A'})-[*]-(b{active:true})
RETURN path
It will give you the subtree starting from node A, with every other leaf node or intermediate node, having active property as true.

Related

Handling parameterised order by filters within neo4j cypher

I am working on the cypher below but I am not sure how to implement parameterised sorting. I want to sort with the parameters $field and $sort where $field could be: 'species.name', 'species.description', 'species.scientificName', 'monthCount', 'eats' or 'eatenBy' and $sort is only 'asc' or 'desc'. If either of those values is hardcoded then the cypher runs but when passed as a parameter it fails. Any help would be greatly appreciated! :)
MATCH (s:Species)
WHERE toLower(s.name) CONTAINS toLower($search)
WITH s
OPTIONAL MATCH (s)-[e:EATS]->(eatsSpecies:Species)
OPTIONAL MATCH (s)<-[:EATEN_BY]-(eatenBySpecies:Species)
OPTIONAL MATCH (s)<-[:IS_ABOUT]-(image:Image)
OPTIONAL MATCH (s)-[:FALLS_UNDER]->(primary:Primary)
OPTIONAL MATCH (s)-[:MEASURED_BY]->(month:Month)
WITH s, eatsSpecies, eatenBySpecies, image, primary, month
WITH s,
count(DISTINCT eatsSpecies.name) AS eats,
count(DISTINCT eatenBySpecies.name) AS eatenBy,
primary,
image,
count(distinct month) as monthCount
WITH {
name: s.name,
scientificName: s.scientificName,
description: s.description,
primary: case when exists(primary.GUID) then true else false end,
active: case when exists(s.active) then s.active else true end,
months: monthCount,
guid: s.GUID,
eats: eats,
eatenBy: eatenBy,
image: case when exists(image.url) then true else false end
} AS species order by $field $sort
SKIP $skip
LIMIT $limit
RETURN collect(species)
Something like this could work, by adding a field key to the object and inverting the collection before the SKIP and LIMIT
The suggestion below is pure Cypher. Using apoc, you could also create your dynamic queries.
MATCH (s:Species)
WHERE toLower(s.name) CONTAINS toLower($search)
WITH s
OPTIONAL MATCH (s)-[e:EATS]->(eatsSpecies:Species)
OPTIONAL MATCH (s)<-[:EATEN_BY]-(eatenBySpecies:Species)
OPTIONAL MATCH (s)<-[:IS_ABOUT]-(image:Image)
OPTIONAL MATCH (s)-[:FALLS_UNDER]->(primary:Primary)
OPTIONAL MATCH (s)-[:MEASURED_BY]->(month:Month)
WITH s, eatsSpecies, eatenBySpecies, image, primary, month
WITH s,
// add a 'sortField'
s[$field] AS field,
count(DISTINCT eatsSpecies.name) AS eats,
count(DISTINCT eatenBySpecies.name) AS eatenBy,
primary,
image,
count(distinct month) as monthCount
WITH {
name: s.name,
scientificName: s.scientificName,
description: s.description,
primary: case when exists(primary.GUID) then true else false end,
active: case when exists(s.active) then s.active else true end,
months: monthCount,
guid: s.GUID,
eats: eats,
eatenBy: eatenBy,
image: case when exists(image.url) then true else false end,
field: field
} AS species ORDER BY species.field
WITH COLLECT(species) AS sortedSpecies
RETURN CASE $sort
WHEN "asc" THEN sortedSpecies[$skip .. $limit]
ELSE REDUCE(array=[], i IN RANGE(1,size(sortedSpecies)) |
array
+sortedSpecies[size(sortedSpecies)-i]
)[$skip .. $limit]
END AS sortedSpecies

Return relations as child nodes in tree (Neo4j Cypher)

Is there a way to retrieve relations as child elements of a tree?
This is the basic data i have:
CREATE (:Customer {id:1, name:'Customer 1'})<-[:CREATED_BY]-(c:Category {id:1, name:'Category 1'})
WITH c as category, range(2, 7) as subCatIds
FOREACH (s IN subCatIds | CREATE (category)<-[:SUBCATEGORY_OF]-(:Category {id:s, name:'SubCategory '+s}))
WITH category, range(1, 5) as attTypeIds
FOREACH (a IN attTypeIds | CREATE (category)-[:HAS_ATTRIBUTE {name:'Attribute '+a, required: (a%2=0)}]->(:AttributeType {id:a, name:'AttributeType '+a}))
WITH category
MATCH p = (:AttributeType)<-[:HAS_ATTRIBUTE]-(category)<-[:SUBCATEGORY_OF]-(:Category)
RETURN p
So this query returns correctly the tree structure:
MATCH (:Customer {id:1})<-[:CREATED_BY]-(c:Category {id:1}),
p = (c)<-[:SUBCATEGORY_OF*0..1]-(:Category)
WITH COLLECT(p) as category
CALL apoc.convert.toTree(category) yield value
RETURN value
How do i add the relationships [:HAS_ATTRIBUTE] as child nodes to this query?
I've tried already:
MATCH (:Customer {id:1})<-[:CREATED_BY]-(c:Category {id:1}),
SubCatsP = (c)<-[:SUBCATEGORY_OF*0..1]-(:Category),
AttP = (c)-[:HAS_ATTRIBUTE]->(att:AttributeType)
WITH COLLECT(SubCatsP) as category, RELATIONSHIPS(AttP) as attributes
CALL apoc.convert.toTree(category) yield value
RETURN value, attributes
But this returns 5 records (1 for each relationship [:HAS_ATTRIBUTE]) with the Category-Subcategories tree repeated.
I expect the result to be:
{
id: 1,
name: 'Category 1',
SUBCATEGORY_OF:[
{id:2, name: 'Subcategory 2'}, ...
]
HAS_ATTRIBUTE:[
{name: 'Attribute 1', required: false, att: {name:'AttributeType 5',id:5}}, ...
<OR>
{name: 'Attribute 1', required: false, att.name:'AttributeType 5',att.id:5}, ...
]
}
Is this even possible or do you consider a better approach to perform 2 separate queries?
You can try combining the paths into a single list:
MATCH (:Customer {id:1})<-[:CREATED_BY]-(c:Category {id:1}),
SubCatsP = (c)<-[:SUBCATEGORY_OF*0..1]-(:Category),
AttP = (c)-[:HAS_ATTRIBUTE]->(att:AttributeType)
WITH COLLECT(SubCatsP) + COLLECT(AttP) as category
CALL apoc.convert.toTree(category) yield value
RETURN value

Neo4j cypher: Find all paths till node of certain type

In Neo4j I have stored data with nodes of type A and B. Between 2 nodes A there could be many nodes of type B. I would like to fetch first node of type A for each path outgoing from a given node A. I post example structure below.
/->A2->A3
A1-->A4->A5
\->B1->A6
For input: A1, I would like my query to return only A2, A4 and A6.
Query that I'm using right now is below:
MATCH p=(source:Node ) - [:relation*] -> (target:Node) WHERE
source.name = "A1" AND target.type = "A" RETURN target
However it returns nodes A3 and A5 which I want to get rid of.
I have used this sample data set to reproduce your scenario:
create (root:Node {name : "A1", type: "A"})-[:LINKED_TO]->(:Node{name : "A2", type: "A"})-[:LINKED_TO]->(:Node{name : "A3", type: "A"}),
(root)-[:LINKED_TO]->(:Node{name : "A4", type: "A"})-[:LINKED_TO]->(:Node{name : "A5", type: "A"}),
(root)-[:LINKED_TO]->(:Node{name : "B1", type: "B"})-[:LINKED_TO]->(:Node{name : "A6", type: "A"})
Then, this query, using filter() function:
// MATCH all paths between source and target, starting from 2 hops.
// target node should be the last node of the path.
MATCH p=(source:Node)-[:LINKED_TO*]->(target:Node)
WHERE source.name = "A1" AND target.type = "A" AND NOT (target)-->()
// grouping by path, I have filtered the nodes of each path, getting only ones that have type = "A"
// then I get the first one by index (index [0])
WITH p, filter(node IN nodes(p)[1..] WHERE node.type = "A")[0] as firstA
// return the firstA node
RETURN firstA
The output:
╒════════════════════════╕
│"firstA" │
╞════════════════════════╡
│{"name":"A6","type":"A"}│
├────────────────────────┤
│{"name":"A4","type":"A"}│
├────────────────────────┤
│{"name":"A2","type":"A"}│
└────────────────────────┘
Tip: instead of a property named type you can add another label for each node denoting your type, like :A and :B. Remember that labels are ideal for grouping nodes into sets. Also, a node can have more than one label.

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

Consolidating & Collecting into one Record

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.

Resources