Been here and tackled this one…
There are several different ways to do it, and I even use several ways depending on my use case at the time.
Barriers to cross:
- Solve the basic syntax
- Decide if filters are single level or multi-level and use var blocks when needed
- Build it dynamically with your favorite scripting language (ie: JS)
1. Solve the basic syntax
This is actually much easier in DQL right now than it is in GQL. In DQL, all you need to do is have a single root function and then join the other filters with conjunctions in a filter directive:
query {
Users(func: type(User)) @filter(eq(favoriteDrink,"XXXX") AND eq(favoriteFood,"XXXX") AND eq(favoriteCountry:"XXXX")) {
uid
}
}
So here we can just add the type function as the root and use the AND conjunction to join the filter functions together in a filter directive. You can also use OR and NOT. Use parenthesis where needed to make specific enclosures.
Now here everything is on a single level and all with AND, what if we need to filter on multiple levels and use a mix of conjunctions?
2. Decide if filters are single level or multi-level and use var blocks when needed
To filter for data at different levels with a mix of conjunctions, the idea is to create a var block for every filter and then combine them in the end. Note: This may degrade performance based on use cases.
query {
var1 as var(func: eq(favoriteColor,"X"))
var(func: eq(interest,"Y")) {
var2 as forUser
}
var(func: eq(city,"Z")) {
usedInAddress {
var3 as forUser
}
}
Users(func: type(User)) @filter((uid(var1) OR uid(var2)) AND uid(var3)) {
uid
}
}
In this example we are looking for users with either the same favoriteColor (X) or a similar edge to a common interest (Y) and within a city (Z) that is used by addresses that are for the user. The var blocks go a long way to solve these advanced queries. I would test every var block to see if it performs better top to bottom or bottom to top. Some blocks may work better if flipped upside down.
3. Build it dynamically with your favorite scripting language (ie: JS)
Here you can simply use your favorite scripting language to build the query. I will just illustrate the simple format where var blocks and nested queries are not needed. (not a full working example)
const type = "User"
const conj = "AND"
const filters = []
filters.push({pred:"favoriteDrink",op:"eq",val:"XXXX"})
filters.push({pred:"favoriteFood",op:"eq",val:"XXXX"})
filters.push({pred:"favoriteCountry",op:"eq",val:"XXXX"})
let query += `query { ${type}s(func: type(${type})) @filter(`
filters.forEach((f,i) => {
const c = (i>0) ? ` ${conj} ` : ""
query += `${c}${f.op}(${f.pred},"${f.val}")`
})
query += ") { uid } }"