Support field functions

Moved from GitHub dgraph/2037

Posted by JimmyCheng:

In SQL language, we can apply simple functions to the fields, including create new fields.
e.g.

SELECT concat(first_name, ' ' , last_name) AS full_name
SELECT A, B, 'c' as C from MyTable
SELECT ROUND(Weight_kg / (Height_m * Height_m), 2) AS BMI from ...

In DGraphQL, is it possible to support following kind of query?

{
 allNodes(func: eq(type, "DP")){
     supplyDate
     consumeDate
     status
     issueBehindSchedule: math(supply_date > consume_date)
     noSupplyDate: eq(supplyDate, null)
     atRisk: eq(status, "At Risk")
  }
}

pawanrawal commented :

atRisk: eq(status, “At Risk”)

I am assuming that here you want all nodes with type DP but want the status value to be returned only if it equals At Risk?

JimmyCheng commented :

Here the fields “issueBehindSchedule”, “noSupplyDate”, “atRisk” are calculated fields according to the existing fields. the value here are “true” or “false”. There are some other requirements like simple inline string, date functions.

manishrjain commented :

It sounds like an interesting feature, but not sure how useful it is. All of these can be done at the application layer, without necessarily needing the DB to make these calculations.

campoy commented :

Hi there,

I just wanted to inform you that at least one part of your request is already doable with the current state of GraphQL±.

Predicates are not allowed in mathematical expressions, only variables are, so you will need to do the following in order to get a issueBehindSchedule field:

{
 allNodes(func: eq(type, "DP")){
     sd as supply_date
     cd as consume_date
     issueBehindSchedule: math(sd > cd)
  }
}

The rest of your comparisons will still not work since we do not support string comparison yet.

I am considering new features for Dgraph 1.2 (coming up ~ end of Q3) and would love to know more about your constraints to figure out what solution would be best.
I also would like to make sure you understand I’m not guaranteeing this will be implemented, as GraphQL and other features have higher priority.

Please let me know if you’d be interested on a quick chat.

MichelDiz commented :

I was analyzing this issue. This issue in relation to #2001 is a duplicate of it and not the other way around. This issue was created 9 days after #2001. Anyway.

The lack of depth in the text of this issue could unintentionally, lost some important information in the discussion. Manish had already commented on a possible solution there for this. He mentioned that would be adding a directive called @post. To support “function edges” (Which means in value edges). But he did not get deep on it.

BTW, not sure what ‘post’ means. Would it be like “Post process”? i guess tho.

I’m going to write possible syntax ideas here for anyone working on this feature in the future. Taking into account both issues.

Syntax Examples:

Return True or False.

- predicate @post(eq("value"))
- predicate @post(eq(null)) #Check if the predicate has value returns true if false.
- predicate @post(eq(val(varName)))
- predicate @post(eq[val1, val2, ..., valN])
- predicate @post(eq[$var1, "value", ..., $varN])
- predicate @post(IE("value")) #for inequality
- myAlias: predicate @post(lt(54))
- predicate @post(count(value)) #It is exactly equal to eq, but check if it is Int or float.
- predicate @post(if(var1 >= var2))
- predicate @post(if(count(value) > var2))

Returns computed values.

# Returns "predicate": "String1 String2 String3"
- predicate @post(concat(var1, " ", var2, " ", var3))
- predicate @post(concat(var1, var2, var3), separator) #Optionally include a separator
- predicate @post(concat($var1, $name))

Using as value variable can be useful for upsert blocks.

# Returns "predicate": "String1 String2"
- value0 as predicate @post(concat(var1, var2)) 
- value1 as predicate @post(concat($var1, $name))

“Anonymous predicate” using an alias.

- myAlias: @post(count(value)) #This is equivalent to "myAlias: math(number)"
- myAlias: @post(if(var1 >= var2)) #This is equivalent to "myAlias: math(if(var1 >= var2))"
- value2 as myAlias: @post(if(var1 >= var2)) #Using the computed value in a variable.

Covering the issue query:

{
  allNodes(func: type("DP")){
      sd as supplyDate
      cd as consumeDate
      status
      issueBehindSchedule: math(sd > cd)
      noSupplyDate: supplyDate @post(eq(null))
      atRisk: status @post(eq("Okay"))
   }
}

The problem with this query is in the case of “null”. Dgraph does not currently support undefined or null. There is no way to check them. Perhaps issue #4619 would cover that.

Instead of issueBehindSchedule: math(sd > cd) (which works, but it is “hacky”). we could do issueBehindSchedule: @post(if(sd > cd))

Covering the other issue

{
  me(func: uid(0xabcd)) {
    f as firstName@en
    l as lastName@en
    fullName: @post(concat( f, ' ', l))
  }
}
{
  me(func: uid(0xabcd)) {
    f as firstName@en
    l as lastName@en
    fullName : @post(concat( f, ' ', l))
    isMarriedTo {
            fm as firstName@en
            lm as lastName@en
         fullName : @post(concat( fm, ' ', lm))
    }
    is as count(isMarriedTo)
    married: @post(if(is > 0))
  }
}