The introduction of deep filtering to DatoCMS has given developers the ability to filter records based on the content within their embedded blocks. This new feature of the Content Delivery API opens up many possibilities, reducing the need to make multiple API calls to get the desired data.
In this guide, you will understand what deep filtering is, why it was added to DatoCMS, and learn how to use it through many real-world examples.
Let’s dive in!
What Is Deep Filtering in DatoCMS?
Deep filtering is a feature of the Content Delivery API that allows you to filter records based on the content of their blocks. If you are not familiar with this concept, in DatoCMS blocks are data structures that users can embed in specific record fields. In other words, a block represents a sort of graphical component—such as a call to action, slider, and testimonial quote.
There are two types of block fields:
Structured Text: A field type to create rich text content through a Notion-like editor (e.g., the rich text field of a blog post record).
Modular Content: A special field type to define a dynamic area for richer page layouts (e.g. the design area field of a landing page record).
When a record has a Structured Text or Modular Content field, content creators can populate it with one or more blocks. Now, imagine you want to retrieve some records by the content of those fields. For example, you want to get only blog posts or landing pages that contain some specific information in a given block. Thanks to deep filtering, that is now possible!
In detail, deep filtering enables you to specify filtering conditions for blocks embedded in a Structured Text or Modular Content field. This new mechanism provides a simplified way to access the exact data you need without unnecessary additional API calls or application-level filtering operations. That means faster data retrieval, which results in improved server response time.
How To Get Started With Deep Filtering
Follow the steps below and learn how to enable the deep filtering feature in your DatoCMS project.
Prerequisites
Deep filtering works on a per-field basis. Specifically, you can enable it only on the Structured Text and Modular Content fields of a given model. Thus, your content schema must involve some models with at least one field of one of those two types.
Assume you are building a blog application that only requires two models: Article and Landing page. You could define the Article model as follows:
And the Landing Page model as:
Note that the two models involve a Structured Text and Modular Content field, respectively.
Keep in mind that DatoCMS allows you to specify which blocks content editors can embed in the field. If the field does not accept any block type, it will always be empty and GraphQL deep filters will not be applicable.
In this example, we will consider three block types: Call to Action, Hero, and Product. This is how they are defined:
Summing up, the two prerequisites to enable deep filtering on a field are:
It must be a Structured Text or Modular Content field.
It must accept at least one block type.
Enable the Deep Filtering Feature on a Field
Hover the Structured Text or Modular Content field and click the “Edit Field” button on the right:
The following modal will show up:
To turn on the desired feature, toggle the “Enable deep filtering in GraphQL?” slider:
Before clicking the “Save field” button, make sure the block field accepts at least one block type. Reach the “Validations” tab, and verify that there are a few blocks allowed for the field as in the Modular Content sample field below:
Great, you just enabled deep filtering! You can now employ the GraphQL deep filters to the Content Delivery API calls. Learn how in the next two chapters.
Deep Filtering Applied To a Modular Content Field: Use Cases
Modular Content fields are ideal for defining records that require dynamic layouts. Some examples? Landing pages, case studies, and portfolio pages. In a website or web application, those are all key pages!
Learn how to filter those records with deep filtering through real-world GraphQL sample queries.
In the schema used in this blog post, the target model for this section is landing_page
. In particular, the Modular Content field to apply deep filters on is content
. The blocks supported by content
are call_to_action
, hero
, and product
.
Single Block Filtering
Landing page records can involve more than one block. However, suppose you are interested in filtering them based on the content of blocks of a specific type only.
Use the query GraphQL below to get all landing_page
instances that contain a call_to_action
block whose title
is equal to the “Discover all products!” string:
As you can see, when a field has deep filtering enabled, you can then specify an any
object. This can contain a property for each block type that the field accepts. In these properties, you can add one or more filtering conditions as explained in the documentation. In other terms, the keyword any
reads as “find records where any X/Y/Z block respects these conditions”.
That query would produce a result like this:
Out of all the landing pages available, it retrieved only the ones matching the specified filtering criteria.
Note that you can add more than one filtering condition. For example, you could look for all landing pages that have a product
block whose name
contains “Course” and price
is lower than 50:
query { allLandingPages( filter: { content: { any: { product: { name: { matches: { pattern: "Course" } } price: { lt: "50" } } } } } ) { id title }}
This time, the result will be:
Multiple Block Filtering
Block filtering is not limited to a single block but can applied to multiple block types.
Write the GraphQL query that follows to get all landing pages that have both a hero
block with a “Join our TypeScript course!“ title
and a product
whose price
is greater than 50:
query { allLandingPages( filter: { content: { any: { hero: { title: { eq: "Join our TypeScript course!" } } product: { price: { gt: "50" } } } } } ) { id title }}
To define specific filtering conditions on multiple blocks, all you have to do is specify more than one property in the any
object. By default, DatoCMS enforces the AND operator between each.
The result will be:
For the OR operator, you need instead a different syntax. Now, you want to get all landing pages that have a hero
block with the “Join our JavaScript course!“ title
or a product
block whose price
is greater than 50. Achieve that with:
query { allLandingPages( filter: { OR: [ { content: { any: { hero: { title: { eq: "Join our JavaScript course!" } } } } } { content: { any: { product: { price: { gt: "50" } } } } } ] } ) { id title }}
The new returned data will be:
This time, the result also contains the “Discounted JavaScript Course” landing page.
Block Presence Filtering
Together with filtering on the content of blocks, you can also filter records based on the presence of at least one block with the exists
operator:
query { allLandingPages( filter: { content: { exists: true } } ) { id title }}
The above GraphQL query will return all landing pages that have at least one block:
If you are instead interested in the presence of a specified block type, you need to use the containsAny
operator:
query { allLandingPages( filter: { content: { containsAny: { product: false, callToAction: true } } } ) { id title }}
You will now get all landing pages that have a call_to_action
block but have not a product
block:
Deep Filtering Records Based on a Structured Text Field: Use Cases
Structured Text is a versatile field type for defining rich textual content. DatoCMS provides some built-in elements, such as headers, code snippets, lists, and more, but you can also extend it to accept custom blocks.
In the schema used in this blog post, the target model for this section is article
. In detail, the Structured Text field to apply deep filtering on is content
. The only custom block supported by content
is call_to_action
:
See how to filter records with these types of fields via GraphQL deep filters.
Rich Text Content Filtering
Before getting started, you need to know that the Content Delivery API already supports filtering records based on the content of a Structured Text field. Thus, you do not need deep filtering for that.
For example, you can get all blog posts that contain the word “JavaScript” or “TypeScript” with this GraphQL query:
query { allArticles( filter: { content: { matches: { pattern: "(Java|Type)script" } } } ) { id title subtitle }}
Notice the use of a complex pattern to implement the intended search strategy. That query will work like a charm:
However, that GraphQL query would no longer work when you enable deep filtering on the content
field. You would get the following error:
Why? Because deep filtering on a Structured Text field will cause a change to the associated GraphQL filter type! After enabling it, the equivalent query will become:
query { allArticles( filter: { content: { value: { matches: { pattern: "(Java|Type)script" } } } } ) { id title subtitle }}
Basically, you just have to wrap content
with the value
object.
Et voilà! You’ll get the same result as before this time:
Note: None of these approaches will consider the content within the custom blocks. So, if an article does not contain the term “JavaScript” in the rich-text section but contains it in a call_to_action
block, the queries will not return it.
Block Filtering
Deep filtering on Structured Text gives you the ability to apply filtering conditions to custom blocks contained in the field as well. Specifically, you can use all deep filters that are valid for Modular Content fields.
This is how you can do it:
query { allArticles( filter: { content: { blocks: { containsAny: { callToAction: true } }, value: { matches: { pattern: "CMS" } } } } ) { id title subtitle }}
The blocks
object accepts filters as mentioned in the previous chapter. Those conditions will only be applied to the blocks contained in the field.
The above query will return all articles that contain the word “CMS” and has a call_to_action
block:
Congrats! You are now a DatoCMS deep filtering master!
Pros and Cons of Deep Filtering
Time to dig into the benefits and limitations of the feature.
👍 Pros:
Enhanced filtering capabilities: You can now retrieve records based on the content of their blocks.
Granular control: The deep filtering feature must be enabled on a per-field basis, so you are in control of when to use it.
Intuitive GraphQL syntax: Writing queries involving deep filters is intuitive and supported by the Explorer feature on the Content Delivery API Playground page.
Many filtering options available: All filtering conditions available for record filtering also work for deep record filtering.
👎 Cons:
Breaking change in the GraphQL schema with Structured Text fields: Enabling deep filtering on Structured Text fields leads to breaking changes in the existing GraphQL schema. So, before enabling it in your primary environment, test it on a sandbox environment.
No nested filtering: As of this writing, deep filtering is limited to one level of depth. That is, you cannot filter records by the content of blocks nested inside other blocks of a field.
Conclusion
In this article, you dug into the deep filtering feature supported by the DatoCMS Content Delivery API. In particular, you understood that it enables you to filter records based on the content within their blocks.
You now know:
What deep filtering is.
How it works.
How to enable it on a Structured Text or Modular Content field.
How to use it in different scenarios.
DatoCMS keeps improving and the list of features gets longer every month. Want to give it a try? Sign up for free today and get your hands on one of the most feature-rich headless CMSs on the market!
Thanks for reading! We hope that you found this article helpful. Feel free to reach out to us with any questions, comments, or suggestions. Consider also joining our community on Slack.