Reputation: 47
I have in trouble when use Neo4j to build social network.
My idea:
I want get list member(limit 10 member) in group and list friends general between user currently logged and each member in group (limit 2 friends for each member). So it's my query
MATCH (g:group{_id:"120504"})<-[:memberof]-(m:user)
WITH m ORDER BY m ASC LIMIT 10
OPTIONAL MATCH (m)<-[:friend]-(x:user)<-[:friend]-(o:user{_id:"216645475418"})
return m as member,collect(x{.*})[0..2] as generalFr
Very easy right? but trouble here is collect(x{.*}), it's only return 2 person, but when i'm check by clause PROFILE or EXPLAIN, it's hits all node in database without limit. So i need everyone help me how to limit that collect without create 2 different query.
I think we must be limit OPTIONAL MATCH but i don't known how to do it.
Thank for helping. ;)
Upvotes: 0
Views: 569
Reputation: 30417
This is currently a limitation with Cypher, in that you cannot LIMIT per row in such a way to avoid this kind of scenario.
There are some workarounds in a Cypher knowledge base article on limiting match results per row.
You're already using one by using a slice of the collected list, but as you noted it still does a full expansion to all possible matches and only filters down after.
You should be able to improve efficiency a bit by collecting just the nodes and getting the relevant slice, and only then extracting the properties map, that should save you from doing property access before you slice to 2 elements per list:
WITH m as member, collect(x)[0..2] as generalFr
RETURN member, [x in generalFr | x {.*}] as generalFr
You'll want to PROFILE that query to double-check that this will actually save you db hits (if the planner is smart enough it might be handling this for you, but not sure).
Alternately, if you can use APOC Procedures, you could use either apoc.cypher.run()
to execute a limited subquery per row (but this has the overhead of parsing and executing cypher per row), or you could use the path expander procedures to expand out and limit the results.
Here's an example of how you might use the path expanders in your query:
MATCH (o:user{_id:"216645475418"}) // for later
MATCH (g:group{_id:"120504"})<-[:memberof]-(m:user)
WITH o, m ORDER BY m ASC LIMIT 10
CALL apoc.path.expandConfig(m, {relationshipFilter:'<friend', labelFilter:'user', minLevel:2, maxLevel:2, endNodes:[o], limit:2, optional:true}) YIELD path
WITH m as member, collect(nodes(path)[1]) as generalFr
RETURN member, [x in generalFr | x {.*}] as generalFr
Keep in mind db hits are not tracked within procedure calls, so you won't be able to determine efficiency of this query based on that, use timing instead.
The idea on this one is that it will expand out from each m
using the relationships as defined in the filter, whitelisting only :user nodes in the path, at exactly 2 hops away, with the restriction that the previously matched o
node is the end node of the expansion with a limit of 2 (once it finds 2 it will stop looking) and the entire thing is optional, so it won't wipe out the row if no matches are found.
Upvotes: 1