DQL conditional upsert with nested query value variables

What I want to do

I want to use DQL mutation to update my data. The choice to use DQL is because I want to bypass @auth rules
I have the following simplified GraphQL Schema

type Payment { # @auth rules omitted for simplification
  id: ID!
  isSuccessful: Boolean
  method: String
  reference: PaymentExternal
}

union PaymentExternal = ExternalXCharge | ExternalYRecord

type ExternalXCharge {
  id: String! @id
  # other fields specific to XCharge
}

type ExternalYRecord {
  id: String! @id
  # other fields specific to YRecord
}

with the following sample data

{
  "id": "0x8",
  "isSuccessFul": null,
  "method": "creditcard",
  "reference": {
    "id": "x_charge_id_12345"
  }
}

When querying using DQL, I get the following output

{
  "uid": "0x8",
  "Payment.method": "creditcard",
  "Payment.reference": {
    "uid": "0x7",
    "ExternalXCharge.id": "chrg_test_5odblz0450uyyjfo34z"
  }
}

I would like to update isSuccessful for the Payment record 0x8 while also checking whether reference.id value is equal to “x_charge_id_12345”

What I did

Since I have a conditional update, I tried to use Conditional Upsert, using the following RDF syntax

upsert {
  query {
    p as payment(func: uid(0x8)) {
      uid
      Payment.method
      Payment.reference {
        chargeId as ExternalXCharge.id
      }
    }
  }
  mutation @if(eq(val(chargeId), "x_charge_id_12345")) {
    set {
      uid(p) <Payment.isSuccessful> "true" .
    }
  }
}

ISSUE
When I run the above request, no update occurred. The data has not been updated, when I tried to query using GraphQL and DQL syntax.
It seems chargeId is not set properly, when I tried removing @if and set the values to new test predicates as follows

upsert {
  query {
    p as payment(func: uid(0x8)) {
      uid
      m as Payment.method
      Payment.reference {
        chargeId as ExternalXCharge.id
      }
    }
  }
  mutation {
    set {
      uid(p) <Payment.isSuccessful> "true" .
      uid(p) <testMethod> val(m) .
      uid(p) <testChargeId> val(chargeId) .
    }
  }
}

Only the value of testMethod is set.

However, both val(m) and val(chargeId) don’t seem to work with the @if condition

How do I update using the predicate ExternalXCharge.id on Payment.reference as a condition?

NOTE:
When the update is successful I got the following as part of the raw response
Is extensions.txn.preds supposed to be like that?

"preds":["1-\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000Payment.isSuccessful"]

PS: I’m using Postman to send requests

Dgraph metadata

dgraph version

Dgraph version : v21.03.0
Dgraph codename : rocket
Dgraph SHA-256 : b4e4c77011e2938e9da197395dbce91d0c6ebb83d383b190f5b70201836a773f
Commit SHA-1 : a77bbe8ae
Commit timestamp : 2021-04-07 21:36:38 +0530
Branch : HEAD
Go version : go1.16.2
jemalloc enabled : true

For Dgraph official documentation, visit https://dgraph.io/docs.
For discussions about Dgraph , visit https://discuss.dgraph.io.
For fully-managed Dgraph Cloud , visit https://dgraph.io/cloud.

Licensed variously under the Apache Public License 2.0 and Dgraph Community License.
Copyright 2015-2021 Dgraph Labs, Inc.

Dgraph’s @if upserts currently do not directly support string equals in the directive itself. You’ll want to do those matches in the query and then match against the len() of your query variable.

Something like this will work, essentially moving your conditional check into the query section:

upsert {
  query {
    p as payment(func: uid(0x2)) {
      uid
      Payment.method
      Payment.isSuccessful
      Payment.reference {
        chargeId as ExternalXCharge.id
      }
    }
    match as var(func: eq(val(chargeId), "x_charge_id_12345"))
  }
  mutation @if(eq(len(match), 1)) {
    set {
      uid(p) <Payment.isSuccessful> "true" .
    }
  }
}


Yup, that’s expected. The encoding here accounts for the multi-tenancy namespace available in the enterprise version and Dgraph Cloud.

Wow, that works great. Thank you!

Could you explain this part? I wonder why only <testMethod> is set and <testChargeId> is not

This will do it:

upsert {
  query {
    p as payment(func: uid(0x8)) {
      uid
      m as Payment.method
      r as Payment.reference {
        chargeId as ExternalXCharge.id
      }
    }
  }
  mutation {
    set {
      uid(p) <Payment.isSuccessful> "true" .
      uid(p) <testMethod> val(m) .
      uid(r) <testChargeId> val(chargeId) .
    }
  }
}

The val is a special function in that it is not just returning a list of values but rather a list mapped to uids. So when the uid of the value is not found in the `p variable then the value gets ignored.