Nested / cyclic filtering not working

Moved from GitHub dgraph/5045

Posted by ppp225:

What version of Dgraph are you using?

v20.03.0-beta.20200320, v2.0.0-rc1

Have you tried reproducing the issue with the latest release?

yes

What is the hardware spec (RAM, OS)?

linux, windows, 32G

Steps to reproduce the issue (command/config used to run Dgraph).

Lets say I have a schema like this:

Car---> Stat
  |       ^
  v        \
Contract->Group
  |
  v
Dealer

When doing the following in the same query block:
Car → S as Stat
Car → Contract → Group → Stat @filter(uid(S))

the filter result is empty, even if count(S) == 1, and without the filter, the query returns what it is supposed to.

(dgraph code example in next post)

Expected behaviour and actual result.

As I understand, S should contain the uidList of matched Stat nodes. So when filtering Stat nodes with it, it should work, and not be empty.

ppp225 commented :

schema

type Car {
	carName: string
	carStat: [Stat]
	carContract: [Contract]
}
carName: string @index(hash) .
carStat: [uid] @reverse .
carContract: [uid] @reverse .

type Stat {
	statName: string
}
statName: string @index(hash) .

type Contract {
	contractID: string
	contractDealer: Dealer
	contractGroup: Group
}
contractID: string .
contractDealer: uid @reverse .
contractGroup: uid @reverse .

type Dealer {
	dealerName: string
}
dealerName: string @index(hash) .

type Group {
	groupName: string
	groupStat: Stat
}
groupName: string .
groupStat: uid @reverse .

mutation

{
set {
<_:Ferrari> <carName> "Ferrari" .
<_:Porsche> <carName> "Porsche" .
<_:Speed> <statName> "Speed" .
<_:Handling> <statName> "Handling" .

<_:Ferrari> <carStat> <_:Speed>(statValue=1.0) .
<_:Ferrari> <carStat> <_:Handling>(statValue=0.6) .
<_:Porsche> <carStat> <_:Speed>(statValue=0.7) .
<_:Porsche> <carStat> <_:Handling>(statValue=0.9) .

<_:Dealer1> <dealerName> "Dealer1" .
<_:Dealer2> <dealerName> "Dealer2" .
<_:contract1> <contractID> "111" .
<_:contract2> <contractID> "222" .
<_:contract3> <contractID> "333" .
<_:contract4> <contractID> "444" .

<_:Ferrari> <carContract> <_:contract1> .
<_:Porsche> <carContract> <_:contract2> .
<_:Ferrari> <carContract> <_:contract3> .
<_:Ferrari> <carContract> <_:contract4> .

<_:contract1> <contractDealer> <_:Dealer2> .
<_:contract2> <contractDealer> <_:Dealer2> .
<_:contract3> <contractDealer> <_:Dealer1> .
<_:contract4> <contractDealer> <_:Dealer2> .

<_:group1> <groupName> "SpeedGroup" .
<_:contract1> <contractGroup> <_:group1> .
<_:contract2> <contractGroup> <_:group1> .
<_:contract3> <contractGroup> <_:group1> .
<_:contract4> <contractGroup> <_:group2> .
<_:group1> <groupStat> <_:Speed> .
<_:group2> <groupStat> <_:Handling> .

<_:Ferrari> <dgraph.type> "Car" .
<_:Porsche> <dgraph.type> "Car" .
<_:Speed> <dgraph.type> "Stat" .
<_:Handling> <dgraph.type> "Stat" .
<_:Dealer1> <dgraph.type> "Dealer" .
<_:Dealer2> <dgraph.type> "Dealer" .
<_:contract1> <dgraph.type> "Contract" .
<_:contract2> <dgraph.type> "Contract" .
<_:contract3> <dgraph.type> "Contract" .
<_:group1> <dgraph.type> "Group" .
}
}

query

query {
  withFilter(func: Type(Car), first: 1) {
    carName
    S as carStat @filter(eq(statName, "Speed")) {
      uid
      statName
    }
    count(carStat @filter(eq(statName, "Speed"))) # one Stat node statName=Speed
    carContract {
      contractID
      contractGroup {
        groupName
        groupStat @filter(uid(S)) { # empty, but shouldn't be
          uid
          statName
        }
      }
    }
  }
  withoutFilter(func: Type(Car), first: 1) {
    carContract {
      contractGroup {
        groupStat {
          uid
          statName  # this is the one, it should be visible with filter
        }
      }
    }
  }
}

result

  "data": {
    "withFilter": [
      {
        "carName": "Porsche",
        "carStat": [
          {
            "uid": "0x16",
            "statName": "Speed" # one Stat node statName=Speed
          }
        ],
        "count(carStat)": 1,
        "carContract": [
          {
            "contractID": "222",
            "contractGroup": {
              "groupName": "SpeedGroup" # here is missing
            }
          }
        ]
      }
    ],
    "withoutFilter": [
      {
        "carContract": [
          {
            "contractGroup": {
              "groupStat": {
                "uid": "0x16",
                "statName": "Speed" # this is the one, it should be visible with filter
              }
            }
          }
        ]
      }
    ]
  }

MichelDiz commented :

Is this the desirable result?

{
  "data": {
    "withFilter": [
      {
        "carName": "Porsche",
        "carStat": [
          {
            "uid": "0x2716",
            "statName": "Speed"
          }
        ],
        "count(carStat)": 1,
        "carContract": [
          {
            "contractID": "222",
            "contractGroup": {
              "groupName": "SpeedGroup",
              "groupStat": {
                "uid": "0x2716",
                "statName": "Speed"
              }
            }
          }
        ]
      }
    ]
  }
}

Use this query

 {
  origin as var(func: Type(Car), first: 1) #@filter(eq(carName, "Porsche"))
  {
    S as carStat @filter(eq(statName, "Speed"))  
  }
 withFilter(func: uid(origin)) {
    carName
   carStat @filter(eq(statName, "Speed")) {
      uid
      statName
    }
    count(carStat) @filter(eq(statName, "Speed")) 
    carContract {
      contractID
      contractGroup {
        groupName
        groupStat @filter(uid(S)) {
          uid
          statName
        }
      }
    }
  }

}

This issue is related to this Promise a nested block (under construction - I'm still working in the use case)

ppp225 commented :

Thanks for your response @MichelDiz !
I already tried the query with multiple blocks, and it didn’t work for my use case. It works for a single element (Speed in this case), however not for multiple.
I updated the mutation above, so please reimport it, and try this query:

 {
 DEALERS as var(func: Type(Dealer)) {
    dealerName
  	~contractDealer {
      contractID
      TOO_MANY_CARS as ~carContract
    }
    val(TOO_MANY_CARS)
  }
 dealer(func: uid(DEALERS)) {
    dealerName
  	~contractDealer {
      contractID
      REQUIRED_CAR as ~carContract {
        carName     # should only include this car
      }
      val(REQUIRED_CAR)
      contractGroup {
        groupName
        groupStat {
          statName
          ~carStat @filter(uid(TOO_MANY_CARS)){ # doesn't work, includes both cars
          # ~carStat @filter(uid(REQUIRED_CAR)){ # doesn't work, result is empty
          # ~carStat { # doesn't work, result has all cars
            carName
          }
        }
      }
    }
  }
}

The result, which I get, looks like this

  "data": {
    "dealer": [
      {
        "dealerName": "Dealer2",
        "~contractDealer": [
          {
            "contractID": "222",
            "~carContract": [
              {
                "carName": "Porsche"   # should only include this car
              }
            ],
            "contractGroup": {
              "groupName": "SpeedGroup",
              "groupStat": {
                "statName": "Speed",
                "~carStat": [
                  {
                    "carName": "Ferrari"   # this should be filtered out
                  },
                  {
                    "carName": "Porsche"   # should only include this car
                  }
                ]
              }
            }
          },
(...)

Above is the query related to this Issue.

Full use case

Below, I added my full use case, where I require this functionality - in case you find it useful. What is done here, is calculating weighted scores based on facet data and some weights stored in nodes. I want to use this query in an upsert (to save the final calculated score in a Node).

 {
 DEALERS as var(func: Type(Dealer)) {
    dealerName
  	~contractDealer {
      contractID
      TOO_MANY_CARS as ~carContract
    }
    val(TOO_MANY_CARS)
  }
 dealer(func: uid(DEALERS)) {
    dealerName
  	~contractDealer {
      contractID
      REQUIRED_CAR as ~carContract {
        carName     # should only include this car
      }
      val(REQUIRED_CAR)
      contractGroup {
        groupName
        WEIGHT as math(0.12) # would be saved in DB as groupWeight
        groupStat {
          statName
          ~carStat @facets(STAT_VALUE as statValue) @filter(uid(TOO_MANY_CARS)){ # doesn't work, includes both cars
          #~carStat @facets(STAT_VALUE as statValue) @filter(uid(REQUIRED_CAR)){ # doesn't work, result is empty
          # ~carStat @facets(STAT_VALUE as statValue) { # doesn't work, result has all cars
            carName
            sv1: val(STAT_VALUE) # should have only value from one car
          }
          sv2: SV2 as sum(val(STAT_VALUE)) # equal to sv1
        }
        sv3: SV3 as sum(val(SV2)) # equal to sv2 and sv1
        weightedScore: WS as math(WEIGHT * SV3)
      }
      weight2: WEIGHT2 as sum(val(WEIGHT)) # propagate down
      weightedScore2: WS2 as sum(val(WS)) # propagate down
    }
    weightTotal: WEIGHT3 as sum(val(WEIGHT2)) # aggregate
    scoreTotal: WS3 as sum(val(WS2))  # aggregate
    score: math(WS3 / WEIGHT3)  # finally calculate weighted score
  }
}

Of course for this query to work, I need:

  • improved filtering, as described in this issue,
  • correct facet values, which I described in another issue.
1 Like