Query and mutate in the same statement?

In my application i my entities have business-ids that are used application wide. They are UUID generated by the “business layer”, and are stored in dgraph under the id predicate. Generally, when i query or want to update the store i have to look up the UID of the entity (by querying the id pred). Can i store the ID as a variable and the run a mutation in the same statement?

{
A AS var(func:eq(id, "1234-567-89")) {
  
}
  
mutation{
  set{ 
    <A> <lastname> "Jensen"
  }
}
}

We are going to support uuid from next release. With that you can directly use xid without worrying about collisions.

me(id:A) {
name
}

You can infact use this now also, but we hash xid so that might be chances of collision. We will handle uuid natively from next release.

1 Like

Thank you for the prompt reply, that makes stuff a lot easier.

Seems that this thread, and these two issues

are related.

I’m wondering if to support all of this we could support binding patterns through a query that have a semantics of per match as different from variables which are global (we could support both). For example, something like

{
  me(...) {
    edge1 ?a {
      edge2 ?b {
        # here ?a and ?b would be per branch
        # in the answer set, rather than global
        # like vars
      }
    }
  }
  mutation {
    # a linked mutation would work against
    # all such matched patterns.
    # so every (?a, ?b) pair
  }
}

So this would allow, for example, to state friends of friends, without the cycles back to self (i.e. this issue remove reflexive edges from results · Issue #899 · dgraph-io/dgraph · GitHub)

{
  me(...something that selects a set of people...) ?a {
    friend {
      friend @filter(NOT id: ?a) {  # ?a would be bound per person the originally selected set
        name
      }
    }
  }
}

or for the example in the issue

{
  coactors(func:allofterms(name, "Jane Campion")) @cascade @normalize {
    JC_films as director.film {      # JC_films = all Jane Campion's films
      starting_movie: name
      starring {
        JC_actors as performance.actor ?a {      # JC_actors = all actors in all JC films, but ?a is per match
          actor : name
          actor.film {
            performance.film @filter(not var(JC_films)) {  # find a different film
              film_together : name
              starring {
                # find a coactor who has been in some JC film but isn't this actor
                performance.actor @filter(var(JC_actors) ... but not ?a) {
                  coactor_name: name
                }
              }
            }
          }
        }
      }
    }
  }
}

It would allow for all kinds of fine grained mutations as well.

{
  me(...something that selects a set of people...) ?a {
    friend {
      friend ?b @filter(NOT id: ?a) {
        name
      }
    }
  }
  mutation {
   set {
      ?a friend ?b   # for every matched friend of friend pair
   }
  }
}

And similarly for deletions.

I can see it would probably mean big changes, but would be a great language feature. SPARQL and Cypher each support a notion of this sort of pattern bindings. It might be possible though to do it in a post processing step where the original semantics is retained and the post processor grabs the answer and further filters the results with the bindings in mind.

Generally speaking, we already do this via variables. The biggest change I see here is the local vs global scope of variables, which is something @ashwin95r is sort of working on… But we need a more thorough story on it.

Doing mutations based on variables can be done as well, the main issue would be handling race conditions. We don’t support transactions, so, we’ll have to either do best effort, or try some level of atomicity.

Agreed. It’s the local/global scope I’m getting at here. Seems like having both would be great.

Without transactions, query+mutation has the same semantics (and race conds) as user issues query, user builds a mutation, user submits mutation. So I’d think query and mutate in same statement is fine as best effort until there is also features for transactions/atomicity.

Does that mean that currently a multiple query block isn’t atomic - i.e. two queries in the same block could see different versions of the data if there are interleaving updates?

Yes. We guarantee only that the data updates are linearizable. That means, if X is written, any read which happens after X would be able to see X or any future writes. The last part, “or any future writes” could mean that one sees X, and the other sees X’, where X’ comes after X.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.