Aggregate over a list of matching nodes - neo4j

I have a Cypher query which I'd like to expand to be summed up over a list of matching nodes.
My query looks like this:
MATCH (u:User {name: {input} })-[r:USES]-(t) RETURN SUM(t.weight)
This matches one User node, and I'd like to adjust it to match a list of User nodes and then perform the aggregation.
My current implementation just calls the query in a loop and performs the aggregation outside of Cypher. This results in slightly inaccurate results and a lot of API calls.
Is there a way to evaluate the Cypher query against a list of elements (strings in my case)?
I'm using Neo4j 2.1 or 2.2.
Cheers

You can use the IN operator and pass in an array of usernames:
MATCH (u:User)-[r:USES]-(t)
WHERE u.name in ['John','Jim','Jack']
RETURN u.name, SUM(t.weight)
Instead of the array here you can use an parameter holding and array value as well:
MATCH (u:User)-[r:USES]-(t)
WHERE u.name in {userNames}
RETURN u.name, SUM(t.weight)
userNames = ['John','Jim','Jack']

Related

How to show relationships in an optional manner in neo4j?

I have multiple nodes and relationships in neo4j, certain nodes have relationship depth as 4, while certain have 2. I'm using neo4j's HTTP API to get the data in graph format
Sample query:
MATCH p= (n:datasource{resource_key:'ABCD'})-[:is_dataset_of]-(c:dataset)-[q]-(v:dataset_columns)-[s]-(b:component)-[w]-(e:dashboard) return p
If i use this query then i can get output if this exact relationship is present but I also want to get the output if the 2nd relationship is not available, Any pointers on how to achieve this?
Here is one way:
MATCH p = (:person1 {hobby: 'gamer'})-[:knows]-(:person2)
RETURN p
UNION ALL
MATCH p = (:person1 {hobby: 'gamer'})-[:knows]-(:person2)--(:person3)
RETURN p
The UNION clause combines the results of 2 queries. And the ALL option tells UNION to not bother to remove duplicate results (since the 2 subqueries will never produce the same paths).
If you really want the path to be returned, you can do something along these lines, using apoc (https://neo4j-contrib.github.io/neo4j-apoc-procedures/3.4/nodes-relationships/path-functions/)
MATCH requiredPath=(n)-[r]->(m)
OPTIONAL MATCH optionalPath = (m)-[q]->(s)
RETURN apoc.path.combine(requiredPath,optionalPath) AS p

Cypher "IN" operator

The second query gives a list of customerkeys. I want that result to be used on the fist query as long as I only want to match the customers with the keys that are on that list. In SQL there is a IN operator. What about in Cypher?
MATCH (s:Sale)-[:ORDERED_BY]->(c:Customer)
WHERE c.customerKey IN ******
RETURN c.name, SUM(s.orderQuantity)
MATCH (s:Sale)-[:CUSTOMER]->(c:Customer)
WITH SUM(s.orderQuantity) as qtt, c
WHERE qtt>1
RETURN c.customerKey
There is an IN operator that works on lists, and there is a COLLECT() command that changes rows into a list.
That said, I don't think you need to do this step at all. You should be able to combine the two queries and just use the customers resulting from the second query to feed into the first query like so:
MATCH (s:Sale)-[:CUSTOMER]->(c:Customer)
WITH SUM(s.orderQuantity) as qtt, c
WHERE qtt>1
// now pass the filtered list of customers to the first query
// no need to do any additional filtering or even deal with customerKey
WITH c
MATCH (s:Sale)-[:ORDERED_BY]->(c)
RETURN c.name, SUM(s.orderQuantity)

Grouping Sequential items in cypher

So here's the deal, I'm using Neo4J 3.01 and I have a graph with nodes of type Action which have amongst other links and properties the following two properties:
Type: [Comment,Reply,Vote etc]
Date:[epoch timestamp]
I am attempting to run a cypher query that returns a sorted list of nodes ordered by the date field but collapsing (Collect?) sequential items of the same type
So for the following nodes:
{type:'Comment',date:1}
{type:'Comment',date:2}
{type:'Vote',date:3}
{type:'Comment',date:4}
{type:'Comment',date:5}
{type:'Reply',date:6}
{type:'Reply',date:7}
{type:'Vote',date:8}
{type:'Vote',date:9}
I would hope to get something like:
{type:'Comment',actions:[{type:'Comment',date:1},{type:'Comment',date:2}]}
{type:'Vote',actions:[{type:'Vote',date:3}]}
{type:'Comment',actions:[{type:'Comment',date:4},{type:'Comment',date:5}]}
{type:'Reply',actions:[{type:'Reply',date:6},{type:'Reply',date:7}]}
{type:'Vote',actions:[{type:'Vote',date:8},{type:'Vote',date:9}]}
I attempted a simple collect and order cypher query:
Match (a:Action)
with a.type as type, a order by a.date limit 20
return type, collect(a) as actions
but this seems to collect each type in its own group regardless of the sequence, so the actual result is something like:
{type:'Comment',actions:[{type:'Comment',date:1},{type:'Comment',date:2},{type:'Comment',date:4},{type:'Comment',date:5}]}
{type:'Vote',actions:[{type:'Vote',date:3},{type:'Vote',date:8},{type:'Vote',date:9}]}
{type:'Reply',actions:[{type:'Reply',date:6},{type:'Reply',date:7}]}

Limit the results of a union cypher query

Let's say we have the example query from the documentation:
MATCH (n:Actor)
RETURN n.name AS name
UNION
MATCH (n:Movie)
RETURN n.title AS name
I know that if I do that:
MATCH (n:Actor)
RETURN n.name AS name
LIMIT 5
UNION
MATCH (n:Movie)
RETURN n.title AS name
LIMIT 5
I can reduce the returned results of each sub query to 5.How can I LIMIT the total results of the union query?
This is not yet possible, but there is already an open neo4j issue that requests the ability to do post-UNION processing, which includes what you are asking about. You can add a comment to that neo4j issue if you support having it resolved.
This can be done using UNION post processing by rewriting the query using the COLLECT function and the UNWIND clause.
First we turn the columns of a result into a map (struct, hash, dictionary), to retain its structure. For each partial query we use the COLLECT to aggregate these maps into a list, which also reduces our row count (cardinality) to one (1) for the following MATCH. Combining the lists is a simple list concatenation with the “+” operator.
Once we have the complete list, we use UNWIND to transform it back into rows of maps. After this, we use the WITH clause to deconstruct the maps into columns again and perform operations like sorting, pagination, filtering or any other aggregation or operation.
The rewritten query will be as below:
MATCH (n:Actor)
with collect ({name: n.title}) as row
MATCH (n:Movie)
with row + collect({name: n.title}) as rows
unwind rows as row
with row.name as name
return name LIMIT 5
This is possible in 4.0.0
CALL {
MATCH (p:Person) RETURN p
UNION
MATCH (p:Person) RETURN p
}
RETURN p.name, p.age ORDER BY p.name
Read more about Post-union processing here https://neo4j.com/docs/cypher-manual/4.0/clauses/call-subquery/

Neo4j Cypher: control order of MATCH based on input array?

I'm writing a query that iterates over some set of nodes:
MATCH (foo:Bar)
WHERE foo.id IN {ids}
Is there any way for me to specify/require/ensure that the matched nodes be in the same order as the input ids array?
As an analogy, with Neo4j 1.x, this was the behavior with native ID lookups:
START foo=node({ids})
For trivial cases, I can always re-order the results on the client myself. In this particular case, my query does some additional processing, and I want to make sure the nodes get processed in the right order.
Here's a console example to experiment with:
http://console.neo4j.org/r/l5oouj
Thanks!
UNWIND to the rescue!
UNWIND {ids} AS id
MATCH (foo:Bar {id: id})
http://console.neo4j.org/r/vit7c8

Resources