Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cypher: Limiting the number of returned links for a selection of nodes

Tags:

neo4j

cypher

I am failing at an apparently simple query.

Algo:

  1. Sort nodes by some value (desc):

    MATCH (n) WHERE has(n.II_VAL) WITH n, n.II_VAL as ShInflInd order by ShInflInd desc
    
  2. For each node, return its direct neighbors (ordered by a second value):

    MATCH (n)-[r]->(m) with n, m, m.VALUE as SubsOpRev order by SubsOpRev desc
    

The challenge is to return the top 100 n nodes and for each of them only 10 relations r (if possible in a single row).

EDIT:

Sorry for being so unspecific that people understood me wrong.

In detail, the following query gives me the top nodes, sorted:

MATCH (n) WHERE HAS(n.II_VAL)
WITH n, n.`II_VAL` AS ShInflInd ORDER BY ShInflInd DESC
RETURN n.NAME LIMIT 100;

+--------------------------------------+
| n.NAME                               |
+--------------------------------------+
| "PUBLIC"                             |
| "BARCLAYS PLC"                       |

Now I can add the sub-query to this which adds the links:

MATCH (n) WHERE HAS(n.II_VAL)
WITH n, n.`II_VAL` AS ShInflInd ORDER BY ShInflInd DESC LIMIT 100
MATCH (n)-[r]->(m) WHERE HAS(m.VALUE)
WITH r, n, m, m.VALUE AS SubsOpRev
RETURN n.NAME, r.WEIGHT_MERGED, m.NAME, SubsOpRev LIMIT 10;

+----------------------------------------------------------------------------------------+
| n.NAME   | r.WEIGHT_MERGED    | m.NAME                                     | SubsOpRev |
+----------------------------------------------------------------------------------------+
| "PUBLIC" | 0.66               | "VBS MUTUAL BANK"                          | 2630      |
| "PUBLIC" | 0.2923             | "STRATCORP LIMITED"                        | 10842     |

Now what I would like, is that after returning 10 links for "PUBLIC" (possibly sorted by r.WEIGHT_MERGED or SubsOpRev) the query returns the second node ("BARCLAYS PLC") and its 10 links, etc.

I have tried:

MATCH (n) WHERE HAS(n.II_VAL)
WITH n, n.`II_VAL` AS ShInflInd ORDER BY ShInflInd DESC
MATCH (n)-[r]->(m) WHERE HAS(m.VALUE)
WITH r, n, m, m.VALUE AS SubsOpRev
RETURN collect([n.NAME, r.WEIGHT_MERGED, m.NAME, SubsOpRev])[0..10];

Resulting in:

+------------------------------------------------------------------------------------------------------------------------------------------+
| collect([n.NAME, r.WEIGHT_MERGED, m.NAME, SubsOpRev])[0..3]                                                                              |
+------------------------------------------------------------------------------------------------------------------------------------------+
| [["PUBLIC",0.66,"VBS MUTUAL BANK",2630],["PUBLIC",0.2923,"STRATCORP LIMITED",10842], ...

which means I am still stuck with "PUBLIC".

Modifying the query a little makes things worse, as it returns totally different data:

MATCH (n) WHERE HAS(n.II_VAL)
WITH n, n.`II_VAL` AS ShInflInd ORDER BY ShInflInd DESC
MATCH (n)-[r]->(m) WHERE HAS(m.VALUE)
WITH r, n, m, m.VALUE AS SubsOpRev
RETURN n.NAME, collect([r.WEIGHT_MERGED, m.NAME, SubsOpRev])[0..10] LIMIT 3;

+------------------------------------------------------------------------------+
| n.NAME                | collect([r.WEIGHT_MERGED, m.NAME, SubsOpRev])[0..10] |
+------------------------------------------------------------------------------+
| "RS-INVEST AS"        | [[0.5,"VERUCO EIENDOM AS",100]]                      |
| "DBM"                 | [[0.1435,"CHELYABINSKOBLGAZ",6752]]                  |

Ideally, the query should yield something like

| [["PUBLIC",0.66,"VBS MUTUAL BANK",2630],["PUBLIC",0.2923,"STRATCORP LIMITED",10842], ... |
| [["BARCLAYS PLC",x,"XYZ",y], ... |
like image 213
jnode Avatar asked Nov 30 '25 06:11

jnode


2 Answers

You just need to limit and then continue with your query. To provide a reproducible example, let's switch to the movie dataset that ships with Neo4j. Let's say you want to grab the 3 oldest movies in the database and then the 2 oldest actors in each of those movies.

MATCH (m:Movie)
WITH m ORDER BY m.released LIMIT 3
MATCH (p:Person)-[:ACTED_IN]->(m)
WITH m, p ORDER BY p.born
WITH m, COLLECT(p.name)[0..2] AS oldest
RETURN m.title, oldest;

This yields:

   | m.title                         | oldest                              
---+---------------------------------+--------------------------------------
 1 | Something's Gotta Give          | ['Jack Nicholson', 'Diane Keaton']
 2 | Top Gun                         | ['Tom Skerritt', 'Kelly McGillis']
 3 | One Flew Over the Cuckoo's Nest | ['Jack Nicholson', 'Danny DeVito']

So you'll want something like this:

MATCH (n) WHERE HAS(n.II_VAL)
WITH n ORDER BY n.II_VAL DESC LIMIT 100
MATCH (n)-[r]->(m) WHERE HAS(m.VALUE)
WITH n, r, m ORDER BY m.VALUE DESC
RETURN n.NAME, COLLECT([r.WEIGHT_MERGED, m.NAME, m.VALUE])[0..10];
like image 165
Nicole White Avatar answered Dec 03 '25 07:12

Nicole White


You can limit the number of nodes to 100:

MATCH (n)
WHERE has(n.II_VAL)
WITH n, n.II_VAL AS ShInflInd
ORDER BY ShInflInd DESC 
LIMIT 100

You can collect all SubsOpRev and then RETURN a slice of your collection. Something along this line (I assume the SubsOpRev is what you want):

MATCH (n)-[r]->(m)
WITH n, m.VALUE AS SubsOpRev 
ORDER BY SubsOpRev DESC
RETURN n, collect(DISTINCT SubsOpRev)[1..10]

This will give you one node n per row and a slice of the list of all collected SubsOpRev.

like image 40
Martin Preusse Avatar answered Dec 03 '25 05:12

Martin Preusse



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!