Query to return all-or-nothing from an aggregation of uid sets

I have a query question. I’m trying to run a query using an aggregation of uids uid(a, b, c...) and figure out a way of returning only if each set in the aggregation (a, b, c, etc.) is non-empty.

This is basically do an all-or-nothing query on uid aggregation. Is this possible? I’m having a hard time figuring out how it could be done.

I’ve tried exploring sum() and count() to achieve this because I know the expected size of the total uid set, but I’m running into several errors.

  {
    var(func: has(a), first: 1) { 
        a_count as count(foo) 
    }
    var(func: has(b), first: 10) {
      b_count as count(foo)
    }
    # The below is a hack because you can't do "c as count(uid)" in the above
    var() {
      b_sum as sum(val(b_count))
      a_sum as sum(val(a_count))
    }
    var() {
      total_sum as math(a_sum + b_sum)
    }
    c(func: eq(val(total_sum), 11)) {
      # Running the expand below produces "Wrong variable type encountered for var(total_sum) 3."
      expand(val(total_sum))
    }
  }

The above is my current tinkering, but even something like this has its own errors:

  {
    a as var(func: has(a), first: 1) { }
    b as var(func: has(b), first: 10) { }
    c(func: eq(count(val(a), val(b)), 11)) {
      _predicate_
    }
  }

That returns Multiple functions as arguments not allowed

I’ve also tried using the @groupby directive:

  {
    a as var(func: has(a), first: 1) { }
    b as var(func: has(b), first: 10) { }
    var(func: uid(a, b)) @groupby(type) {
      c as count(uid)
    }
    d(func: eq(val(c), 11)) {
      _predicate_
    }
  }

Only to get this response: Vars can be assigned only when grouped by UID attribute

Is there any way to ensure I only get a query result when all uid aggregates have their own non-empty results?

Could you produce a sample like this one below?

{
  "xids": [
    {
      "uid": "_:1xid",
      "value": "WhPlErd9WE1234562pb1yFjFHlewUIQwQ"
    },
    {
      "uid": "_:2xid",
      "value": "ahPlErd9WE1234562pb1yFjFHlewUIQwQ"
    },
    {
      "uid": "_:3xid",
      "value": "ehPlErd9WE1234562pb1yFjFHlewUIQwQ"
    }
  ],
  "nodes": [
      {
        "xid": {
          "uid": "_:1xid"
        },
        "firstName": "Tom",
        "lastName": "Cruise"
      },
      {
        "xid": {
          "uid": "_:2xid"
        },
        "firstName": "Maria",
        "lastName": "Sharapova"
      },
      {
        "xid": {
          "uid": "_:3xid"
        },
        "firstName": "Robert",
        "lastName": "Downey Jr."
      },
      {
        "xid": {
          "uid": "_:1xidNull"
        },
        "test": "1test"
      },
      {
        "xid": {
          "uid": "_:2xidNull"
        },
        "test": "2test"
      },
      {
        "xid": {
          "uid": "_:3xidNull"
        },
        "test": "3test"
      },
      {
        "xid": {
          "uid": "_:4xidNull"
        },
        "test": "4test"
      }
    ]
}

You could also use a “trick” with math functions e.g:

{
	var(func: has(firstName), first: 3) {
    uid
		a_count as count(xid)
	}
	var(func: has(test), first: 4) {
		t_count as count(xid)
	}
	var() {
	    a_sum as sum(val(a_count))
	    t_sum as sum(val(t_count))
	    total_sum as summm: math(t_sum + a_sum)
	}
      
	Checks() {
        lessOrEq : math(total_sum <= 7 )
        gt : math(total_sum > 7 )
	}
    returnAll(func: uid(a_count, t_count)) {
        uid
        expand(_all_)
    }
}

That is, “if less than or equal to” you use the result of the “returnAll” block. Otherwise, ignore it.

Result

"data": {
    "Checks": [
      {
        "lessOrEq": true
      },
      {
        "gt": false
      }
    ],
    "returnAll": [
      {
        "uid": "0x2711",
        "lastName": "Sharapova",
        "firstName": "Maria"
      },
      {
        "uid": "0x2712",
        "lastName": "Downey Jr.",
        "firstName": "Robert"
      },
      {
        "uid": "0x2716",
        "lastName": "Cruise",
        "firstName": "Tom"
      },
      {
        "uid": "0x271b",
        "test": "3test"
      },
      {
        "uid": "0x271c",
        "test": "4test"
      },
      {
        "uid": "0x271e",
        "test": "1test"
      },
      {
        "uid": "0x271f",
        "test": "2test"
      }
    ]
  }

News, I guess this new PR would help in this situation.
https://github.com/dgraph-io/dgraph/pull/2947/files

groupby only can be used between edges. So you should use a predicate used to relations. Like “friend”, “city”, “country” and so on. UID isn’t an option for groupby.

Cheers.

Thank you @MichelDiz! The provided query example fits perfectly with what I needed in this case (returning anything if there’s a total sum match). Aliasing count(uid) will definitely be a great general feature moving forward and I look forward to that addition.

1 Like