ElasticGraph Query API: Filter Conjunctions
$ curl -s https://block.github.io/elasticgraph/dc.yml | docker compose -f - up --pull always
ElasticGraph supports two conjunction predicates:
allOf- Matches records where all of the provided sub-filters evaluate to true.
This works just like an
ANDoperator in SQL.Note: multiple filters are automatically ANDed together. This is only needed when you have multiple filters that can’t be provided on a single filter input because of collisions between key names. For example, if you want to AND multiple OR’d sub-filters (the equivalent of (A OR B) AND (C OR D)), you could do
allOf: [{anyOf: [...]}, {anyOf: [...]}].When
nullor an empty list is passed, matches all documents. anyOf- Matches records where any of the provided sub-filters evaluate to true.
This works just like an
ORoperator in SQL.When
nullis passed, matches all documents. When an empty list is passed, this part of the filter matches no documents.
By default, multiple filters are ANDed together. For example, this query finds artists
formed after the year 2000 with BLUES as one of their genres:
query FindModernBluesArtists {
artists(filter: {
bio: {yearFormed: {gt: 2000}}
genres: {anySatisfy: {equalToAnyOf: [BLUES]}}
}) {
nodes {
name
bio {
yearFormed
description
}
}
}
}
ORing subfilters with anyOf
To instead find artists who were formed after the year 2000 OR play BLUES music, you
can pass the sub-filters as a list-of-objects to anyOf:
query FindModernOrBluesArtists {
artists(filter: {
anyOf: [
{bio: {yearFormed: {gt: 2000}}}
{genres: {anySatisfy: {equalToAnyOf: [BLUES]}}}
]
}) {
nodes {
name
bio {
yearFormed
description
}
}
}
}
anyOf is available at all levels of the filtering structure so that you can OR
sub-filters anywhere you like.
ANDing subfilters with allOf
allOf is rarely needed since multiple filters are ANDed together by default. But it can
come in handy when you’d otherwise have a duplicate key collision on a filter input. One
case where this comes in handy is when using anySatisfy to filter on a
list. Consider this query:
query ArtistsWithPlatinum90sAlbum {
artists(filter: {
albums: {
anySatisfy: {
soldUnits: {gte: 1000000}
releasedOn: {gte: "1990-01-01", lt: "2000-01-01"}
}
}
}) {
nodes {
name
albums {
name
releasedOn
soldUnits
}
}
}
}
This query finds artists who released an album in the 90’s that sold more than million copies. If you wanted to broaden the query to find artists with at least one 90’s album and at least one platinum-selling album–without requiring it to be the same album–you could do this:
query ArtistsWith90sAlbumAndPlatinumAlbum {
artists(filter: {
albums: {
allOf: [
{anySatisfy: {soldUnits: {gte: 1000000}}}
{anySatisfy: {releasedOn: {gte: "1990-01-01", lt: "2000-01-01"}}}
]
}
}) {
nodes {
name
albums {
name
releasedOn
soldUnits
}
}
}
}
GraphQL input objects don’t allow duplicate keys, so
albums: {anySatisfy: {...}, anySatisfy: {...}} isn’t supported, but allOf
enables this use case.
Warning: Always Pass a List
When using allOf or anyOf, be sure to pass the sub-filters as a list. If you instead
pass them as an object, it won’t work as expected. Consider this query:
query AnyOfGotcha {
artists(filter: {
bio: {
anyOf: {
yearFormed: {gt: 2000}
description: {matchesQuery: {query: "accordion"}}
}
}
}) {
nodes {
name
bio {
yearFormed
description
}
}
}
}
While this query will return results, it doesn’t behave as it appears. The GraphQL
spec mandates that list inputs coerce non-list values into a list of one
value. In this case,
that means that the anyOf expression is coerced into this:
query AnyOfGotcha {
artists(filter: {
bio: {
anyOf: [{
yearFormed: {gt: 2000}
description: {matchesQuery: {query: "accordion"}}
}]
}
}) {
# ...
}
}
Using anyOf with only a single sub-expression, as we have here, doesn’t do anything;
the query is equivalent to:
query AnyOfGotcha {
artists(filter: {
bio: {
yearFormed: {gt: 2000}
description: {matchesQuery: {query: "accordion"}}
}
}) {
# ...
}
}