Regular expression on the string of a property name in Cypher - neo4j

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}│
└─────────────────────┘

Related

question about "neo4j query" (combine two result)

I'm using neo4j and cypher.
and have a question about cypher.
there are my results.
result 1)
MATCH (f:Fruit)-[r0]-(a:Animal)-[r1]-(h:Human)-[r2]-(p:Plant) where p.type = 'Flower' RETURN *;
result 2)
MATCH (p:Plant)-[r3]-(t:Threat) where p.type = 'Flower' RETURN *;
-->
There are relationship and graph are drawn.
but, I want combine two result.
Is there any way?
Thank you, 🤦‍♂️
Yes, there is a way to combine results.
UNION ALL
https://neo4j.com/docs/cypher-manual/current/clauses/union/
then to remove any duplicates; use UNION (without ALL)
In your example:
MATCH (f:Fruit)-[r0]-(a:Animal)-[r1]-(h:Human)-[r2]-(p:Plant)
where p.type = 'Flower'
RETURN p, null as t
UNION ALL
MATCH (p:Plant)-[r3]-(t:Threat)
where p.type = 'Flower'
RETURN p, t
Sample Result:
╒══════════╤════════════════╕
│"p" │"t" │
╞══════════╪════════════════╡
│{"name":3}│null │
├──────────┼────────────────┤
│{"name":3}│{"id":"some_id"}│
└──────────┴────────────────┘
All columns that you return must have the same name so I made a hack on naming "null" as t.

How could I use a WHERE in an EXISTS() in Cypher

I am trying to find all path in a graph between two nodes, with the condition that any node in this path must be connected to another node with a label TIMESWITCH and valid endDate and startDate.
The purpose of the TIMESWITCH nodes is to indicate that the connected node can be used only for certain time ranges.
So I used this Cypher query :
MATCH p=(:MYNODE {myid:'node1'}) -[*]- (:MYNODE {myid:'node2'})
WHERE ALL(n in nodes(p) WHERE EXISTS(
(n)<--(ts:TIMESWITCH)
WHERE ts.startDate < datetime("2018-10-01T00:00:00") <= ts.endDate ))
RETURN p
But this query is incorrect because the WHERE clause is forbidden inside an EXISTS. That simply means that inside an exists, one can test equality but not inequality.
So I don't know what to do ... how am I supposed to write that exists ?
You should be able to use a pattern comprehension as a workaround, as pattern comprehensions allow you to introduce new variables for the comprehension and filter with a WHERE clause. You'll just need to check the size of the comprehension to ensure it's not empty.
MATCH p=(:MYNODE {myid:'node1'}) -[*]- (:MYNODE {myid:'node2'})
WHERE ALL(n in nodes(p) WHERE size([(n)<--(ts:TIMESWITCH)
WHERE ts.startDate < datetime("2018-10-01T00:00:00") <= ts.endDate | ts]) > 0)
RETURN p

Cypher: Quantifying over zero or more node-then-relations

I want to return all nodes a and b, where b is not downstream of a via any path that begins with relation rel. I keep finding myself having to write one condition for the case where a is linked directly to b via rel, and one for the indirect case, leading to something like this:
//Semi-pseudo-code.
match (a)-[*]->(b)
optional match dir=(a)-[:rel]->(b)
optional match indir=(a)-[:rel]-()-[*]->(b)
where length(dir)=0
and length(indir)=0
return a,b
Is there any easier way? Really I want something like this, where the bare quantifier means "zero or more nodes-then-relations":
match (a)-[*]->(b)
match not (a)-[:rel]-*->(b)
return a,b
Note: I suspect this may at root be the same as my last question: Cypher: Matching nodes at arbitrary depth via a strictly alternating set of relations
We can use WHERE NOT to formulate negative conditions, in a similar fashion to your second semi-pseudocode:
MATCH (a)-[*]->(b)
WHERE NOT ((a)-[:rel]->()-[*1..]->(b))
RETURN a, b
Of course, this will be anything but efficient, so you should at least try to restrict the labels of a and b and the relationships between them, e.g. (a:Label1)-[:rel1|rel2*]->(b:Label2)
An example:
CREATE
(n1:N {name: "n1"}),
(n2:N {name: "n2"}),
(n3:N {name: "n3"}),
(n4:N {name: "n4"}),
(n5:N {name: "n5"}),
(n1)-[:x]->(n2),
(n3)-[:rel]->(n4),
(n4)-[:x]->(n5)
The query results in:
╒══════════╤══════════╕
│a │b │
╞══════════╪══════════╡
│{name: n1}│{name: n2}│
├──────────┼──────────┤
│{name: n4}│{name: n5}│
└──────────┴──────────┘
As you can see, it does not include n3 and n5, as it starts with a :rel relationship.
This should work:
MATCH (a)-[rs*]->(b)
WHERE TYPE(rs[0]) <> 'rel'
RETURN a, b;
However, the query below should be much more performant, as it filters out all unwanted path beginnings before it does the very expensive variable-length path search. The *0.. syntax makes the variable-length search use a lower bound of 0 for the length (so x will also be returnable as b).
MATCH (a)-[r]->(x)
WHERE TYPE(r) <> 'rel'
MATCH (x)-[*0..]->(b)
RETURN a, b;

Comparision operator with in properies of query of Neo4j

I am in need of get data of whether there is no relation exists between two labels and condition based data on one of labels. I found an answer following ::
MATCH (n:Label1)
WHERE NOT (n)-[:REL_1]-(:Label2)
OR (n)-[:REL_1]-(e:Label2 {id:1})
RETURN count(DISTINCT(n))
But What I need is like all of id>=5 data should come in result
If I perform a query like ::
MATCH (n:Label1)
WHERE NOT (n)-[:REL_1]-(:Label2)
OR (n)-[:REL_1]-(e:Label2)
WHERE e.id >= 5
RETURN count(DISTINCT(n))
It is producing error ::
Invalid input 'H': expected 'i/I' (line 1, column 94 (offset: 93))
[UPDATED]
A Cypher query cannot have 2 WHERE clauses in a row. In addition, you have not defined the e identifier.
This query should work (I assume you only want to count n if it does not have such a relationship OR if it has at least one in which e.id is at least 5):
MATCH (n:Label1)
OPTIONAL MATCH (n)-[:REL_1]-(e:Label2)
WITH n, e
WHERE e IS NULL OR e.id >= 5
RETURN count(DISTINCT n);
You can nest WHERE filters, you just have to give up syntactic sugar to do it.
MATCH (n:Label1)
WHERE ALL(p IN (n) - [:REL_1] - (:Label2) WHERE LAST(NODES(p))['id'] >= 5)
RETURN COUNT(n)
You reconstruct any match-filter construct you can dream of with the ANY, ALL, NONE toolset, which allow you to apply filters internally as well and nest the iterable component (between IN and `WHERE) to multiple depths.

Neo4j indices slow when querying across 2 labels

I've got a graph where each node has label either A or B, and an index on the id property for each label:
CREATE INDEX ON :A(id);
CREATE INDEX ON :B(id);
In this graph, I want to find the node(s) with id "42", but I don't know a-priori the label. To do this I am executing the following query:
MATCH (n {id:"42"}) WHERE (n:A OR n:B) RETURN n;
But this query takes 6 seconds to complete. However, doing either of:
MATCH (n:A {id:"42"}) RETURN n;
MATCH (n:B {id:"42"}) RETURN n;
Takes only ~10ms.
Am I not formulating my query correctly? What is the right way to formulate it so that it takes advantage of the installed indices?
Here is one way to use both indices. result will be a collection of matching nodes.
OPTIONAL MATCH (a:B {id:"42"})
OPTIONAL MATCH (b:A {id:"42"})
RETURN
(CASE WHEN a IS NULL THEN [] ELSE [a] END) +
(CASE WHEN b IS NULL THEN [] ELSE [b] END)
AS result;
You should use PROFILE to verify that the execution plan for your neo4j environment uses the NodeIndexSeek operation for both OPTIONAL MATCH clauses. If not, you can use the USING INDEX clause to give a hint to Cypher.
You should use UNION to make sure that both indexes are used. In your question you almost had the answer.
MATCH (n:A {id:"42"}) RETURN n
UNION
MATCH (n:B {id:"42"}) RETURN n
;
This will work. To check your query use profile or explain before your query statement to check if the indexes are used .
Indexes are formed and and used via a node label and property, and to use them you need to form your query the same way. That means queries w/out a label will scan all nodes with the results you got.

Resources