MATCH (s:Product {id:'5001207'})-[r]-> (o) WHERE (o:ProdAttrs) AND any(key in keys(o) WHERE toLower(key) contains 'Network') return o
In this query, if I want to express "contains Network|Phone|Smart", how to modify the query? That is the logic 'OR' in value.
Edited:
I tested this but it doesn't work:
MATCH (s:Product {id:'5001207'})-[r]-> (o)
WHERE (o:Attrs OR o:ExAttrs) AND any(key in keys(o)
WHERE key =~ '(?!).*(network|phone).*')
return o
The non-RE version works. Any errors? There is no syntax error. It just doesn't return result as expected from the .
EDIT 2:
My function to generate the query:
public String query_partial_match(String skuid, List<String> attrKey) {
return "MATCH (s:Product {id:'" + skuid + "'})-[r]-> (o) " +
"WHERE " +
"(o:ExAttrs OR o:ProdAttrs) AND " +
"any(key in keys(o) WHERE key =~'(?i).*(" + attrKey + ").*') " +
"return o";
}
This is my Java method to generate the query. So how to use the Java list 'attrKey' in this query?
Just use OR in your WHERE clause. Might be helpful to work with the list of all lowercase props.
MATCH (s:Product {id:'5001207'})-[r]->(o:ProdAttrs)
WHERE any(key in [prop in keys(o) | toLower(prop)] WHERE key contains 'network' OR key contains 'phone' OR key contains 'smart')
RETURN o
One option is to use a regular expression to test for multiple alternate string values.
MATCH (s:Product {id:'5001207'})-->(o:ProdAttrs)
WHERE ANY(key in KEYS(o) WHERE key =~ '(?i).*(Network|Phone|Smart).*')
RETURN o;
(?i) enables case-insensitivity.
Also, if you pass in a parameter (say, $terms) containing the collection of target strings (e.g., ['Network', 'Phone', 'Smart']), the query can generate the corresponding regular expression. For example:
MATCH (s:Product {id:'5001207'})-->(o:ProdAttrs)
WHERE ANY(key in KEYS(o) WHERE
key =~ '(?i).*(' + REDUCE(s=$terms[0], t IN $terms[1..] | s + '|' + t) + ').*')
RETURN o;
Related
I'm trying to implement follow/unfollow in Neo4J. I would like to write a query would toggle the relationship between two nodes.
I currently have the following query:
neoSession.writeTransaction(tx => tx.run('MATCH (me:User), (other:User) WHERE ID(me) = $me AND ID(other) = $other OPTIONAL MATCH (me)-[af:FOLLOWS]->(other) CALL apoc.do.when(af IS NULL, CREATE (me)-[f:FOLLOWS]->(other), DELETE af)', { me: req.user_id, other: req.body.user, datetime: Date.now() }));
Prettified query-only:
MATCH (me:User), (other:User)
WHERE ID(me) = $me AND ID(other) = $other
OPTIONAL MATCH (me)-[af:FOLLOWS]->(other)
CALL
apoc.do.when(
af IS NULL,
CREATE (me)-[f:FOLLOWS]->(other),
DELETE af
)
But this results in the error
Neo4jError: Invalid input '>' (line 1, column 169 (offset: 168))
"MATCH (me:User), (other:User) WHERE ID(me) = $me AND ID(other) = $other OPTIONAL MATCH (me)-[af:FOLLOWS]->(other) CALL apoc.do.when(af IS NULL, CREATE (me)-[f:FOLLOWS]->(other), DELETE af)"
The queries (last two arguments) to apoc.do.when() have to be strings, so quote each of them.
Also, in order for each of those queries to use those variables, you need to pass those variables in a parameter map as a 4th argument.
Each of the conditional queries must RETURN something, otherwise there will be no rows yielded and anything after would be a no-op.
The call must YIELD value, so that needs to be present, and last, a query cannot end with a procedure call, so you need to RETURN something.
This one should work, you can adjust it as needed:
MATCH (me:User), (other:User)
WHERE ID(me) = $me AND ID(other) = $other
OPTIONAL MATCH (me)-[af:FOLLOWS]->(other)
CALL
apoc.do.when(
af IS NULL,
"CREATE (me)-[f:FOLLOWS]->(other) RETURN f",
"DELETE af RETURN null as f",
{me:me, af:af}
) YIELD value
RETURN value.f as f
String query = "START n1=node:TEST_INDEX(nodeName='" + elementID
+ "') match n1-[:hasSubClass]->n2-[:hasPropertyGroup]->n3-[:containsProperty]->n4 with n2 match n2-[:hasInstance]->n5 "
+ "where n2.elementType='class' and not(n4.status! = 'Deleted') and n4.nodeName! =~ '(?i)" + propertyName + "' and where count(n5)=0 return n4,count(n5)";
I am trying to convert this particular query to MATCH query facing issue in understanding these conditions
not(n4.status! = 'Deleted') and n4.nodeName! =~ '(?i)" + propertyName
+ "'
I tried to change the query :-
MATCH(n1:TESTDATA{nodeName:'ProductConcept'})
match (n1)-[:hasSubClass]->(n2)-[:hasPropertyGroup]->(n3)-[:containsProperty]->(n4) with n2,n4
match (n2)-[:hasInstance]->(n5)
where n2.elementType='class'
and NOT EXISTS(n4.status)
and n4.nodeName <>'(?i)view'
//and where count(n5)=0
return n4,count(n5)
Assuming you pass elementId and propertyName as parameters, this Cypher seems to be equivalent to the intent of your original query:
START n1=node:TEST_INDEX(nodeName=$elementId)
MATCH (n1)-[:hasSubClass]->(n2)-[:hasPropertyGroup]->()-[:containsProperty]->(n4)
WHERE
n2.elementType = 'class' AND
n4.status = 'Deleted' AND
(NOT n4.nodeName =~ ('(?i)' + $propertyName)) AND
SIZE((n2)-[:hasInstance]->()) = 0
RETURN n4
This query does not bother to return the equivalent of COUNT(n5), since the query requires that the value must always be 0.
A string is stored in a node:
match (r:Reply)--(n:TRANS)
return split(toInteger(n.content), " ")
As you can imagine, the 'toInteger' method in the second line is incorrectly positioned - and returns exceptions.
Is there a method in Cypher where one can convert a list of Strings into a list of integers?
Here is an example of how to convert the numbers in a space-separated string to integers (in separate rows):
WITH '123 456 789' AS content
UNWIND SPLIT(content, " ") AS i
RETURN TOINTEGER(i) AS value;
And here is an example of how to get the integers in an array:
WITH '123 456 789' AS content
RETURN [i IN SPLIT(content, " ") | TOINTEGER(i)] AS values;
I have a graph that consists of DATASET and GRAPH nodes. With the following relationships:
DATASET->READS->GRAPH
GRAPH->WRITES->DATASET
When I run the following : MATCH (p1:DATASET_NAME { name:'test1.dat' }),(p3:DATASET_NAME { name:'test32.txt' }), p = ((p1)-[:READS|:WRITES*1..8]->(p3))
RETURN p
In Neo4J Desktop I get a result that is correct, where node names are present. But when I run it in py2neo:
graph.run("MATCH (p1:DATASET_NAME {
name:'test1.dat' }),(p3:DATASET_NAME { name:'test32.txt' }), p = ((p1)-[:READS|:WRITES*1..8]->(p3)) RETURN p").dump()
I get a result in the following format:
(f3ff862)-[:READS]->(c539bdc)-[:WRITES]->(b217f5a)-[:READS]->(ebf9c4f)-[:WRITES]->(f9ddd22)-[:READS]->(fcca016)-[:WRITES]->(a9c241a)
(f3ff862)-[:READS]->(c539bdc)-[:WRITES]->(b217f5a)-[:READS]->(ebf9c4f)-[:WRITES]->(f9ddd22)-[:READS]->(fcca016)-[:WRITES]->(e152f69)-[:READS]->(fcca016)-[:WRITES]->(a9c241a)
(f3ff862)-[:READS]->(c539bdc)-[:WRITES]->(b217f5a)-[:READS]->(ebf9c4f)-[:WRITES]->(cbc5d42)-[:READS]->(fcca016)-[:WRITES]->(a9c241a)
I am assuming that these are some sort of references. Is there a way where I can get the string value for name from these references?
You are returning the matched path so what you see in py2neo is the representation of the path object. In the neo4j console it does a little extra looking up for you and presents the path as a set of nodes and relationships and it labels them according to what you have configured in the console.
If you want to see the names in your py2neo output you could use the reduce function on the returned path p to produce a string with the node names and relationship types. Something like this should get you started.
MATCH (p1:DATASET_NAME { name:'test1.dat' }),(p3:DATASET_NAME { name:'test32.txt' }), p = ((p1)-[:READS|:WRITES*1..8]->(p3))
RETURN head(nodes(p)).name + ' - ' + reduce(path_str = "", r in relationships(p) | path_str + type(r) + ' - ' + endnode(r).name)
In py2neo [Note the escape characters added in order to avoid cypher error. # ...reduce(path_str = \"\"... ]:
graph.run("MATCH (p1:DATASET_NAME { name:'/projects/bkrpty_vfcn/bkrpty_vfcn_vendr/data/serial/temp/yyyymmdd_yyyymmddhhmiss_bk_mrk_stat_init.dat' }),(p3:DATASET_NAME { name:'/projects/bkrpty_vfcn/bkrpty_vfcn_vendr/tables/onevgb1/ai_bkrpty_case' }), p = ((p1)-[:READS|:WRITES*1..8]->(p3)) RETURN head(nodes(p)).name + ' - ' + reduce(path_str = \"\", r in relationships(p) | path_str + type(r) + ' - ' + endnode(r).name)").dump()
This is in continuation of Neo4j: Listing node labels
I am constructing a dynamic MATCH statement to return the hierarchy structure & use the output as a Neo4j JDBC input to query the data from a java method:
MATCH p=(:Service)<-[*]-(:Anomaly)
WITH head(nodes(p)) AS Service, p, count(p) AS cnt
RETURN DISTINCT Service.company_id, Service.company_site_id,
"MATCH srvhier=(" +
reduce(labels = "", n IN nodes(p) | labels + labels(n)[0] +
"<-[:BELONGS_TO]-") + ") WHERE Service.company_id = {1} AND
Service.company_site_id = {2} AND Anomaly.name={3} RETURN " +
reduce(labels = "", n IN nodes(p) | labels + labels(n)[0] + ".name,");
The output is as follows:
MATCH srvhier=(Service<-[:BELONGS_TO]-Category<-[:BELONGS_TO]-SubService<-
[:BELONGS_TO]-Assets<-[:BELONGS_TO]-Anomaly<-[:BELONGS_TO]-) WHERE
Service.company_id = {1} and Service.company_site_id = {21} and
Anomaly.name={3} RETURN Service.name, Category.name, SubService.name,
Assets.name, Anomaly.name,
The problem I am seeing:
The "BELONGS_TO" gets appended to my last node
Line 2: Assets<-[:BELONGS_TO]-Anomaly**<-[:BELONGS_TO]-**
Are there string functions (I have looked at Substring..) that can be used to remove it? Or can I use a CASE statement with condition n=cnt to append "BELONGS_TO"?
The same problem persists with my last line:
Line 5: Assets.name,Anomaly.name**,** - the additional "," that I need to eliminate.
Thanks.
I think you need to introduce a case statement into the reduce clause something like this snippet below. If the node isn't the last element of the collection then append the "<-[:BELONGS_TO]-" relationship. If it is the last element then don't append it.
...
reduce(labels = "", n IN nodes(p) |
CASE
WHEN n <> nodes(p)[length(nodes(p))-1] THEN
labels + labels(n)[0] + "<-[:BELONGS_TO]-"
ELSE
labels + labels(n)[0]
END
...
Cypher has a substring function that works basically like you'd expect. An example: here's how you'd return everything but the last three characters of a string:
return substring("hello", 0, length("hello")-3);
(That returns "he")
So you could use substring to trim the last separator off of your query that you don't want.
But I don't understand why you're building your query in such a complex way; you're using cypher to write cypher (which is OK) but (and I don't understand your data model 100%) it seems to me like there's probably an easier way to write this query.