Neo4j where predicates on multiple attributes - neo4j

In Neo4j, for the where predicate, can we have constraints on more than one property? For example, suppose that we have a list of pairs: L = [(23, 'San Diego'), (25, 'Palo Alto'), (21, 'Seattle'), ....], then does Cypher support something similar to the following:
Match (a) where (a.age, a.city) in L return a
The age and city combinations need to be in the L list

Neo4j does not accept tuples but map of key, value pairs (or dictionary).
However, this query will be close to what you have described.
WITH [{age:23, city:'San Diego'}, {age:25, city:'Palo Alto'}, {age:21, city:'Seattle'}] as L
MATCH (p:Person) WHERE {age: p.age, city: p.city} in L
RETURN p
Sample result:
╒═══════════════════════════════════════════╕
│"p" │
╞═══════════════════════════════════════════╡
│{"name":"Andy","city":"San Diego","age":23}│
└───────────────────────────────────────────┘
See below:
https://neo4j.com/docs/cypher-manual/current/syntax/values/#composite-types

Related

Regular expression on the string of a property name in Cypher

I know it is possible to use regular expressions for property values like for example:
MATCH (n)
WHERE n.SomeProperty =~ 'somestring*'
RETURN n;
What i want is to use regular expression on the property name and check for all the properties which start with a certain string like for example:
MATCH (n)
WHERE n.`SomeProperty*` > 10
RETURN n;
So I want to have all nodes which have a property which begins with 'SomeProperty' and have a value > 10 for this property.
This doesn't seems possible with using regular expressions like in my example. I've tried it and with my research i couldn't find a solution. Does anyone have an idea how to achieve this using another technique ?
Given the following test graph
CREATE (:TestNode {somePropertyOne: 10})
CREATE (:TestNode {somePropertyTwo: 11})
CREATE (:TestNode {somePropertyThree: 12})
CREATE (:TestNode {someOtherProperty: 13})
The following query achieves what you want
MATCH (n)
WHERE ANY(x IN keys(n) WHERE x STARTS WITH 'someProperty' AND n[x] > 10)
RETURN n
╒════════════════════════╕
│"n" │
╞════════════════════════╡
│{"somePropertyTwo":11} │
├────────────────────────┤
│{"somePropertyThree":12}│
└────────────────────────┘
Bear in mind that its really not an optimized query for graphs, so it will be slow on decent size databases.
I created sample nodes as below:
Create (n1:RexNode {someproperty10: 10}),
(n2:RexNode { someproperty11: 11}),
(n3:RexNode {someproperty12: 12})
Then I used this query to return n2 and n3. As you can see n1 starts with someproperty but the value is not greater than 10. The quantifier is ANY so it will only look for at least one property (key of node n) and it will return it.
MATCH (n)
WITH n
WHERE
ANY( k in keys(n)
WHERE k STARTS WITH 'someproperty'
AND n[k] > 10
)
RETURN n
Result:
╒═════════════════════╕
│"n" │
╞═════════════════════╡
│{"someproperty11":11}│
├─────────────────────┤
│{"someproperty12":12}│
└─────────────────────┘

Matching all nodes related to a set of other nodes - neo4j

I'm just getting started with neo4j and would like some help trying to solve a problem.
I have a set of Questions that require information (Slots) to answer them.
The rules of the graph (i.e. the Slots required for each Question) are shown below:
Graph diagram here
In a scenario in which I have a set of slots e.g. [Slot A, Slot B] I want to be able to check all Questions that the Slots are related to e.g. [Question 1 , Question 2].
I then want to be able to check for which of the Questions all required Slots are available, e.g. [Question 1]
Is this possible, and if so how should I go about it?
Yes it's possible.
Some data fixtures :
CREATE (q1:Question {name: "Q1"})
CREATE (q2:Question {name: "Q2"})
CREATE (s1:Slot {name: "Slot A"})
CREATE (s2:Slot {name: "Slot B"})
CREATE (s3:Slot {name: "Slot C"})
CREATE (q1)-[:REQUIRES]->(s1)
CREATE (q1)-[:REQUIRES]->(s2)
CREATE (q2)-[:REQUIRES]->(s1)
CREATE (q2)-[:REQUIRES]->(s3)
Find questions related to a slots list :
MATCH p=(q:Question)-[:REQUIRES]->(slot)
WHERE slot.name IN ["Slot A", "Slot B"]
RETURN p
Then, find questions related to a slot list, and return a boolean if the slot list contains all required slots for a question :
MATCH p=(q:Question)-[:REQUIRES]->(slot)
WHERE slot.name IN ["Slot A", "Slot B"]
WITH q, collect(slot) AS slots
RETURN q, ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
╒═════════════╤═══════════════════════════════════════════════════════╕
│"q" │"ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)"│
╞═════════════╪═══════════════════════════════════════════════════════╡
│{"name":"Q1"}│true │
├─────────────┼───────────────────────────────────────────────────────┤
│{"name":"Q2"}│false │
└─────────────┴───────────────────────────────────────────────────────┘
A bit of explanation on that part ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
the ALL predicate, will check that the condition for every value in a list is true, for example ALL (x IN [10,20,30] WHERE x > 5)
the extract shortcut syntax, you pass a list, it returns a list of the extracted values, the syntax is extract(x IN <LIST> | <key to extract>) for example :
extract(x IN [{name: "Chris", age: 38},{name: "John", age: 27}] | x.age)
// equivalent to the shortcut syntax for extract, with square brackets
[x IN [{name: "Chris", age: 38},{name: "John", age: 27}] | x.age]
Will return [38,27]
Combining it now :
For every path, extract the Slot node
[(q)-[:REQUIRES]->(s) | s]
Returns
[s1, s2]
Are every of s1 and s2, in the list of the slot nodes previously collected ?
ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
Return true or false
Return only the questions when true :
MATCH p=(q:Question)-[:REQUIRES]->(slot)
WHERE slot.name IN ["Slot A", "Slot B"]
WITH q, collect(slot) AS slots
WITH q WHERE ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
RETURN q

How to find specific subgraph in Neo4j using where clause

I have a large graph where some of the relationships have properties that I want to use to effectively prune the graph as I create a subgraph. For example, if I have a property called 'relevance score' and I want to start at one node and sprawl out, collecting all nodes and relationships but pruning wherever a relationship has the above property.
My attempt to do so netted this query:
start n=node(15) match (n)-[r*]->(x) WHERE NOT HAS(r.relevance_score) return x, r
My attempt has two issues I cannot resolve:
1) Reflecting I believe this will not result in a pruned graph but rather a collection of disjoint graphs. Additionally:
2) I am getting the following error from what looks to be a correctly formed cypher query:
Type mismatch: expected Any, Map, Node or Relationship but was Collection<Relationship> (line 1, column 52 (offset: 51))
"start n=node(15) match (n)-[r*]->(x) WHERE NOT HAS(r.relevance_score) return x, r"
You should be able to use the ALL() function on the collection of relationships to enforce that for all relationships in the path, the property in question is null.
Using Gabor's sample graph, this query should work.
MATCH p = (n {name: 'n1'})-[rs1*]->()
WHERE ALL(rel in rs1 WHERE rel.relevance_score is null)
RETURN p
One solution that I can think of is to go through all relationships (with rs*), filter the the ones without the relevance_score property and see if the rs "path" is still the same. (I quoted "path" as technically it is not a Neo4j path).
I created a small example graph:
CREATE
(n1:Node {name: 'n1'}),
(n2:Node {name: 'n2'}),
(n3:Node {name: 'n3'}),
(n4:Node {name: 'n4'}),
(n5:Node {name: 'n5'}),
(n1)-[:REL {relevance_score: 0.5}]->(n2)-[:REL]->(n3),
(n1)-[:REL]->(n4)-[:REL]->(n5)
The graph contains a single relevant edge, between nodes n1 and n2.
The query (note that I used {name: 'n1'} to get the start node, you might use START node=...):
MATCH (n {name: 'n1'})-[rs1*]->(x)
UNWIND rs1 AS r
WITH n, rs1, x, r
WHERE NOT exists(r.relevance_score)
WITH n, rs1, x, collect(r) AS rs2
WHERE rs1 = rs2
RETURN n, x
The results:
╒══════════╤══════════╕
│n │x │
╞══════════╪══════════╡
│{name: n1}│{name: n4}│
├──────────┼──────────┤
│{name: n1}│{name: n5}│
└──────────┴──────────┘
Update: see InverseFalcon's answer for a simpler solution.

Cypher - Only show node name, not full node in path variable

In Cypher I have the following query:
MATCH p=(n1 {name: "Node1"})-[r*..6]-(n2 {name: "Node2"})
RETURN p, reduce(cost = 0, x in r | cost + x.cost) AS cost
It is working as expected. However, it prints the full n1 node, then the full r relationship (with all its attributes), and then full n2.
What I want instead is to just show the value of the name attribute of n1, the type attribute of r and again the name attribute of n2.
How could this be possible?
Thank you.
The tricky part of your request is the type attribute of r, as r is a collection of relationships of the path, not a single relationship. We can use EXTRACT to produce a list of relationship types for all relationships in your path. See if this will work for you:
MATCH (n1 {name: "Node1"})-[r*..6]-(n2 {name: "Node2"})
RETURN n1.name, EXTRACT(rel in r | TYPE(rel)) as types, n2.name, reduce(cost = 0, x in r | cost + x.cost) AS cost
You also seem to be calculating a cost for the path. Have you looked at the shortestPath() function?

Return Neo4J Combined Relationships When Searching Across Several Relationship Types

I would like to query for various things and returned a combined set of relationships. In the example below, I want to return all people named Joe living on Main St. I want to return both the has_address and has_state relationships.
MATCH (p:Person),
(p)-[r:has_address]-(a:Address),
(a)-[r1:has_state]-(s:State)
WHERE p.name =~ ".*Joe.*" AND a.street = ".*Main St.*"
RETURN r, r1;
But when I run this query in the Neo4J browser and look under the "Text" view, it seems to put r and r1 as columns in a table (something like this):
│r │r1 │
╞═══╪═══|
│{} │{} │
rather than as desired with each relationship on a different row, like:
Joe Smith | has_address | 1 Main Street
1 Main Street | has_state | NY
Joe Richards | has_address | 22 Main Street
I want to download this as a CSV file for filtering elsewhere. How do I re-write the query in Neo4J to get the desired result?
You may want to look at the Cypher cheat sheet, specifically the Relationship Functions.
That said, you have variables on all the nodes you need. You can output all the data you need on each row.
MATCH (p:Person),
(p)-[r:has_address]-(a:Address),
(a)-[r1:has_state]-(s:State)
WHERE p.name =~ ".*Joe.*" AND a.street = ".*Main St.*"
RETURN p.name AS name, a.street AS address, s.name AS state
That should be enough.
What you seem to be asking for above is a way to union r and r1, but in such a way that they alternate in-order, one row being r and the next being its corresponding r1. This is a rather atypical kind of query, and as such there isn't a lot of support for easily making this kind of output.
If you don't mind rows being out of order, it's easy to do, but your start and end nodes for each relationship are no longer the same type of thing.
MATCH (p:Person),
(p)-[r:has_address]-(a:Address),
(a)-[r1:has_state]-(s:State)
WHERE p.name =~ ".*Joe.*" AND a.street = ".*Main St.*"
WITH COLLECT(r) + COLLECT(r1) as rels
UNWIND rels AS rel
RETURN startNode(rel) AS start, type(rel) AS type, endNode(rel) as end

Resources