Promise a nested block (under construction - I'm still working in the use case)

Update: I have opened this for open source discussion. And as I didn’t finish my use cases. Others could read this, fix my thoughts or add more use cases.

Use case 1

1 - Selecting films to which these directors have partnered in directing with other directors.

using var in a parent block is a cognitive jump to use it in nested blocks.

{
 var(func: eq(name@en, ["Ridley Scott", "Steven Spielberg"]) ) {
     name@en
    director.film  {
      initial_release_date
      name@en
      worked_with : ~director.film
        @filter(NOT eq(name@en, ["Ridley Scott", "Steven Spielberg"])) {
          WK as uid
          name@en
      }
    }
  }
        
  worked_withRidleyANDSteven(func: uid(WK) ) {
     name@en
  }
}

Result

{
  "data": {
    "worked_withRidleyANDSteven": [
      {
        "name@en": "John Woo"
      },
      {
        "name@en": "Emir Kusturica"
      },
      {
        "name@en": "Kátia Lund"
      },
      {
        "name@en": "Mehdi Charef"
      },
      {
        "name@en": "Stefano Veneruso"
      },
      {
        "name@en": "Spike Lee"
      },
      {
        "name@en": "George Miller"
      },
      {
        "name@en": "Danny DeVito"
      },
      {
        "name@en": "John Landis"
      },
      {
        "name@en": "Joe Dante"
      },
      {
        "name@en": "Jordan Scott"
      }
    ]
  }
}

1 -

{
 query(func: eq(name@en, ["Ridley Scott", "Steven Spielberg"]) ) @filter(has(director.film)) {
    # GET_US as uid
     name@en
    director.film @filter(gt(count(~director.film), 1)) {
      initial_release_date
      name@en
      partnered_along_with : ~director.film 
      @filter(NOT eq(name@en, ["Ridley Scott", "Steven Spielberg"])) # @filter(NOT uid(GET_US))
        {
          name@en
      }
    }
  }
}

2-

{   query(func: eq(name@en, ["Ridley Scott", "Steven Spielberg"])) @filter(has(director.film)) {
    # GET_US as uid
     uid
     name@en
    director.film @filter(gt(count(~director.film), 1)) {
      initial_release_date
      name@en
      partnered_along_with : ~director.film 
      @filter(NOT uid(0x1693f, 0x4b928)) # @filter(NOT uid(GET_US))
        {
          name@en
      }
    }
  }
}

3 - This query doesn’t work

For some reason, the GET_US isn’t passed to the nested block.
if you use the filter @filter(NOT uid(0x1693f, 0x4b928)) it works just fine.
In the context of this query, the var GET_US should pass the UID from the target director. e.g:

Ridley Scott 
=> `partnered_along_with : ~director.film @filter(NOT uid(GET_US))` 
should parse the var GET_US as 
 => `partnered_along_with : ~director.film @filter(NOT uid(0x1693f))` 
Steven Spielberg
=> `partnered_along_with : ~director.film @filter(NOT uid(GET_US))` 
should parse the var GET_US as 
 => `partnered_along_with : ~director.film @filter(NOT uid(0x4b928))` 
{
   query(func: eq(name@en, ["Ridley Scott", "Steven Spielberg"])) @filter(has(director.film)) {
    GET_US as uid
     name@en
    director.film  {
      initial_release_date
      name@en
      partnered_along_with : ~director.film @filter(NOT uid(GET_US)) {
          test : val(GET_US)
          name@en
      }
    }
  }
}

4 - The wise way of doing it (For THIS use case only)

is by using “count”, but this is “hackish”, using var in a parent block is a cognitive jump to use it in nested blocks.

What I did below is simple and straightforward, if I want movies where there is more than one director, then I can use “count” to check this out for me a nested block before it.

{   
query(func: eq(name@en, ["Ridley Scott", "Steven Spielberg"])) @filter(has(director.film)) {
    GET_US as uid
     name@en
    director.film @filter(gt(count(~director.film), 1)) {
      initial_release_date
      name@en
      partnered_along_with : ~director.film {
          test : val(GET_US)
          name@en
      }
    }
  }
  
}

Note:
Example of use case https://stackoverflow.com/questions/55741592/query-variables-in-dgraph-filter
Instead of two blocks, we could solve it by creating a “promisse”.

Another “Promise” usage.

The dataset in the end.

Consider this query.

I want to return friends from a particular user and filter these friends’ students at the same school as the user.

This first query does it, but you need to know the UID from the school in hand

{
  q(func: uid(0x4e36)){
    uid
    name
    jobTitle
    studied_at { name }
    friend @filter(uid_in(studied_at, "0x4e3b")){
      name
    }
  }
}

To make it work, we need to solve this “Add support of Value Variables for uid_in. #3066” and support pass a value variable to a nested block. Which is the main point of this topic “Promise a nested block”.

if we fix only the 3066 the query would be like

{
    var(func: uid(0x4e36)){
    STAT as studied_at
  }
  q(func: uid(0x4e36)){
    uid
    name
    jobTitle
    studied_at { name }
    friend @filter(uid_in(studied_at, val(STAT))){
      name
    }
  }
}

But if we fix 3066 and also add support to pass a value variable to a nested block the query would be like - And that one would be just perfect

{
  q(func: uid(0x4e36)){ 
    uid
    name
    jobTitle
    STAT as studied_at
    friend @filter(uid_in(studied_at, val(STAT))){
      name
    }
  }
}

#And of course this would be nice to wide queries like

I want to return friends from all users and filter these friends’ students at the same school as the user.

{
  q(func: type(Person)){
    uid
    name
    jobTitle
    STAT as studied_at
    friend @filter(uid_in(studied_at, val(STAT))){
      name
    }
  }
}

Dataset for this

{
	"set": [{
			"uid": "_:School1",
			"dgraph.type": "School",
			"name": "Main City School",
			"telephone": "(425) 123-4567",
			"url": "http://www.maincityschool.com"
		},
		{
			"uid": "_:School2",
			"dgraph.type": "School",
			"name": "Other City School",
			"telephone": "(425) 333-4567",
			"url": "http://www.othercityschool.com"
		},
		{
			"uid": "_:Jane",
			"dgraph.type": "Person",
			"name": "Jane Doe",
			"jobTitle": "Student",
			"studied_at": [{
				"uid": "_:School1"
			}],
			"friend": [{
					"uid": "_:Lucas"
				},
				{
					"uid": "_:Noah"
				},
				{
					"uid": "_:Olivia"
				},
				{
					"uid": "_:Sophia"
				}
			]
		},
		{
			"uid": "_:Lucas",
			"dgraph.type": "Person",
			"name": "Lucas Doe",
			"jobTitle": "Student",
			"studied_at": [{
				"uid": "_:School1"
			}],
			"friend": [{
					"uid": "_:Jane"
				},
				{
					"uid": "_:Olivia"
				},
				{
					"uid": "_:Noah"
				},
				{
					"uid": "_:Sophia"
				}
			]
		},
		{
			"uid": "_:Olivia",
			"dgraph.type": "Person",
			"name": "Olivia Doe",
			"jobTitle": "Student",
			"studied_at": [{
				"uid": "_:School1"
			}],
			"friend": [{
					"uid": "_:Jane"
				},
				{
					"uid": "_:Lucas"
				},
				{
					"uid": "_:Noah"
				},
				{
					"uid": "_:Sophia"
				}
			]
		},
		{
			"uid": "_:Noah",
			"dgraph.type": "Person",
			"name": "Noah Doe",
			"jobTitle": "Student",
			"studied_at": [{
				"uid": "_:School2"
			}],
			"friend": [{
					"uid": "_:Jane"
				},
				{
					"uid": "_:Lucas"
				},
				{
					"uid": "_:Olivia"
				},
				{
					"uid": "_:Sophia"
				}
			]
		},
		{
			"uid": "_:Sophia",
			"dgraph.type": "Person",
			"name": "Sophia Doe",
			"jobTitle": "Student",
			"studied_at": [{
				"uid": "_:School2"
			}],
			"friend": [{
					"uid": "_:Jane"
				},
				{
					"uid": "_:Lucas"
				},
				{
					"uid": "_:Olivia"
				},
				{
					"uid": "_:Noah"
				}
			]
		}
	]
}

More case with above dataset:

Find friends that have studied at the same school as mine.

{
  q(func: type(Person), first:1) @cascade{
    uid
    name
    jobTitle
    studied_at {
      uid
      name
    }
    friend {
      name
      studied_at @filter(eq(name, "Main City School")) {
      uid
      name
    }
    }

  }

}

Result (This is a desired result)

{
  "data": {
    "q": [
      {
        "uid": "0x1",
        "name": "Jane Doe",
        "jobTitle": "Student",
        "studied_at": [
          {
            "uid": "0x6",
            "name": "Main City School"
          }
        ],
        "friend": [
          {
            "name": "Lucas Doe",
            "studied_at": [
              {
                "uid": "0x6",
                "name": "Main City School"
              }
            ]
          },
          {
            "name": "Olivia Doe",
            "studied_at": [
              {
                "uid": "0x6",
                "name": "Main City School"
              }
            ]
          }
        ]
      }
    ]
  }
}

But, we need a clean query tho

{
  q(func: type(Person), first:1) @cascade{
    uid
    name
    jobTitle
    studied_at {
      uid
      STAT as name
    }
    friend {
      name
      studied_at @filter(eq(name, val(STAT))){
      uid
      name
    }
    }

  }

}

This query above isn’t possible to run. Cuz We do not pass the variable value to the next nested block. So that’s why we need to “promise” it.

But, we need an even cleaner query tho!

This one would be perfect.

{
  q(func: type(Person)){
    uid
    name
    jobTitle
    studied_at {
     STAT as uid
     name
    }
    friend @filter(uid_in(studied_at, val(STAT))){
      name
    }
  }

}

I have opened this for open source discussion. And as I didn’t finish my use cases. Others could read this, fix my thoughts or add more use cases.

Also see Howto filter by propagating a variable

Hello @MichelDiz,

I’m also in the need of such a mechanism.

I thought I would already work as the Query Variables doc says Query variables can be used in other query blocks or in a child node of the defining block.
But in the child node, the variable exists but is empty.

If I understand well, current variables are global and here we need variables with a scope local to the block and passed to its children.

Using 2 blocks only works if we manipulate one element, but when dealing with multiple elements, the variable merges all UIDs and the result is wrong.