Versioned predicates

This may be a big one but it would be great if there was support for versioning predicates, allowing time travel in the graph.
I can imagine a way similarly to how language support is implemented with the @lang indexer
set { _:New_Node <name> "new_name"@revision_0 . }

You could then query
data(func: ...) { name@revision_0 name@revision_1 ... }

3 Likes

Hey @paulrostorp,

Thanks for the suggestion.
I was thinking if something similar can be achieved using facets. You can do facets filtering in the query if required. So your set would like below:

set { _:New_Node <name> "new_name" (revision=0) . }

and query would look like below:

data(func: ...) { name @facets(revision) }
2 Likes

@ashishgoswami

I had not realized you could have facets on non-node predicates, but your suggestion does not completely match with what I am looking for, and that for two reasons:

  1. It requires to store the data in the facet, which in itself would be manageable if it weren’t for 2. Adding a new revision would require passing all the past revisions in order to avoid losing the old data.

To be clear what I’m looking to have is a history of all the values that a predicate has had over time. A revision corresponds to an explicit update that occurred on a predicate.

If I do the following operations

<0x01> <name> "initial name"@revision_0 .
<0x01> <name> "updated name"@revision_1 .
<0x01> <name> "another update to name"@revision_2 .
<0x01> <name> "another update to name"@latest .

and then query

data(func: uid(0x01)) { 
  name@revision_1
}

would return

{
  "name@revision_1": "updated name"
}

Facets do not currently support this. If @lang could be extended to work with other predicates than string this would work. I hope what I’m writing makes sense, if not please let me know

2 Likes

I think that copying the lang behavior would be nice. But dgraph can’t control the versioning. You might do it using Upsert Block. But, even if we copy the lang behavior it would be necessary to change some things in the Upsert Block. e.g:

upsert {
  query {
    v as q(func: eq(name, "initial name")) {
       N as name@*
    }
  }

  mutation {
    set {
      uid(v) <name> "updated name"@{(val(N)+1)} . # <= This would become
    # uid(v) <name> "updated name"@revision_1 . # <= This
    }
  }
}

Anyway, this would involve a lot of changes. Also, would be good to use another symbol instead o “@”. to not confuse with lang.

@ashishgoswami I think facets would be hard, the facets are hard to update. The user must do it on his end and doesn’t scales well. Also, to preserve the old version would be good to store it on the facet e.g

 _:New_Node <name> "new_name" (revision_0 ="new_name", revision_1 ="updated name") . 

So the user could iterate over it and check for previous changes. I see this type of approach in Datomic DB. But it is truly native. All changes are recorded as it was a git commit. So the dev can check old values. This is very useful for a Bank, for example, to find errors or even fraud. Datomic is used by Nubank, the major digital bank in South Americas.

So, for me, this type of feature is really useful and even more great if it is native.

5 Likes