Search code examples
neo4jcyphersocial-networking

Query to search for any user showing first those who are "friends" and "friends of friends" of specific user


I'm new working with neo4j and Cypher.

Currently I'm developing a social network site using neo4j for data. This will have a search option in the top bar to find other users on the social network, but for the results I want to show first those who are friends, then those who are friends of friends, then the others. All this, paging search results like search in facebook.

To achieve this, I am looking for ways to create an optimal query with Cypher for this search option.

My USER nodes have a structure like this:

(me:User { mid:"1234", name:"John Doe", email:"[email protected]" })

Where "mid" property is a custom ID.

Friendship relationships between USER nodes have the label "FRIENDOF" in both directions:

(a:User)-[:FRIENDOF]->(b:User) and (a:User)<-[:FRIENDOF]-(b:User)

The most efficient query that I designed for this is:

MATCH p = allShortestPaths((me:User)-[:FRIENDOF*]->(other:User))
WHERE me.mid = "1234"
AND other.name = "Any user name"
RETURN other, length(p) AS Length
ORDER BY length(p) ASC
SKIP 10
LIMIT 10

This query seems to work well, but I cann't get of my head the idea that there should be a more optimal way for this query.

Following the examples of Neo4j documentation (http://neo4j.com/docs/stable/cypher-cookbook-friend-finding.html), I tried to create this query by mixing the query of friends, friends of friends and others with a UNION operation, but with UNION I can not page the results due to actual issue 1879 (https://github.com/neo4j/neo4j/issues/1879) and related 2725, and the query for "others" requires the results of the previous querys (friends and friends of friends)

Any better ideas to make this query less expensive in neo4j terms?

How can I search users that are not friends or friends of friends?

Thanks!


Solution

  • You have a good starting block for the query. You just need to think, that the shortestPath should be optional (hence users not connected), so, taken this into account, you could do the following query :

    MATCH (me:User {mid:"1234"}), (other:User {name:"Any User name"})
    OPTIONAL MATCH p=shortestPath((me)-[:FRIEND*1..2]-(other))
    RETURN me.mid, other.mid, length(p) as distance
    ORDER BY distance DESC
    

    In the case there is no path between the two users, the distance will be null, so you might check this at the application level.

    Tip: Default depth limit for shortestPath is 15.

    Referenced demo graph : http://console.neo4j.org/r/t2n1qc

    EDIT

    This query is based on the demo console for retrieving also number of friends in common :

    MATCH (me:User { login:'randall.tremblay' })
    MATCH (search:User { login:'witting.franz' })
    OPTIONAL MATCH p=shortestPath((me)-[:FRIEND*]-(search))
    WITH me, search, p, size((me)-[:FRIEND]-()-[:FRIEND]-(search)) AS common
    RETURN me.login, search.login, length(p) AS distance, common