Search code examples
databasegrapharangodbaql

Ideas to improve my AQL query


In my graph, a user is connected to many items and each item can be recursively divided into many sub-items. Since my items have a particular ordering, I'm considering these two options to model my data:

enter image description here

Now I want to create a query to retrieve two levels of items for a given userID. This AQL is my first attempt:

for itemId in (for b in board filter b._from == @_from sort b.order return b._to)
    for item1 in (for t in item filter t._id == itemId return keep(t, '_id', 'title'))
        return merge(item1, {board: (
            for itemId2 in (for b in board filter b._from == item1._id sort b.order return b._to)
                for t in item filter t._id == itemId2 return keep(t, '_id', 'title')
        )})

The query works and it outputs a result like this:

[
  {
    "title": "item 1",
    "_id": "item/41260117498",
    "board": [
      {
        "title": "item 4",
        "_id": "item/42205736442"
      },
      {
        "title": "item 5",
        "_id": "item/42208423418"
      }
    ]
  },
  {
    "title": "item 2",
    "_id": "item/41260772858",
    "board": []
  },
  {
    "title": "item 3",
    "_id": "item/41883233786",
    "board": []
  }
]

Which is fine, but I feel like my query is unnecessarily complex for such a simple traversal. Could someone help me to create a better one, please?


Solution

  • The point in using a graph database is utilize its graph querying capabilities letting it handle the edges, and not to do it on your own by manually joining the edges.

    I will demonstrate the pattern matching traversals for such a query; they handle the transision over the edges (_from and _to) transparently under the hood for you.

    First you would create a graph to configure your edge relations, we use an anonymous graph for simplicity. You have the edge collection board and the vertex collections user and item.

    First you simply let run the whole query to get a feeling for how it works, and inspect the full result:

    FOR v, e, p IN 1..3 OUTBOUND 'user/andy' board RETURN {v: v, e: e, p: p}
    

    You see that you only need to specify the start node, and the edge collection board, it will find the vertex collections to query on its own by inspecting the edges.

    Now we may add FILTERs to omit edges and vertices that we don't like:

    FOR v, e, p IN 1..3 OUTBOUND 'user/andy' board 
       FILTER e.title == 'item 2'
       FILTER p.edges[1].title != 'item 1'
       RETURN {v: v, e: e, p: p}
    

    The example contains two FILTER instructions; one is matching each edge to have the title attribute being equal "item 2", the other is matching that the first edge in the traversal has to contain title "item 1".

    Finaly we use document manipulation to only get the parts of the documents we like:

    FOR v, e, p IN 1..3 OUTBOUND 'user/andy' board 
       FILTER e.title == 'item 2' 
       RETURN {v: v, e: {title: e.title, _id: e._id}, pathEdgeTitle: p.edges[*].title}