How to store ordered data?

I am new to graph database, and I’m wondering how to store ordered data in graph database.

I have a blog project, and each series has many ordered articles like the first article of this series, the second article of this series.

My goal is:

  • For each series, I can find all its articles with their order preserved
  • For each article, I can find its series, and its previous and next article

I have two solutions in mind:

  • Give each article a series_index to store its order in the series
  • Give each article a prev_article and next_article, and a series has its first_article field

Which solution is better? Or any there any other better solutions!

Is it possible to add a dateTime variable to each article? Then you can sort by this date var at query time.
That has the added bonus of if you get any late entries (laggards) you dont have to recompute your series index/next article etc.

This one, a “chain of edges” is good. You can reduce it to a single edge. Instead of two. And use the reverse index.

That’s a good point. Without an index, Dgraph can’t guarantee order.

But if you use a single edge with a 1 to 1 relation. This ordering can be overcome manually.

Some points about Chain of Edges

Use this filter below to find the first entity in the chain

articles @filter(not has(<~next>)) {

And below to find the last

 articles @filter(not has(<next>)) {

These filters can be useful to do a kind of “POP/SHIFT” Method (just like JavaScript) in combination with Upsert Block.

e.g:

This will remove the first element in the chain of nodes. And promote the second in the line as the first node.

upsert {
  query {
    v as var(func: eq(title, "Series ABC" ) ){
      G as articles @filter(not has(<~next>)) {
        shiftTo as next
      }
    }
  }

  mutation {
    delete {
      uid(v) <articles> uid(G) .
      uid(G) <next> uid(shiftTo) . #we need this to avoid collision
 #You can also do " uid(G) * * . " to delete the whole node instead of just relations.
    }
  }
}

This will “pop” the last node in the chain.

upsert {
  query {
    v as var(func: eq(title, "Series ABC" ) ){
      G as articles @filter(not has(<next>)) {
        pop as <~next> 
      }
    }
  }

  mutation {
    delete {
      uid(v) <articles> uid(G) .
      uid(pop) <next> * .
    }
  }
}

Note that in with these queries above we are only deleting the relationships. If you also want to delete the node being removed from the chain. You can add something like Delete "S * * " :
delete { uid(G) * * . }

With Chain of Edges you don’t need pagination.

As you have the UID of the article you are in. You can query the next and the previous UID like the query below. And you can go even further, just add more edges to traverse and aliases to them.

{
  query(func: eq(title, "Series ABC" )) @normalize{
    articles @filter(uid(0x9c85)) {
      <~next> { 
      previous : title 
      previousuid: uid
    }
      next { 
        next: title
        nextuid: uid
      }
    }
  }
}

Result

{
  "data": {
    "query": [
      {
        "next": "fourth article",
        "nextuid": "0x9c84",
        "previous": "second article",
        "previousuid": "0x9c86"
      }
    ]
  }
}

Queries

{
  var(func: eq(title, "Series ABC" )){
    G as articles @filter(not has(<~next>)) 
  }
  q(func: uid(G)) @recurse  {
    uid : uid
    title : title
    next @normalize
  }
}

response:

{
  "data": {
    "q": [
      {
        "uid": "0x9c5d",
        "title": "first article",
        "next": [
          {
            "title": [
              "second article",
              "third article",
              "fourth article",
              "fifth article",
              "sixth article"
            ],
            "uid": "0x9c58"
          }
        ]
      }
    ]
  }
}

Sample dataset

{
   "set": [
      {
         "title": "Series ABC",
         "articles": [
            {
               "uid": "_:article06",
               "title": "sixth article",
               "body": ""
            },
            {
               "uid": "_:article05",
               "title": "fifth article",
               "body": "",
               "next": {
                  "uid": "_:article06"
               }
            },
            {
               "uid": "_:article04",
               "title": "fourth article",
               "body": "",
               "next": {
                  "uid": "_:article05"
               }
            },
            {
               "uid": "_:article03",
               "title": "third article",
               "body": "",
               "next": {
                  "uid": "_:article04"
               }
            },
            {
               "uid": "_:article02",
               "title": "second article",
               "body": "",
               "next": {
                  "uid": "_:article03"
               }
            },
            {
               "uid": "_:article01",
               "title": "first article",
               "body": "",
               "next": {
                  "uid": "_:article02"
               }
            }
         ]
      }
   ]
}
2 Likes