Neo4j/Cypher: How to exclude/include nodes with multiple properties? - neo4j

I have a collection of nodes with multiple properties, for example:
(:Item {id: 1, type: 10})
(:Item {id: 2, type: 10})
(:Item {id: 1, type: 11})
(:Item {id: 1, type: 13})
(:Item {id: 2, type: 11})
In other examples, I can include nodes using a single value, using a query like this (with the IN clause):
MATCH (x:Item)
WHERE x.type IN [10,13]
RETURN x
Im trying to include/exclude nodes with certain properties, something like the next query:
MATCH (x:Item)
WHERE x IN [({id: 1, type: 10}), ({id: 2, type: 11})]
RETURN x
with the above query i don't get an error, but i don't get any results either. Could it be possible so that it includes or excludes certain nodes? In the above example could be more easy do 2-MATCHs for the 2 nodes, but in the real problem i want to include/exclude more than 10 nodes dynamically.

x in your example is much more that just a map with type and id, it is an identifier that represents the complete node.
If you rewrite x as a map of x.id and x.type then you can make that comparison.
MATCH (x:Item)
WHERE {id: x.id, type: x.type} IN [({id: 1, type: 10}), ({id: 2, type: 11})]
RETURN x

Related

How to return a list of related nodes for each node in Cypher

I would like to write a query for the graph below, which should return three lists, each list containing all persons working for the same person.
Given the graph below, the result should be this three lists:
[Fritz]
[Pepe]
[Susy, Peter]
I fail to write such query. My query returns all employes in one list.
The following statements create the graph model for the example I have given:
MATCH (c:Example) DETACH DELETE c;
CREATE (p1:Parent:Example {id: 1, name: 'Andy', title: 'Developer'});
CREATE (p2:Parent:Example {id: 2, name: 'Lila', title: 'Developer'});
CREATE (p3:Parent:Example {id: 3, name: 'Lula', title: 'Developer'});
CREATE (c11:Child:Example {id: 11, name: 'Peter', title: 'Developer'});
CREATE (c12:Child:Example {id: 12, name: 'Susy', title: 'Developer'});
CREATE (c21:Child:Example {id: 21, name: 'Fritz', title: 'Developer'});
CREATE (c31:Child:Example {id: 31, name: 'Pepe', title: 'Developer'});
MATCH (p {id: 1}), (c {id: 11}) MERGE (p)<-[:WORKS_FOR]-(c);
MATCH (p {id: 1}), (c {id: 12}) MERGE (p)<-[:WORKS_FOR]-(c);
MATCH (p {id: 2}), (c {id: 21}) MERGE (p)<-[:WORKS_FOR]-(c);
MATCH (p {id: 3}), (c {id: 31}) MERGE (p)<-[:WORKS_FOR]-(c);
It's relatively straightforward with Cypher
MATCH (c)-[:WORKS_FOR]->(p)
RETURN p.name AS boss, collect(c.name) AS coWorkers
Result
╒══════╤════════════════╕
│"boss"│"coWorkers" │
╞══════╪════════════════╡
│"Andy"│["Peter","Susy"]│
├──────┼────────────────┤
│"Lila"│["Fritz"] │
├──────┼────────────────┤
│"Lula"│["Pepe"] │
└──────┴────────────────┘
The trick is understanding aggregations https://neo4j.com/docs/cypher-manual/current/functions/aggregating/#grouping-keys

Neo4j console : Type mismatch: expected Collection<T> but was Node

I have a query that doesn't work. The error is:
Type mismatch: expected Collection but was Node coming from FOREACH..
Code:
MATCH (user:User {user_role: 'Customer'})
WITH user
OPTIONAL MATCH (user)-[hv:HAS_VAUCHER]->(v:Vaucher {status: 2})
WITH user, count(hv) as hv
WHERE hv = 0
WITH user
FOREACH (u IN user | CREATE (v:Vaucher {discount: 5, created_at: 1488531600, start_at: 1488531600, type: 'March', status: 2})<-[:HAS_VAUCHER]-(u))
I don't know where the error is.
While there are some improvements one could make to the query, here's a quick fix, dropping FOREACH since you don't have users in a collection, and you already have one user per row:
MATCH (user:User {user_role: 'Customer'})
WITH user
OPTIONAL MATCH (user)-[hv:HAS_VAUCHER]->(v:Vaucher {status: 2})
WITH user, count(hv) as hv
WHERE hv = 0
WITH user
CREATE (v:Vaucher {discount: 5, created_at: 1488531600, start_at: 1488531600, type: 'March', status: 2})<-[:HAS_VAUCHER]-(user)
I made a further research and the way to do it was to create a collection:
MATCH (user:User {user_role: 'Customer'})
WITH user
OPTIONAL MATCH (user)-[hv:HAS_VAUCHER]->(v:Vaucher {status: 2})
WITH user, count(hv) as hv
WHERE hv = 0
WITH user, COLLECT(user) AS users
FOREACH (u IN users | CREATE (v:Vaucher {discount: 5, created_at: 1488531600, start_at: 1488531600, type: 'March', status: 2})<-[:HAS_VAUCHER]-(u))
the "user" when returned returns N nodes (not 1) but when passed to FOREACH it maybe takes only the first node.
you can shorten the filtering part with a WHERE NOT clause and simplify query like this
MATCH (user:User {user_role: 'Customer'})
where not (user)-[:HAS_VAUCHER]->(:Vaucher {status: 2})
CREATE (v:Vaucher {discount: 5, created_at: 1488531600,
start_at: 1488531600, type: 'March', status: 2})<-[:HAS_VAUCHER]-(u)

neo4j: What is the syntax to set cypher query parameters in the browser interface?

I am trying to run queries from the neo4j browser to reproduce results from my neo4j-javascript-driver client.
What is the syntax for defining query parameters in the neo4j b
I recently attended a neo4j training session in NYC where the trainer (David Fauth) did this, unfortunately, I did not take notes on it, since I figured that I could read-up on this online...but no success.
In neo4j-browser you need type for example:
:params {nodes: [{name: "John", age: 18}, {name: "Phill", age: 23}]}
Then you can use params as usual:
UNWIND {nodes} as node
MERGE (A:User {name: node.name, age: node.age})
RETURN A
For clear params in neo4j-browser type :params {}.
For additional help type :help params.
In Neo4j-3.3.4, the cypher likes this:
:param nodes: [{name: 'John', age: 18}, {name: 'Phill', age: 23}]
Neo4j Browser result:
here
In Neo4j Browser 3.5+ you can use the Cypher Shell parameter syntax, documented here: https://neo4j.com/docs/operations-manual/3.5/tools/cypher-shell/#cypher-shell-parameters
:param name => expression
The expression must be kept on a single line.
The expression could be a scalar or a list:
:param foo => ['a', 'b', 'c']
Maps can't be used directly with this syntax as of Neo4j 4.1. You can wrap them into a list:
:param foo => [{name: 'Alice', age: 38, address: {city: 'London', residential: true}}]
Or you can use :params:
:params {foo: {name: 'Alice', age: 38, address: {city: 'London', residential: true}}}
In Neo4j Browser 3.5+ you can use
:params param_name => 'param_value'
The basic cases:
Set singe variable value mytext="Hello": :param mytext => "Hello"
set dictionary values attr={"oid":1, "text":"Hello"}:
:param attr => ({oid: 1, text:"Hello"})
Cypher usage:
MATCH (x) WHERE x.a = $mytext RETURN x
set a value MATCH (x) WHERE x.a = $attr.oid SET x.b = $attr.text
set multiple values MATCH (x) WHERE ... SET x = $attr

Active record: filter on distinct value

I have several entries in a table in my RoR app:
{id: 1, item_id: a, ...}
{id: 2, item_id: a, ...}
{id: 3, item_id: a, ...}
{id: 4, item_id: b, ...}
{id: 5, item_id: b, ...}
{id: 6, item_id: c, ...}
{id: 7, item_id: d, ...}
{id: 8, item_id: d, ...}
I would like to create a query that filters on distinct item_id and returns the first matching entry, so that the result would be
{id: 1, item_id: a, ...}
{id: 4, item_id: b, ...}
{id: 6, item_id: c, ...}
{id: 7, item_id: d, ...}
When using
select(:item_id).distinct
I cannot access the other attributes of the entries. How can this be accomplished?
If you just want to get the lowest id for every item_id, you could use an aggregate query to retrieve these ids:
grouped_with_min_id = Model.group(:item_id).minimum(:id)
rows = Model.find(grouped_with_min_id.map(&:id))
This may not be the most efficient solution as it uses two queries. But other methods will probably require you to use Model.find_by_sql and write your own sql.
Disclaimer: I did not try that out.

Chaining result with "WITH" doesn't work when the subsequent query doesn't have matched result in Neo4j cypher query

For example, I created two linked nodes:
create (a:ACTOR {id: "a1", name: "bruce wellis"})
create (m:MOVIE {id: "m1", title: "die hardest"})
create (a)-[:ACTED_IN]->(m)
1. From this cypher query:
match (a:ACTOR {id: "a1"})
with a
optional match (m:MOVIE {id: "m1"})
set m += {
title: "die easier"
}
return a;
I can have result:
+-----------------------------------------+
| a |
+-----------------------------------------+
| Node[1000]{name:"bruce wellis",id:"a1"} |
+-----------------------------------------+
1 row
Properties set: 1
The query successfully returned the actor node.
2. (UPDATED) But if you make the match MOVIE subquery failed:
match (a:ACTOR {id: "a1"})
with a
optional match (m:MOVIE {id: "mm"})
set m += {
title: "die easier"
}
return a;
I got error:
CypherTypeException: Expected m to be a node or a relationship, but it was :`null`.
How to make the second query returning matched actor result?
A MATCH that fails to match anything will always return no rows.
So, in #2, since the second MATCH failed, it returns no rows.
You could use OPTIONAL MATCH in place of the second MATCH, and you should see results.
[EDITED]
For the Updated question, this (somewhat ugly) workaround should work:
MATCH (a:ACTOR {id: "a1"})
WITH a
OPTIONAL MATCH (m:MOVIE {id: "mm"})
WITH a, COLLECT(m) AS cm
FOREACH(m IN cm | SET m += {title: "die easier"})
RETURN a;

Resources