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"
}
}
}
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
}
}
{
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.