Once enabled, new filters become available in the GraphQL Content Delivery API for the specified field.
To be precise, there is a second necessary condition to see the newly activated GraphQL filters: the field must accept at least one block type. If the field does not allow embedding any block types within it, then deep filtering is not applicable — there's nothing to filter! — and therefore the GraphQL filters relative to deep filtering will not be present.
Check out this blog entry for an in-depth look at use cases where deep filtering can simplify accessing specific data without multiple API calls, enhancing performance and efficiency.
In the following examples, let's consider a blog_post
model with a content
Modular Content field that accepts two types of blocks: hero
and product
.
To retrieve blog_post
records that contain a product
block with a name
equal to "T-Shirt"
, and a price
greater than 30, you can use the following GraphQL query:
query {allBlogPosts(filter: {content: {any: { product: { name: { eq: "T-Shirt" }, price: { gt: 30 } } }}}) {# ...}}
In other words, within a field with deep filtering enabled, you can specify an any
key where, for each block type that the field accepts, you can define one or more filtering conditions. The word "any" can be read as: "find records where any product block respects these conditions".
The filtering conditions are the same ones discussed in the previous section on Filtering records, with the only difference being that since blocks don't have meta fields like ie. _firstPublishedAt
they cannot be used in this context. The only meta key available for blocks is id
.
If you specify conditions for more than one type of block, then all conditions must be respected.
query {allBlogPosts(filter: {content: {any: {product: { name: { eq: "T-Shirt" } }hero: { title: { matches: { pattern: "offer" } } }}}}) {# ...}}
The example above will search for all blog posts that have both a product
block called "T-Shirt"
, and a hero block with a title that contains the term "offer"
.
If you want to apply logical OR conditions between various conditions, you can always use the OR
filter.
The previous query can be modified to return blog posts that have either a "T-Shirt" product
block or an "offer" hero
block, like this:
query {allBlogPosts(filter: {OR: [{content: {any: { product: { name: { eq: "T-Shirt" } } }}},{content: {any: { hero: { title: { matches: { pattern: "offer" } } } }}}]}) {# ...}}
If you are only interested into filtering records that, in a particular field, contain at least one block, regardless of its type, you can use the exists
filter:
query {allBlogPosts(filter: {content: { exists: true }}) {# ...}}
Inverting the condition into exists: false
will find all blog posts that don't have any block in the field.
If you want to be more specific and filter records that contain at least one block of one or more specific types, then you can use the "containsAny": true
filter. You can also search for records that do not contain any blocks of a specific type with"containsAny": false
.
The following query returns all blog posts that contain at least one block of type product
, but do not contain any blocks of type hero
:
query {allBlogPosts(filter: {content: {containsAny: { product: true, hero: false }}}) {# ...}}
When deep filtering is not enabled on a Structured Text field, its GraphQL filters allow to filter by its textual content only, like this:
query {allProducts(filter: {structuredTextField: {matches: { pattern: "bi(cycl|k)e", caseSensitive: false }}}) {# ...}}
However, when you activate deep filtering, the filter format will change, and the value
argument will group together all filters related to the textual content.
To put it differently, when deep filtering is turned on, the previous query needs to be rewritten as:
query {allProducts(filter: {structuredTextField: {value: { # this argument has been introducedmatches: { pattern: "bi(cycl|k)e", caseSensitive: false }}}}) {# ...}}
We just saw that enabling deep filtering on a Structured Text field will cause a change to the associated GraphQL filter type. This means that existing GraphQL queries may need to be rewritten in order to avoid errors.
This is another reason why deep filtering is activable or not on a per-field basis: so that you are in control of when (and how) to introduce this change.
To avoid unpleasant surprises in production, it is a good idea to test the switch to deep filtering for Structured Text fields in a a sandbox environment first, and see if it breaks any of your existing GraphQL queries.
All the query possibilities in deep filtering mentioned above for the Modular Content fields also apply to the Structured Text fields.
The only distinction is that all the filter arguments related to blocks are nested inside the blocks
argument:
query {allProducts(filter: {structuredTextField: {blocks: {any: { cta: { title: { eq: "Subscribe!" } }},value: {matches: { pattern: "bi(cycl|k)e", caseSensitive: false }}}}) {# ...}}}
Deep filtering is currently limited to only one level of depth. That is, you cannot filter records based on the content of blocks deeply nested inside other blocks. For example, if you have:
A modular content field
A parent block with a "parent title" field and another modular content field called "child blocks"
Inside a child block, you have another field called "child title"
You can filter by the "parent title" but you cannot filter by the "child title".
Deep filtering is currently limited to the Content Delivery API (CDA / GraphQL). You cannot deep filter records in the Content Management API (CMA / REST).