Foreach array - Neo4j - foreach

I have this query and i have a problem with FOREACH:
MATCH (pc:car), (pa:parts)
With pc.car as CAR, collect(pa.name_parts) as parts_total
Foreach (i IN parts_total in CASE WHEN not (:car)-[:related_to5]->(:parts) is null then [1] else [0] END | Match (parts_total) Return parts_total)
Return *
i would like to have the value "0" for the elements of the arrays not connected to "car" and "1" for the elements instead connected.

That query has numerous syntax errors. For example, you cannot have other clauses (like MATCH) in a FOREACH clause.
This should do what you want, fairly efficiently:
MATCH (pa:parts)
WITH COLLECT(pa) AS ps
MATCH (car:car)-[:related_to5]->(p:parts)
WITH ps, car, COLLECT (p) AS partsInCar
RETURN car,
REDUCE (s = [], i IN ps |
s + {part: i, value: CASE WHEN i IN partsInCar THEN 1 ELSE 0 END}) AS result

Related

How to continue executing a Neo4J Cypher request after MATCH is not matched?

I have the following params in my Neo4J:
{
"lists": [
{
"from": "someone",
"to": "somebody"
}
]
}
And the following query:
MATCH (c:Concept{name:'infranodus'}) WITH c, $lists AS list
UNWIND CASE WHEN list = [{}] THEN [null] ELSE list END AS l
WITH l
MATCH (cp1:Concept{name:l.from})
WITH cp1
MATCH (cp2:Concept{name:'somebody'})
RETURN cp1,cp2;
The query above will work.
However, if I replace l.from with a non-existent parameter, e.g. l.about, then — as the match doesn't happen — the second cp2 match doesn't fire.
How can I change this behavior and continue executing this query even if cp1 is not found? Maybe there's a way to pass on a dummy variable as a result?
MATCH (c:Concept{name:'infranodus'}) WITH c, $lists AS list
UNWIND CASE WHEN list = [{}] THEN [null] ELSE list END AS l
WITH l
MATCH (cp1:Concept{name:l.about})
WITH cp1
MATCH (cp2:Concept{name:'somebody'})
RETURN cp1,cp2;
Use OPTIONAL MATCH. If there is no match is found, then it will use NULL for the missing part of the pattern. It is similar to outer join in SQL.
NEW:
OPTIONAL MATCH (cp1:Concept{name:l.about})
OLD:
MATCH (cp1:Concept{name:l.about})
You can maybe replace it with an IN predicate ?
For eg :
WITH {from: 'Matt Olg', about: 'Matthew Olg'}
AS l
MATCH (n:Person)
WHERE n.name IN [l.from, l.to]
RETURN n.name
╒══════════╕
│"n.name" │
╞══════════╡
│"Matt Olg"│
└──────────┘

Cypher: Matching nodes in a nested FOREACH

I have a nested FOREACH loop that needs to match a tag to multiple labels.
OPTIONAL MATCH (a: Article {URL: event.URL})
FOREACH(ignoreme in case when a is null then [1] else [] end |
CREATE (a: Article {URL: event.URL})
//other statements ..... //
FOREACH (relation in CASE WHEN event.article.nlp_relations is not null then event.article.nlp_relations else [] end |
match (a)-[:HAS_NLP_TAG]->(t_from) where (t_from:Tag or t_from:Entity) and t_from.value = relation.from.value
match (a)-[:HAS_NLP_TAG]->(t_to) where (t_to:Tag or t_to:Entity) and t_to.value = relation.to.value
call apoc.create.relationship(t_from,relation.type , {}, t_to)
)
)
This does not work because you cannot use a match inside a foreach. I can say that there will always be a node to match as it will be created earlier in the same query. so it will never be null, but I do not know how to express this in this current form. Can anybody help
It looks like you can just reformulate your second FOREACH as a normal MATCH-CREATE combination, something like (I didn't try to run it):
OPTIONAL MATCH (a: Article {URL: event.URL})
FOREACH(ignoreme in case when a is null then [1] else [] end |
CREATE (a: Article {URL: event.URL}
)
//other statements ..... //
unwind Case When event.article.nlp_relations is null then [] else event.article.nlp_relations end as relation
match (a)-[:HAS_NLP_TAG]->(t_from) where (t_from:Tag or t_from:Entity) and t_from.value = relation.from.value
match (a)-[:HAS_NLP_TAG]->(t_to) where (t_to:Tag or t_to:Entity) and t_to.value = relation.to.value
call apoc.create.relationship(t_from,relation.type , {}, t_to)
)
You might still need to insert an appropriate WITH-Statement before the MATCH-statements.

How to execute cypher query within a CASE WHEN THEN clause in Neo4j Cypher

I have a use case where I am trying to optimize my Neo4j db calls and code by using the RETURN CASE WHEN THEN clauses in Cypher to run different queries depending on the WHEN result. This is my example:
MATCH (n {email: 'abc123#abc.com'})
RETURN
CASE WHEN n.category='Owner' THEN MATCH '(n)-[r:OWNS]->(m)'
WHEN n.category='Dealer' THEN MATCH (n)-[r:SUPPLY_PARTS_FOR]->(m)
WHEN n.category='Mechanic' THEN MATCH (n)-[r:SERVICE]-(m) END
AS result;
I am not sure this is legal but this is what I want to achieve. I am getting syntax errors like Invalid input '>'. How can I achieve this in the best manner?
EDIT for possible APOC solution:
This was my plan before discovering the limitation of FOREACH...
MATCH (user:Person {email:{paramEmail}})
FOREACH (_ IN case when 'Owner' = {paramCategory} then [1] else [] end|
SET user:Owner, user += queryObj
WITH user, {paramVehicles} AS coll
UNWIND coll AS vehicle
MATCH(v:Vehicles {name:vehicle})
CREATE UNIQUE (user)-[r:OWNS {since: timestamp()}]->(v)
SET r += paramVehicleProps
)
FOREACH (_ IN case when 'Mechanic' = {Category} then [1] else [] end|
SET user:Owner, user += queryObj
WITH user, {paramVehicles} AS coll
….
)
FOREACH (_ IN case when 'Dealer' = {paramCategory} then [1] else [] end|
SET user:Owner, user += queryObj
WITH user, {paramVehicles} AS coll
…...
)
RETURN user,
CASE {paramCategory}
WHEN 'Owner' THEN [(n)-[r:OWNS]->(m) | m and r]
WHEN 'Dealer' THEN [(n)-[r:SUPPLY_PARTS_FOR]->(m) | m]
WHEN 'Mechanic' THEN [(n)-[r:SERVICE]-(m) | m]
END AS result`,{
paramQueryObj: queryObj,
paramVehicles: makeVehicleArray,
paramVehicleProps: vehiclePropsArray,
paramSalesAgent: dealerSalesAgentObjarray,
paramWarehouseAgent: dealerWarehouseAgentObjarray
}).....
does anyone know to convert this using apoc.do.when()? note I need 'm' and 'r' in the first THEN.
You should still use a label in your first match, otherwise you get a full database scan and not a index lookup by email!!
for your query you can use pattern comprehensions:
MATCH (n:Person {email: 'abc123#abc.com'})
RETURN
CASE n.category
WHEN 'Owner' THEN [(n)-[r:OWNS]->(m) | m]
WHEN 'Dealer' THEN [(n)-[r:SUPPLY_PARTS_FOR]->(m) | m]
WHEN 'Mechanic' THEN [(n)-[r:SERVICE]-(m) | m] END
AS result;
It is also possible to use apoc.do.case: https://neo4j.com/labs/apoc/4.4/overview/apoc.do/apoc.do.case/
CALL apoc.do.case([
false,
'CREATE (a:Node{name:"A"}) RETURN a AS node',
true,
'CREATE (b:Node{name:"B"}) RETURN b AS node'
],
'CREATE (c:Node{name:"C"}) RETURN c AS node',{})
YIELD value
RETURN value.node AS node;

How to avoid duplications return distinct nodes and the relation ship using neo4j

I would like to return for a given node-id related nodes and their relationships props
For example:
-> defines a bi direction relationship with property timestamp
1234->777
777->1234
1234->999
999->1234
1234->888
888->1234
1234,777,888,999 are node-ids
When I execute this:
final PreparedStatement ps = conn.prepareStatement("start a = node(1234) match (a)-[k:nearby*]->(b) where a<>b return DISTINCT b, k");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Map result = (Map<String, Object>) rs.getObject("b");
System.out.println(result.toString());
}
} catch (SQLException e) {
e.printStackTrace();
logger.error("Error returning userId=" + userIdInput, e);
}
return null;
}
I get:
{userId=777}
{userId=999}
{userId=888}
{userId=888}
{userId=999}
{userId=999}
{userId=777}
{userId=888}
{userId=888}
{userId=777}
{userId=888}
{userId=777}
{userId=999}
{userId=999}
{userId=777}
How I do get distinct results only (777,888,999)
How to retrieve the relationship props of 1234 to the dest node? I expect to get the timestamp prop which defined on each relationship
Thank you,
ray.
I'm not sure what language you're using so I'll focus on the Cypher. Firstly I would replace the START query with a MATCH with a WHERE on ID(a):
MATCH (a)-[k:nearby*]->(b)
WHERE ID(a) = 1234 AND a<>b
RETURN DISTINCT b, k
Secondly I'm pretty sure you don't need the a<>b because Cypher paths won't loop back on the same nodes:
MATCH (a)-[k:nearby*]->(b)
WHERE ID(a) = 1234
RETURN DISTINCT b, k
Lastly, and to your question, I suspect the reason that you're getting duplicates is because you have multiple relationships. If so you can return the result node and an array of the relationships like so:
MATCH (a)-[k:nearby*]->(b)
WHERE ID(a) = 1234
RETURN collect(b), k
That should return you node/relationship objects (with properties on both). Depending on your language/library you might get Maps or you might get objects wrapping the data
If your library doesn't return the start/end nodes for relationships for you, you can do something like this:
MATCH (a)-[k:nearby*]->(b)
WHERE ID(a) = 1234
RETURN collect({rel: b, startnode: startnode(b), endnode: endnode(b)}), k
Hopefully that helps!
You get non distinct results, because you return both b and k
If you only want to get distinct b's use:
MATCH (a)-[k:nearby*]->(b)
WHERE ID(a) = 1234 AND a<>b
RETURN DISTINCT b
You should also use parameters!
MATCH (a)-[k:nearby*]->(b)
WHERE ID(a) = {1} AND a<>b
RETURN DISTINCT b
ps.setInt(1,1234);

Neo4j: Conditional return/IF clause/String manipulation

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.

Resources