简体   繁体   中英

Limit by each node, rather than whole query

I have a load of users and photos, with three possible relationships between them (with potentially more added later). These relationships are POSTED_BY , APPEARS_IN and MENTIONED_IN . Given a particular user, I want a query which returns their network up to n jumps away. For example, this should include photos they've posted, photos they appear in or are mentioned in, along with the accounts which posted those photos.

My query at the moment is:

MATCH (root:account { username: {username} })-[r*1..4]-(u)
RETURN *
LIMIT 50

The issue with this is that the limit is done based on the total rows. This results in only returning one level 1 node when it has lots of subnodes. Here is a more visual explanation of the problem.

This is what it might look with an ordinary user (simplified):

在此输入图像描述

If the first node found has a lot of photos, this is what gets returned:

在此输入图像描述

As the user has a lot of media, it hits the limit without showing an accurate representation of their network. Instead, I'd like my query to limit each node to a maximum of n subnodes.

From a couple of answers I've read on here, it looks like it should be possible with COLLECT , however I can't really find any examples.

You can do this with cypher by limiting path length:

MATCH path=(root:account { username: {username} })-[r*1..4]-(u)
WITH COLLECT(path) as paths
RETURN filter(x in paths WHERE length(x)<50)

You can do this while using Neo4j Traversal API. In evaluator, you can have a check on a length of the path and prune when path is more than the desired length.

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.traversal.Evaluation;
import org.neo4j.graphdb.traversal.Evaluator;

public class MyEvaluator implements Evaluator{

    @Override
    public Evaluation evaluate(Path path) {
        if(path.length()>=50)
        {           
            return Evaluation.INCLUDE_AND_PRUNE;
        }

        return Evaluation.INCLUDE_AND_CONTINUE;
    }
}

I think you need something like the following:

 MATCH (root:account { username: {username} })-[]-> (child1)-[r*1..4]-(u)
 RETURN child1, collect(u)
 LIMIT 50

this will return up to 50 rows each row will have two columns. The first column will be child1s and the second column will have the 'Us' of this child

You can construct this query by hand:

MATCH (root:account { username: {username} }) 
WITH {ids: [root], rels: []} as graph

UNWIND graph.ids as N  
  MATCH (N)-[r]->(C) WHERE NOT C IN graph.ids
  WITH graph, C, r LIMIT 50
  WITH graph, collect(C) as ids, collect(r) as rels
  WITH {ids: ids + graph.ids, rels: graph.rels + rels} as graph

UNWIND graph.ids as N  
  MATCH (N)-[r]->(C) WHERE NOT C IN graph.ids
  WITH graph, C, r LIMIT 50
  WITH graph, collect(C) as ids, collect(r) as rels
  WITH {ids: ids + graph.ids, rels: graph.rels + rels} as graph

UNWIND graph.ids as N  
  MATCH (N)-[r]->(C) WHERE NOT C IN graph.ids
  WITH graph, C, r LIMIT 50
  WITH graph, collect(C) as ids, collect(r) as rels
  WITH {ids: ids + graph.ids, rels: graph.rels + rels} as graph

UNWIND graph.ids as N  
  MATCH (N)-[r]->(C) WHERE NOT C IN graph.ids
  WITH graph, C, r LIMIT 50
  WITH graph, collect(C) as ids, collect(r) as rels
  WITH {ids: ids + graph.ids, rels: graph.rels + rels} as graph

RETURN graph.rels

Of course, this structure can be gathered using a simple concatenation by javascript in the event of a large jumps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM