graphsocial-networkingarangodb

Graph - show friends of friends (and 1st degree common friend)


I found a "friends of friend" traversal query which meets my basic needs to get the friends of friends (friends of friend Query in ArangoDB 3.0). I have modified it slightly to make it work with "The Knows_Graph" example found in the documentation and I've added some extra people (https://docs.arangodb.com/3.11/graphs/example-graphs/#knows-graph)

enter image description here

My query uses Dave as the starting point and looks for his 2nd degree friends via Bob and Lucy. The script works fine but I also want it to include the 1st degree friends (bob and lucy) in the query result. I can do this by setting the minimum depth to 1, however if I do that I doesn't differentiate the depth of Bob and Lucy (see json output), so I wont be able to tell on a bigger graph who is 1st degree and who is 2nd degree

Sample code for min depth of 2

LET person = DOCUMENT("persons/@persons/dave")
LET foaf = (
  FOR v IN 2..2 ANY person knows      
    RETURN v
)
RETURN MERGE(person, { foaf } )

The sample uses the "knows" edge collection and "person" collection

Here is the json output if I have a min depth of 1 and a max depth of 2. As you can see bob and lucy are show on the same collection level as the others. I want to find a way to display them as 1st level somehow without over-complicating the query or causing performance issues.

[
  {
    "_id": "persons/dave",
    "_key": "dave",
    "_rev": "_U7wRsJG--_",
    "name": "Dave",
    "foaf": [
      {
        "_key": "lucy",
        "_id": "persons/lucy",
        "_rev": "_U8--wPq---",
        "name": "Lucy"
      },
      {
        "_key": "henry",
        "_id": "persons/henry",
        "_rev": "_U8-_FNa---",
        "name": "Henry"
      },
      {
        "_key": "bob",
        "_id": "persons/bob",
        "_rev": "_U7wRsJC--_",
        "name": "Bob"
      },
      {
        "_key": "charlie",
        "_id": "persons/charlie",
        "_rev": "_U7wRsJG---",
        "name": "Charlie"
      },
      {
        "_key": "alice",
        "_id": "persons/alice",
        "_rev": "_U7wRsJC---",
        "name": "Alice"
      },
      {
        "_key": "eve",
        "_id": "persons/eve",
        "_rev": "_U7wRsJG--A",
        "name": "Eve"
      }
    ]
  }
]

Solution

  • With the following query you get the depth information on every of your friends.

    LET person = DOCUMENT("persons/@persons/dave")
    LET foaf = (
      FOR v, e, p IN 1..2 ANY person knows      
        RETURN {v, depth: LENGTH(p.edges)}
    )
    RETURN MERGE(person, { foaf } )
    

    The output should look like:

    [
      {
        "_id": "persons/dave",
        "_key": "dave",
        "_rev": "_U8e0Ube---",
        "foaf": [
          {
            "v": {
              "_key": "lucy",
              "_id": "persons/lucy",
              "_rev": "_U8e0Y2u---"
            },
            "depth": 1
          },
          {
            "v": {
              "_key": "bob",
              "_id": "persons/bob",
              "_rev": "_U8ezwCm---"
            },
            "depth": 1
          },
          {
            "v": {
              "_key": "charlie",
              "_id": "persons/charlie",
              "_rev": "_U8e0-Oe---"
            },
            "depth": 2
          },
          {
            "v": {
              "_key": "eve",
              "_id": "persons/eve",
              "_rev": "_U8e0loy---"
            },
            "depth": 2
          },
          {
            "v": {
              "_key": "alice",
              "_id": "persons/alice",
              "_rev": "_U8e0ptu---"
            },
            "depth": 2
          }
        ]
      }
    ]
    

    If you wan to group the friends of every depth together your query could look like:

    LET person = DOCUMENT("persons/@persons/dave")
    LET foaf = (
      FOR v, e, p IN 1..2 ANY person knows      
        COLLECT depth = LENGTH(p.edges) INTO s KEEP v
        RETURN {depth, foaf: s[*].v}
    )
    RETURN MERGE(person, { foaf } )
    

    The output should look like:

    [
      {
        "_id": "persons/dave",
        "_key": "dave",
        "_rev": "_U8e0Ube---",
        "foaf": [
          {
            "depth": 1,
            "foaf": [
              {
                "_key": "lucy",
                "_id": "persons/lucy",
                "_rev": "_U8e0Y2u---"
              },
              {
                "_key": "bob",
                "_id": "persons/bob",
                "_rev": "_U8ezwCm---"
              }
            ]
          },
          {
            "depth": 2,
            "foaf": [
              {
                "_key": "charlie",
                "_id": "persons/charlie",
                "_rev": "_U8e0-Oe---"
              },
              {
                "_key": "eve",
                "_id": "persons/eve",
                "_rev": "_U8e0loy---"
              },
              {
                "_key": "alice",
                "_id": "persons/alice",
                "_rev": "_U8e0ptu---"
              }
            ]
          }
        ]
      }
    ]
    

    UPDATE

    If you need more information about the relations between the people you can return the path instead of the vertices. The path (p) includes all visited edges (knows relations) and all vertices (persons). For more information about the AQL traversal syntax: see docs.

    LET person = DOCUMENT("persons/@persons/dave")
    LET foaf = (
      FOR v, e, p IN 1..2 ANY person knows      
        COLLECT depth = LENGTH(p.edges) INTO s KEEP p
        RETURN {depth, foaf: s[*].p}
    )
    RETURN MERGE(person, { foaf } )