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
      }
    }
  }
  
}
1 Like

Note:
Example of use case Query variables in Dgraph filter - Stack Overflow
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 - #4 by MichelDiz

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.

I just ran into this - any update on why this isn’t supported? Thanks!

No eyes on it so far. We need more concrete cases to support this. Some use cases I have created are easily done using @ignorereflex for example. Some others don’t, but very few good cases.

Here’s my use case:

I want to show Member communities that AREN’T the one that’s currently loaded.

{
  query(func: eq(Community.slug, "basb")) {
    community_uid AS uid
    members: Community.members {
      uid
      communities: Member.communities @filter(not uid(community_uid)) {
        uid
      }
    }
  }
}

@ignorereflex works for the above case with only one filter - but I have another one i want to apply, which is that I only want to show a member’s communities that are subcommunities of the root community. So communities that are:

  1. Not the root community
  2. Subcommunities of the root community

Any direction on how i’d do this with variables @MichelDiz?

Try

{

  var(func: eq(Community.slug, "basb")) {
     community_uid AS uid
  }

  query(func: uid(community_uid)) {
    members: Community.members {
      uid
      communities: Member.communities @filter(not uid(community_uid)) {
        uid
      }
    }
  }
}

I might have another use case, which I haven’t been able to solve.

Consider the following persons P1, P2 and P3, which is a connected through an edge (relation) with two time-based facets (from and to) to an intermediary node (N1, N2).

P1 --(from: "2021-06-01", to: "2021-06-03")--> N1 <--(from: "2021-05-28", to: "2021-06-05")-- P2 --(from: "2021-05-30", to: "2021-06-02")--> N2 <--(from: "2021-05-20", to: "2021-05-21")-- P3

I would like to query from P1 and expand relation but only if the time-ranges is intersecting.

I would like to get P1 --> N1 <-- P2 --> N2, but as the time-range from P2 --> N2 does not intersect with N2 <-- P3 the later is not followed.

Any way this is possible? Also, in our actual case, P1 multiple relation, as thus we need to filter the facet based on a variable from the parent block. Also, we would like to be able to make the query @recursive.