Increment/Decrement across a set of nodes?

@dmai I would reply inline but had to start a new thread because it was closed even though the latest reply was just a few hours ago.

Continuing the discussion from Single operation increment / decrement numbers:

Wondering how this translates to situations where I have multiple nodes that I want to increment/decrement in a batch. It does not fire off a set mutation for each node because DQL does not process foreach loops… proof:

Here is an example schema and data set:(yes I am combining graphql with DQL)

type counter {
    id: ID
    val: Int
}
mutation {
  addcounter(input:[{val:1},{val:2},{val:3}]) {
    numUids
    counter {
      id
      val
    }
  }
}

If I run your upsert and then the following query:

upsert {
  query {
    counter(func: has(counter.val)) {
      old: A as counter.val
      new: B as math(A+1)
    }
  }
  mutation @if(gt(len(A), 0)) {
    set {
      uid(A) <counter.val> val(B) .
    }
  }
}
query {
  counters(func: has(counter.val)) {
    uid
    counter.val
  }
}

I would expect:

[
  {"uid": "0x8", "counter.val": 2 },
  {"uid": "0x9", "counter.val": 3 },
  {"uid": "0xa", "counter.val": 4 },
]

But what I see instead is:

[
  {"uid": "0x8","counter.val": 1},
  {"uid": "0x9","counter.val": 2},
  {"uid": "0xa","counter.val": 3}
]

Why? This is a sum, not a “concatenate”.

Why this cond? There’s no need for this unless you have this issue Add a possibility to identify a variable that has not been assigned yet. ("undefined") · Issue #4619 · dgraph-io/dgraph · GitHub

I think that issue doesn’t apply here cuz the value is coming from math. That means it is out of the var mapping - which is the problem that causes issues in wide queries like this using has func. This increment approach should work fine with several nodes.

BTW, this gives an idea about the loop issue… If we had a “string” function as the “math” one, I think the issue with the mapping vars would be fixed. Like this Custom Block - Discussion

Sorry, copy/paste/edit error. Fixed it in my question.

I was just copying @dmai upsert example exactly as provided. I don’t know why he used it.

Nonetheless, the results stayed the same as the original and the upsert did not increment the count.val as the @dmai stated it should. Not sure what I missed here. I can retry again after a little while to see if I had another typo somewhere in my code.

That is an interesting and useful concept. +1

I have tested locally (without the GraphQL part)

upsert {
  query {
    counter(func: has(counter.val), orderasc:counter.val) {
      old: A as counter.val
      new: B as math(A+1)
    }
  }
  mutation {
    set {
      uid(A) <counter.val> val(B) .
    }
  }
}
upsert {
  query {
    A as counter(func: has(counter.val), orderasc:counter.val) 
  }
  mutation {
    delete {
      uid(A) <counter.val> * .
    }
  }
}
{
  "set":[{
  "counter.val":1},{
  "counter.val":2},{
  "counter.val":3}]
}

hmm, I probably had a typo in my test then. I only use GraphQL when doing a DQL test, because it is faster to get a test environment up and running in Slash and test, and kill it, then it is to startup Dgraph locally for me. And it ensures my test environment is the same as my production environment excluding schema/data.

@amaster507 You can do updates to multiple nodes exactly with the example you set up.

A mutation like uid(A) <counter.val> val(B) ., expands to all the UIDs in A and their corresponding values with val(B).

When I run your example verbatim (thanks for sharing it), I see the counters working as expected:

First run (old/new increments from 1,2,3 to 2,3,4):

{
  "data": {
    "code": "Success",
    "message": "Done",
    "queries": {
      "counter": [
        {
          "old": 1,
          "new": 2
        },
        {
          "old": 2,
          "new": 3
        },
        {
          "old": 3,
          "new": 4
        }
      ]
    },
    "uids": {}
  }
}

Second run (old/new increments from 2,3,4 to 3,4,5):

{
  "data": {
    "code": "Success",
    "message": "Done",
    "queries": {
      "counter": [
        {
          "old": 2,
          "new": 3
        },
        {
          "old": 3,
          "new": 4
        },
        {
          "old": 4,
          "new": 5
        }
      ]
    },
    "uids": {}
  }
}