How to bulk change predicate value?

Hi,

I want to implement Conway’s game of life with Dgraph. So far, I created the schema, set initial state and made a query to list cells that should die or born.

Now I want to do an upsert to change my “alive” predicate but I have troubles with the request:

{
  "name": "t",
  "url": "http://localhost:8080/mutate?commitNow=true",
  "errors": [
    {
      "message": "while lexing uid(tokill) <alive> false . at line 1 column 19: Invalid input: f at lexText",
      "extensions": {
        "code": "ErrorInvalidRequest"
      }
    }
  ]
}

The schema is:

type Cell {                                                                                                                                                                                          
      i
      j
      alive
      nextto
  }
  i: int @index(int) .
  j: int @index(int) .
  alive: bool @index(bool) .
  nextto: [uid] @reverse .

Initial state:

{
  "set": [
    {
      "uid": "_:0_0",
      "dgraph.type": "Cell",
      "i": 0,
      "j": 0,
      "alive": false,
      "nextto": [
        {
          "uid": "_:3_1"
        },
        {
          "uid": "_:0_1"
        },
        {
          "uid": "_:1_1"
        },
        {
          "uid": "_:1_0"
        }
      ]
    },
    {
      "uid": "_:0_1",
      "dgraph.type": "Cell",
      "i": 0,
      "j": 1,
      "alive": false,
      "nextto": [
        {
          "uid": "_:3_2"
        },
        {
          "uid": "_:0_2"
        },
        {
          "uid": "_:1_2"
        },
        {
          "uid": "_:1_1"
        }
      ]
    },
    {
      "uid": "_:0_2",
      "dgraph.type": "Cell",
      "i": 0,
      "j": 2,
      "alive": false,
      "nextto": [
        {
          "uid": "_:3_3"
        },
        {
          "uid": "_:0_3"
        },
        {
          "uid": "_:1_3"
        },
        {
          "uid": "_:1_2"
        }
      ]
    },
    {
      "uid": "_:0_3",
      "dgraph.type": "Cell",
      "i": 0,
      "j": 3,
      "alive": false,
      "nextto": [
        {
          "uid": "_:3_0"
        },
        {
          "uid": "_:0_0"
        },
        {
          "uid": "_:1_0"
        },
        {
          "uid": "_:1_3"
        }
      ]
    },
    {
      "uid": "_:1_0",
      "dgraph.type": "Cell",
      "i": 1,
      "j": 0,
      "alive": false,
      "nextto": [
        {
          "uid": "_:0_1"
        },
        {
          "uid": "_:1_1"
        },
        {
          "uid": "_:2_1"
        },
        {
          "uid": "_:2_0"
        }
      ]
    },
    {
      "uid": "_:1_1",
      "dgraph.type": "Cell",
      "i": 1,
      "j": 1,
      "alive": false,
      "nextto": [
        {
          "uid": "_:0_2"
        },
        {
          "uid": "_:1_2"
        },
        {
          "uid": "_:2_2"
        },
        {
          "uid": "_:2_1"
        }
      ]
    },
    {
      "uid": "_:1_2",
      "dgraph.type": "Cell",
      "i": 1,
      "j": 2,
      "alive": false,
      "nextto": [
        {
          "uid": "_:0_3"
        },
        {
          "uid": "_:1_3"
        },
        {
          "uid": "_:2_3"
        },
        {
          "uid": "_:2_2"
        }
      ]
    },
    {
      "uid": "_:1_3",
      "dgraph.type": "Cell",
      "i": 1,
      "j": 3,
      "alive": false,
      "nextto": [
        {
          "uid": "_:0_0"
        },
        {
          "uid": "_:1_0"
        },
        {
          "uid": "_:2_0"
        },
        {
          "uid": "_:2_3"
        }
      ]
    },
    {
      "uid": "_:2_0",
      "dgraph.type": "Cell",
      "i": 2,
      "j": 0,
      "alive": false,
      "nextto": [
        {
          "uid": "_:1_1"
        },
        {
          "uid": "_:2_1"
        },
        {
          "uid": "_:3_1"
        },
        {
          "uid": "_:3_0"
        }
      ]
    },
    {
      "uid": "_:2_1",
      "dgraph.type": "Cell",
      "i": 2,
      "j": 1,
      "alive": true,
      "nextto": [
        {
          "uid": "_:1_2"
        },
        {
          "uid": "_:2_2"
        },
        {
          "uid": "_:3_2"
        },
        {
          "uid": "_:3_1"
        }
      ]
    },
    {
      "uid": "_:2_2",
      "dgraph.type": "Cell",
      "i": 2,
      "j": 2,
      "alive": true,
      "nextto": [
        {
          "uid": "_:1_3"
        },
        {
          "uid": "_:2_3"
        },
        {
          "uid": "_:3_3"
        },
        {
          "uid": "_:3_2"
        }
      ]
    },
    {
      "uid": "_:2_3",
      "dgraph.type": "Cell",
      "i": 2,
      "j": 3,
      "alive": true,
      "nextto": [
        {
          "uid": "_:1_0"
        },
        {
          "uid": "_:2_0"
        },
        {
          "uid": "_:3_0"
        },
        {
          "uid": "_:3_3"
        }
      ]
    },
    {
      "uid": "_:3_0",
      "dgraph.type": "Cell",
      "i": 3,
      "j": 0,
      "alive": false,
      "nextto": [
        {
          "uid": "_:2_1"
        },
        {
          "uid": "_:3_1"
        },
        {
          "uid": "_:0_1"
        },
        {
          "uid": "_:0_0"
        }
      ]
    },
    {
      "uid": "_:3_1",
      "dgraph.type": "Cell",
      "i": 3,
      "j": 1,
      "alive": false,
      "nextto": [
        {
          "uid": "_:2_2"
        },
        {
          "uid": "_:3_2"
        },
        {
          "uid": "_:0_2"
        },
        {
          "uid": "_:0_1"
        }
      ]
    },
    {
      "uid": "_:3_2",
      "dgraph.type": "Cell",
      "i": 3,
      "j": 2,
      "alive": false,
      "nextto": [
        {
          "uid": "_:2_3"
        },
        {
          "uid": "_:3_3"
        },
        {
          "uid": "_:0_3"
        },
        {
          "uid": "_:0_2"
        }
      ]
    },
    {
      "uid": "_:3_3",
      "dgraph.type": "Cell",
      "i": 3,
      "j": 3,
      "alive": false,
      "nextto": [
        {
          "uid": "_:2_0"
        },
        {
          "uid": "_:3_0"
        },
        {
          "uid": "_:0_0"
        },
        {
          "uid": "_:0_3"
        }
      ]
    }
  ]
}

The following query returns the good result (by the way, is there a better way to count edges?):

{
    living as var(func: eq(alive, true)) {
        num_alive_nextto_living as count(nextto) @filter (eq(alive, true))
        num_alive_nextto_living_r as count(~nextto) @filter (eq(alive, true))
        num_alive_neibours as math(num_alive_nextto_living + num_alive_nextto_living_r)
    }
    tokill(func: uid(living)) @filter(lt(val(num_alive_neibours), 2) or gt(val(num_alive_neibours), 3)){
        i
        j
    }
    dead as var(func: eq(alive, false)){
        num_alive_nextto_dead as count(nextto) @filter (eq(alive, true))
        num_alive_nextto_dead_r as count(~nextto) @filter (eq(alive, true))
        num_death_neibours as math(num_alive_nextto_dead + num_alive_nextto_dead_r)
    }
    toborn(func: uid(dead)) @filter(eq(val(num_death_neibours), 3)){
        i
        j
    }
}

Result:

{
  "data": {
    "tokill": [
      {
        "i": 2,
        "j": 1
      },
      {
        "i": 2,
        "j": 3
      }
    ],
    "toborn": [
      {
        "i": 1,
        "j": 2
      },
      {
        "i": 3,
        "j": 2
      }
    ]
  },
  "extensions": {
    "server_latency": {
      "parsing_ns": 306809,
      "processing_ns": 2751609,
      "encoding_ns": 67252,
      "total_ns": 3270064
    },
    "txn": {
      "start_ts": 3846
    },
    "metrics": {
      "num_uids": {
        "": 3,
        "_total": 114,
        "alive": 52,
        "i": 4,
        "j": 4,
        "nextto": 16,
        "num_alive_neibours": 6,
        "num_death_neibours": 13,
        "~nextto": 16
      }
    }
  }
}

But

This mutation fails:

upsert{
    query {
        {
            living as var(func: eq(alive, true)) {
                num_alive_nextto_living as count(nextto) @filter (eq(alive, true))
                num_alive_nextto_living_r as count(~nextto) @filter (eq(alive, true))
                num_alive_neibours as math(num_alive_nextto_living + num_alive_nextto_living_r)
            }
            tokill as var(func: uid(living)) @filter(lt(val(num_alive_neibours), 2) or gt(val(num_alive_neibours), 3)){
                i
                j
            }
            dead as var(func: eq(alive, false)){
                num_alive_nextto_dead as count(nextto) @filter (eq(alive, true))
                num_alive_nextto_dead_r as count(~nextto) @filter (eq(alive, true))
                num_death_neibours as math(num_alive_nextto_dead + num_alive_nextto_dead_r)
            }
            toborn as var(func: uid(dead)) @filter(eq(val(num_death_neibours), 3)){
                i
                j
            }
        }
    }
    mutation {
        set {
            uid(tokill) <alive> false .
            uid(toborn) <alive> true .
        }
    }
}

As well as this one:

upsert{
    query {
        {
            living as var(func: eq(alive, true)) {
                num_alive_nextto_living as count(nextto) @filter (eq(alive, true))
                num_alive_nextto_living_r as count(~nextto) @filter (eq(alive, true))
                num_alive_neibours as math(num_alive_nextto_living + num_alive_nextto_living_r)
            }
            var(func: uid(living)) @filter(lt(val(num_alive_neibours), 2) or gt(val(num_alive_neibours), 3)){
                tokill as uid
            }
            dead as var(func: eq(alive, false)){
                num_alive_nextto_dead as count(nextto) @filter (eq(alive, true))
                num_alive_nextto_dead_r as count(~nextto) @filter (eq(alive, true))
                num_death_neibours as math(num_alive_nextto_dead + num_alive_nextto_dead_r)
            }
            var(func: uid(dead)) @filter(eq(val(num_death_neibours), 3)){
                toborn as uid
            }
        }
    }
    mutation {
        set {
            uid(tokill) <alive> false .
            uid(toborn) <alive> true .
        }
    }
}

With this error:

{
  "name": "t",
  "url": "http://localhost:8080/mutate?commitNow=true",
  "errors": [
    {
      "message": "while lexing uid(tokill) <alive> false . at line 1 column 19: Invalid input: f at lexText",
      "extensions": {
        "code": "ErrorInvalidRequest"
      }
    }
  ]
}

What do I miss ?

Edit: I tried to quote values like this:

            uid(tokill) <alive> "false" .
            uid(toborn) <alive> "true" .

and like this:

            uid(tokill) <alive> "false"<xs:boolean> .
            uid(toborn) <alive> "true"<xs:boolean> .

Without more success:

{
  "name": "t",
  "url": "http://localhost:8080/mutate?commitNow=true",
  "errors": [
    {
      "message": "line 2 column 8: Expected some name. Got: lex.Item [6] \"{\" at 2:8",
      "extensions": {
        "code": "ErrorInvalidRequest"
      }
    }
  ]
}

I think it is a typo perhaps

uid(tokill) <alive> "false"^^<xs:boolean> . 
uid(toborn) <alive> "true"^^<xs:boolean> .

You are right about the typo but I still get the error.

You have an extra curly brackets. Remove it and you are good to go.

Thank you! Indeed there where an extra pair of curly brackets inside the query.

Now, with or without ^^<xs:boolean> the mutation works.

I have three questions about the query:

  1. Are those two queries totally equivalent or are there subtle difference in the processing:
tokill as var(func: uid(living)) @filter(lt(val(num_alive_neibours), 2) or gt(val(num_alive_neibours), 3)){ }
var(func: uid(living)) @filter(lt(val(num_alive_neibours), 2) or gt(val(num_alive_neibours), 3)){
        tokill as uid
}
  1. Is there a preferred one?

  2. I counted edges and reversed edges apart and then sum them. Is there a way to count both at the same time ?

  1. Yes.
  2. The first one, without curly brackets.
  3. I don’t think so. Reverse edges are like “a different edge”.

Thank you for your response.

I eventually come to:

upsert{
    query {
        cells as var(func: type(Cell)) {
            num_living_nextto as count(nextto) @filter (eq(alive, true))
            num_living_nextto_r as count(~nextto) @filter (eq(alive, true))
            num_living_neibours as math(num_living_nextto + num_living_nextto_r)
        }
        tokill as var(func: uid(cells)) @filter(eq(alive, true) AND lt(val(num_living_neibours), 2) or gt(val(num_living_neibours), 3))
        toborn as var(func: uid(cells)) @filter(eq(alive, false) AND eq(val(num_living_neibours), 3))
    }
    mutation {
        set {
            uid(tokill) <alive> "false" .
            uid(toborn) <alive> "true" .
        }
    }
}

which is easier to read.