DGO - how to find out the outcome of a conditional upsert

I have an conditional upsert query that check if a node exist:

condition 1 - if not exist: Add node and link it to another
condition 2 - if exist: just link it

I know the outcome of condition 1 by using response.GetUids()
But condition 2 that only link 2 nodes return empty map

How can I check which condition was used and that a link was created if condition 2 was used?

Thanks

Assuming you execute a request as below:

res,err := dg.NewTxn().Do(ctx, req)

If you get an empty map, and you see the predicate name in res.Txn.Preds, you can infer that a predicate was applied between existing uids.

Thanks for the answer.
I noticed it was marked as solution before I had the chance to check this out.

Hi @mbn18, we do eagerly await your feedback! Please let us know how it goes.

Thanks,

So I checked the map / pred list.
If I understand it correctly, this will cover scenarios when there is only on conditional block that use the specific predicate.
If there are more than one, I wont have a clue which one was used.

Is that true?
Thanks

Hi @mbn18, Could you please provide an example you are thinking of? I can try it out and check.

Hey @anand,

Lets assume I wish to link a node to two others of different type.

Cond 1, its already connected → do nothing
Cond 2, edge A is connected → do link to edge B
Cond 3, edge B is connected → do link to edge A
Cond 4, none is connected → do link to both edges

So in cond 2-4 the map will be empty and the pred list will vary between having one edge to both.

Does that make sense?

I think I understand your statement now. You are right, in some cases the “Preds” don’t clarify which edge was just established, and which one was overwritten.

In this case, it might help to write different upserts for each combination (A, and B separately) to have better traceability.

Also, please explore the conditional upsert capability. You can breakout the upserts even more and detect when you are creating vs updating.

These option add a little overhead, but might provide better visibility.

@anand, I already use conditional upsert :slight_smile:

Thinking further of this issue, it might be helpful to have res.Txn.Conditions of []bool.
Where each bool will reflect if that condition was used.

1 Like

Hi Michael,
Thanks for this suggestion.
I will check on this with our engineering team and provide a feedback.

1 Like

Hi @mbn18,
If there is a sample code to illustrate the above pattern, please email it to me. It will help our engineers understand the problem clearly. I already have created a JIRA ticket, but I haven’t been able to provide an accurate example.

Sorry, don`t have a live example.
The issue arise from ambiguity regarding which set of condition was used.

In most cases the set of query result, giud map and preds can help determine. In some cases its harder.
And any way it require complexity (the mingle of part or all of the three).

Hi Michael, putting up this example. Please review if it makes sense so that we can take it forward.

Schema:

<customer>: uid .
<customerid>: string @index(exact) .
<orderid>: string @index(exact) .
<product>: uid .
<productid>: string @index(exact) .

Let’s create some customers and products.

{
  set{
   _:c1 <customerid> "c1" . 
   _:c2 <customerid> "c2" . 
   _:p1 <productid> "p1" .
   _:p2 <productid> "p2" .   
 }
}

Now let’s say that we have an order, and we want to hook up to customer and product via a conditional upsert, like the one below.

upsert{
  query{
    order as var(func: eq(orderid,"o2")){
      customerLink as customer @filter( eq(customerid,"c1"))
      productLink as product @filter(eq(productid, "p1"))
    }
    customer as var(func: eq(customerid,"c1"))
    product as var(func: eq(productid, "p1"))
    
  }
  mutation @if( eq(len(customerLink), 0) OR eq(len(productLink), 0)) {
    set{
      uid(order) <customer> uid(customer) .
      uid(order) <product> uid(product) .
      uid(order) <orderid> "o2" .
      
    }
  }
}

The first time we write, both conditions in the “OR” clause are unsatisfied, and we will get a response as below. A new order was created and predicates were linked.

{
  "data": {
    "code": "Success",
    "message": "Done",
    "queries": {},
    "uids": {
      "uid(order)": "0x2d"
    }
  },
  "extensions": {
    "server_latency": {
      "parsing_ns": 63000,
      "processing_ns": 12374300,
      "encoding_ns": 23300,
      "assign_timestamp_ns": 1396300,
      "total_ns": 14631600
    },
    "txn": {
      "start_ts": 450,
      "commit_ts": 451,
      "preds": [
        "1-customer",
        "1-orderid",
        "1-product"
      ]
    }
  }
}

Now, let’s say that we want to switch the product from p1 to p2. The resulting mutation is as below.

upsert{
  query{
    order as var(func: eq(orderid,"o2")){
      customerLink as customer @filter( eq(customerid,"c1"))
      productLink as product @filter(eq(productid, "p2"))
    }
    customer as var(func: eq(customerid,"c1"))
    product as var(func: eq(productid, "p2"))
    
  }
  mutation @if( eq(len(customerLink), 0) OR eq(len(productLink), 0)) {
    set{
      uid(order) <customer> uid(customer) .
      uid(order) <product> uid(product) .
      uid(order) <orderid> "o2" .
      
    }
  }
}

This will return all three predicates touched. But we don’t know which condition caused it by just looking at the response.

{
  "data": {
    "code": "Success",
    "message": "Done",
    "queries": {},
    "uids": {}
  },
  "extensions": {
    "server_latency": {
      "parsing_ns": 54400,
      "processing_ns": 9710000,
      "encoding_ns": 56500,
      "assign_timestamp_ns": 1521200,
      "total_ns": 11833000
    },
    "txn": {
      "start_ts": 526,
      "commit_ts": 527,
      "preds": [
        "1-customer",
        "1-orderid",
        "1-product"
      ]
    }
  }

Thus, the ask is that we should return individual condition result (as specified in the @if clause) to provide better visibility of the processing in the upsert block.

@mbn18, Please let us know if you have any comments.

Hey @anand,

Do you mean result per if condition or per mutation?
I am an in need of to know which mutation kicked in.
Preferable by txt->mutations → []bool

As for the particular n condition in an if statement. Currently I design the query like this:

{
  q(func:(whatever)) {
    aVar as uid
  } 
}
@if( eq(len(aVar), 0)

As oppose to:

{
  aVar as var(func:(whatever))
}
@if( eq(len(aVar), 0)

This way I can parse later the result and see if the path "q", "[0]", "uid" exist.

Cheers

Once a condition, like the one above, is satisfied, all mutations in the mutation block will be applied. Right now, there is no way to know which condition in the OR clause is true. That’s what I meant in the example. If you have a better, complete example (schema, sample data), it will help. Thanks.

Knowing which specific condition failed in the if can be useful as this can shorten the query. No more of need for inspecting the result for uid and etc.

My current need require also the knowledge of which conditional block passed/failed.

For example:

upsert {
  query {
      find something
    }
  }
  mutation @if(check A1 and check A2 ...) {
    set {
      Whatever
    }
  }
  mutation @if(check B1 and check B2 ...) {
    set {
      Whatever
    }
  }
  mutation @if(check C1 and check C2 ...) {
    set {
      Whatever
    }
  }
}

Lets assume A1 failed (so block 1 was not activated) and and all the rest passed.
So checking txn.blocks will return []bool{false, true, true}

Great, this feedback is useful. I will update our engineering team. Thanks!

1 Like