Math() function does not work in the expected way in a specific scenario

{    
                    var(func: uid(0x001)){
                        user.details{
                            location as user.value
                        }           	  
                    }
                     
                    var(func: type(place)){
                            variable as place.value
                            d1 as math(variable - location <= 25) 
                            d2 as math(variable - location >= (-25))
                        }

                    query(func: type(place))@filter(eq(val(d1), true) AND eq(val(d2), true)){
                        place.name
                        place.value
                    }
  } 

In this scenario, math(variable - location) does not work. I tried to use the second var block as query block and found out that the location value is not subtracted from variable value. It only checks if the variable value is <= 25 and >= -25 fails to subtract location value.

Have you tried this?

                    var(func: type(place)){
                            variable as place.value
                            X1 as math(variable - location) 
                            d1 as math(X1 <= 25) 
                            d2 as math(X1 >= (-25))
                        }

BTW, the only scenarios foreseen for math are those that are documented or have test files validating it. Anything undocumented requires evaluation and perhaps considering supporting it. I mean, anything not documented is “open seas”.

Cheers.

{
     var(func: uid(0x001)){
           user.details{
                 location as user.value
            }           	  
      }

  query(func: type(place)){
          variable as place.value
          x1 : math(variable - location)
          d1 : math(x1 <= 25) 
          d2 : math(x1 >= (-25))
    }                              
}

Error

{
  "name": "t",
  "url": "http://localhost:8080/query?timeout=20s&ro=true&be=true",
  "errors": [
    {
      "message": "Some variables are used but not defined\nDefined:[location variable]\nUsed:[location variable x1]\n",
      "extensions": {
        "code": "ErrorInvalidRequest"
      }
    }
  ]
}

I have tried your suggestion in an altered form but to no avail. It seems that math(variable - location) does not even recognise the variable that is coming from a different var block. Can you add this is as a feature to math() where aliased variables from different var blocks can be used for mathematical operations? In the absence of this I had to use the client application to play around with those variables instead of performing the same in a Dgraph Query

Use more blocks. Variables in some blocks doesn’t work with some types of functions. e.g. Add a possibility to identify a variable that has not been assigned yet. ("undefined") · Issue #4619 · dgraph-io/dgraph · GitHub

That’s not it, the problem is in the second block. You will need 3 blocks in this case.

{
     var(func: uid(0x001)){
           user.details{
                 location as user.value
            }           	  
      }

  query(func: type(place)){
          variable as place.value
          x1 : math(variable - location)
          d1 : math(x1 <= 25) 
          d2 : math(x1 >= (-25))
    }                              
}

I don’t understand how a third block would be of help in this code snippet. I wanted to display all the places that satisfy d1 and d2 in a different block but x1, d1 and d2’s values (either a int or a bool value) are not even displayed when used with the quoted snippet. Please elaborate on how a third block would resolve the conflict.

I think there’s a typo here. should be x1 as instead of x1 :

Yes, that was a typo. Changed that as well, still it won’t show the boolean values d1 and d2

Can you provide a small sample? so I can quickly test or provide a working query.

{    
                    var(func: uid(0x001)){
                        user.details{
                            location as user.value
                        }           	  
                    }
                     
                    var(func: type(place)){
                            variable as place.value
                            x1 as math(variable - location)
                            d1 as math(x1 <= 25) 
                            d2 as math(x2 >= (-25))
                        }

                    query(func: type(place))@filter(eq(val(d1), true) AND eq(val(d2), true)){
                        place.name
                        place.value
                    }
  } 

This was my initial template and in the query block I want to display the values of the predicates place.name and place.value of type place that satisfy the filter condition containing d1 and d2.

By sample I mean a small example of your dataset.

Directive

user.value: int @index(int) .
place.value: int @index(int) .
place.name: string @index(hash) .

type place{
 place.value
 place.name
} 
type user{
 user.value
}

Sample query

{
  	A(func: type(user)){
    	     user.value
  	}
    
    B(func: type(place)){
         place.value
    }
    
}

Output

{
  "data": {
    "A": [
      {
        "user.value": 5
      },
      {
        "user.value": 45
      },
      {
        "user.value": 45
      },
      {
        "user.value": 45
      },
      {
        "user.value": 45
      },
      {
        "user.value": 45
      },
      {
        "user.value": 90
      },
      {
        "user.value": 60
      },
      {
        "user.value": 6
      },
      {
        "user.value": 78
      },
      {
        "user.value": 50
      },
      {
        "user.value": 35
      }
    ],
    "B": [
      {
        "place.value": 25
      },
      {
        "place.value": 5
      },
      {
        "place.value": 25
      },
      {
        "place.value": 100
      }
    ]
  },
  "extensions": {
    "server_latency": {
      "parsing_ns": 101690,
      "processing_ns": 762667,
      "encoding_ns": 31119,
      "total_ns": 1006263
    },
    "txn": {
      "start_ts": 30119
    },
    "metrics": {
      "num_uids": {
        "dgraph.type": 0,
        "place.value": 4,
        "user.value": 12
      }
    }
  }
}

To complement this answer read:

This one would help in some use cases in this post FOREACH func in DQL (loops in Bulk Upsert)

Also, this one would be handy Custom Block - Discussion

PS. Pay attention that any post with tag “discussion” or “proposal” won’t work at all, as it is just a discussion.

About the comments in the Main query

The order of the post is changed. First, read “Main Query” then analyze the “Formatted dataset” and then go back to this part. You can also just jump to the “Final Considerations” if you don’t want to read everything.

Comment: This won’t Number 1

As they are two distinct entities, for some reason Dgraph does not add sums (math func in this case) between two blocks with different entities or even different levels. This requires one more block to combine the two variables. But even tho, some things you wanna do, won’t work.

Comment: This won’t Number 2

As you can see, the “location” variable is not propagated to that block. You should see "loc": 45 in the response, but don’t. That’s a design thing in the query system. I think there are some discussions on that or Github issues. See something like Add a possibility to identify a variable that has not been assigned yet. ("undefined") · Issue #4619 · dgraph-io/dgraph · GitHub

Comment: This won’t Number 3

The aggregation block would be the one indicated, but it only works from 1 to 1. That is, from one entity to another entity. It won’t work from 1 to many. In the case of 1 to many it would add all values of all entities and this is not what is desired for this use case.

Also, I think the “foreach” function I had mentioned in the beginning would help in this case.

e.g:
In the first two blocks, choose only one entity by its UID.

  varr(func: uid(0x1c5631)){  # User entity UID
(...)
  varg(func: uid(0x1c5635)) { # Place entity UID

My example

{    
  var(func: uid(0x1c5631)){
    uid
    location as user.value
}
  var(func: uid(0x1c5635)) {
    uid
    variable as place.value
  }
  varme(){
    var1 as user.value : sum(val(location))
    var2 as place.value : sum(val(variable)) 
    d1 as lt25 : math(var1 - var2 <= 25) 
    gtMinus25 : math(var2 - var1 >= (-25))
    Example1: math(var1 >= 100) 
  }
  
 #This query won't work
  query(func: uid(variable)) @filter(eq(val(d1), true)){
    place.name
    place.value
  }
} 

Response

{
  "data": {
    "varme": [
      {
        "user.value": 45
      },
      {
        "place.value": 100
      },
      {
        "lt25": true
      },
      {
        "gtMinus25": true
      },
      {
        "Example1": false
      }
    ],
    "query": []
  }
}

Also, the values you would have in that block do not work in later blocks e.g: The response value “true” won’t be applied in the filter eq(val(X), true).

Main Query

{    
  varr(func: type(user), first:1){
    location as user.value
}
  varg(func: type(place)){
    variable as place.value
    d1 as lt25 : math(variable - location <= 25) 
    d2 as gtMinus25 : math(variable - location >= (-25))
    checkk: math(location * variable) #This won't Number 1
    va: val(variable)
    loc: val(location) #This won't Number 2
    loc2:  math(1+location) #This won't as  Number 2
    loc3:  math(1) #This works, as there's no var
  }
  varme(){
    checkk: sum(val(location))
    check2: sum(val(variable)) #This won't Number 3
  }
  
  query(func: type(place))@filter(eq(val(d1), false) AND eq(val(d2), true)){
    place.name
    place.value
  }
} 

Response

{
  "data": {
    "varr": [
      {
        "user.value": 45
      }
    ],
    "varg": [
      {
        "place.value": 100,
        "lt25": false,
        "gtMinus25": true,
        "checkk": 0,
        "va": 100
      },
      {
        "place.value": 25,
        "lt25": true,
        "gtMinus25": true,
        "checkk": 0,
        "va": 25
      },
      {
        "place.value": 25,
        "lt25": true,
        "gtMinus25": true,
        "checkk": 0,
        "va": 25
      },
      {
        "place.value": 5,
        "lt25": true,
        "gtMinus25": true,
        "checkk": 0,
        "va": 5
      }
    ],
    "varme": [
      {
        "checkk": 45
      },
      {
        "check2": 155
      }
    ],
    "query": [
      {
        "place.name": "place",
        "place.value": 100
      }
    ]
  }
}

Formatted dataset

{
   "set": [
      {
         "dgraph.type": "place",
         "place.value": 25,
         "place.name": "place"
      },
      {
         "dgraph.type": "place",
         "place.value": 5,
         "place.name": "place"
      },
      {
         "dgraph.type": "place",
         "place.value": 25,
         "place.name": "place"
      },
      {
         "dgraph.type": "place",
         "place.value": 100,
         "place.name": "place"
      },
      {
         "dgraph.type": "user",
         "user.value": 5
      },
      {
         "dgraph.type": "user",
         "user.value": 45
      },
      {
         "dgraph.type": "user",
         "user.value": 45
      },
      {
         "dgraph.type": "user",
         "user.value": 45
      },
      {
         "dgraph.type": "user",
         "user.value": 45
      },
      {
         "dgraph.type": "user",
         "user.value": 45
      },
      {
         "dgraph.type": "user",
         "user.value": 90
      },
      {
         "dgraph.type": "user",
         "user.value": 60
      },
      {
         "dgraph.type": "user",
         "user.value": 6
      },
      {
         "dgraph.type": "user",
         "user.value": 78
      },
      {
         "dgraph.type": "user",
         "user.value": 50
      },
      {
         "dgraph.type": "user",
         "user.value": 35
      }
   ]
}

Final considerations

Many of the things you want to do are not supported by DB. And I believe that a good part of it is perfectly possible to do at the level of the business logic of your application. Implementing each piece of the above case requires time and work to support it and also popularity, not everyone would use the DB like that - If it becomes popular, more chances of someone working on it. So I recommend that you do them in your application.

Cheers.

So, the point of your main query is to show that variable from another var block (case in point variable from varr block) does not work inside the math() function in another block. Is that so?

In part, yes. But I was not entirely sure, this is not a common use by users. But this all thing is not just with Math. Aggregations too, if you are using it at wrong levels or between entities it won’t work. You can make aggregations using empty blocks, but not unrelated blocks and unrelated levels.

Also, Dgraph don’t check (at root) for logic as far as I know. e.g eq(val(d1), true would never work. Unless it is a number. Maybe ask for a feature to check logic like istrue(val(d1)) or something better. We have cond in math func, but it is limited for the query body, not the root params.

All the text and the dataset sample was to explain the findings during my tests.

BTW, I just thought about a possibility. Perhaps using cond would be an option. Something like (I haven’t tested it, I thought about it now)

ref https://dgraph.io/docs/query-language/#math-on-value-variables

{    
                    var(func: uid(0x001)){
                        user.details{
                            location as user.value
                        }           	  
                    }
                     
                    var(func: type(place)){
                            variable as place.value
                            var1 as math(variable - location <= 25) 
                            var2 as math(variable - location >= (-25))
                            A as cond(var1, 1, 0)
                            B as cond(var2, 1, 0)
                        }

                    query(func: type(place))@filter(eq(val(A), 1) AND eq(val(B), 1)){
                        place.name
                        place.value
                    }
  } 

I have no idea if this would work, but it seems plausible. Following the logic of the cond function if the variable in the first parameter is true it returns 1 else it will be 0. That way you could check the value in the last query block.