Display Filter & Base Filter Migration

Added in: release-2023-07

Display Filter Migration

The structure of display filters has changed. Instead of the typical type and value properties alongside the label there are now three distinct properties that can be used to change the filter functionality. The main one is the filter property, which is an object containing the filter query using the new Search Filters Query DSL. There is also a context property which can be used by Custom Filters. Finally, there is a sort property, which is a string that replaces the 'sortBy' filter type.

Type/Value Filters

The type/value combination should be migrated to new filter attribute:

-   id: 'published',
    label: 'Published Documents',
-   type: 'documentState',
-   value: 'published'
+   filter: {key: 'lastPublicationId', exists: true}

For additional filter examples please see the Migration Examples section below.

Custom Filters

Migrate the customFilter value to the new context attribute:

-   id: 'published',
    label: 'My Custom Filter',
-   type: 'customFilter',
-   value: {foo: 'bar'}
+   context: {foo: 'bar'}

SortBy Filter

Migrate the sortBy value to the new sort attribute:

-   id: 'sortByUpdated',
    label: 'Recently updated documents first',
-   type: 'sortBy',
-   value: '-updatedAt'
+   sort: '-updatedAt'

Base Filter Migration

Base Filters have not changed as significantly as Display Filters, as they are equivalent to the filters property within the Display Filter options. The same Search Filters Query DSL is used within the object.


The 'sortBy' filter should be replaced with a sort property on the root of the dashboard config:

    type: 'tableDashboard',
    handle: 'gallery-dashboard',
    pageTitle: 'Gallery Board',
    // ...
-   baseFilters: [
-     {type: 'contentType', value: 'gallery'},
-     {type: 'sortBy', value: '-updatedAt'}
-   ],
+   sort: '-updatedAt',
+   baseFilters: [
+     {key: 'contentType', term: 'gallery'}
+   ]


The filter configuration has changed from type and value properties to key and query expressions. The current supported query expressions are term, range and exists.

A simple legacy filter might look like this:

{type: 'contentType', value: 'regular'}

The new filter should look like this:

{key: 'contentType', term: 'regular'}

Below you will find specific examples of how to migrate different legacy filters. These objects can be used for baseFilters, the displayFilters filters property, and for Public API Search Filters.

Migration Examples

Simple Key/Value Filters

As mentioned above, these are straightforward to migrate. The type property should be renamed to key, and the value property should be renamed to term.

The values should be of the correct type, so string for 'contentType', 'documentType', 'mediaType', 'reference', and integer for id, channelId, ownerId, createdBy. An array of values can also be passed, or you can use a range query expression, or exists if you want to know if the property has been indexed.


Metadata queries will vary based on the desired query expression and the value type.

For metadata values which are stored as objects you must always use a nested property to check if it exists. For example, if you would like to check if a task has been defined you should use {key: 'metadata.myTaskHandle.state', exists: true}. Using {key: 'metadata.myTaskHandle', exists: true} will throw an error because the field 'metadata.myTaskHandle' is not indexed, only the individual properties within it such as 'metadata.myTaskHandle.state'.


References are stored as keywords, so you can use any of the query expressions listed below. The important thing to remember is to query the nested reference property where the id is stored, and not the top-level metadata handle.

{key: 'metadata.myDocumentReference.reference.id', term: 1}
{key: 'metadata.myDocumentReferences.references.id', term: 1}
{key: 'metadata.myCategory.id', term: 1}
{key: 'metadata.myVideoReference.reference.id', term: ['abc123', 'def456']}

Value / Term

For standard term queries you should provide the correct value type, depending on the indexing config of the metadata plugin. You can also provide an array of values.


{type: 'metadata', key: 'myMetadataHandle', value: 'myValue'}
{type: 'metadata', key: 'myMetadataHandle', value: 1}
{type: 'metadata', key: 'myMetadataHandle', value: ['myFirstValue', 'mySecondValue']}


{key: 'metadata.myMetadataHandle', term: 'myValue'}
{key: 'metadata.myMetadataHandle', term: 1}
{key: 'metadata.myMetadataHandle', term: ['myFirstValue', 'mySecondValue']}


Previously there was support for exists and value.exists properties, but this is now just the top-level exists, along with the key:


{type: 'metadata', key: 'myMetadataHandle', exists: true}
{type: 'metadata', key: 'myMetadataHandle', value: {exists: false}}


{key: 'metadata.myMetadataHandle', exists: true}
{key: 'metadata.myMetadataHandle', exists: false}

Date Range / Range

The dateFilter and rangeFilter properties have been replaced by the range property. If you’re also using from and to you should replace them with gte and lte:


{type: 'metadata', key: 'myMetadataHandle', dateFilter: {from: '2023-07-04T00:00:00.000Z', to: '2023-07-04T23:59:59.999Z'}}
{type: 'metadata', key: 'myMetadataHandle', rangeFilter: {from: '2023-07-04T00:00:00.000Z', to: '2023-07-04T23:59:59.999Z'}}


{key: 'metadata.myMetadataHandle', range: {gte: '2023-07-04T00:00:00.000Z', lte: '2023-07-04T23:59:59.999Z'}}
{key: 'metadata.myMetadataHandle', range: {gte: '2023-07-04T00:00:00.000Z', lte: '2023-07-04T23:59:59.999Z'}}

The optional: true parameter requires an OR filter to check for the existence of the value:


{type: 'metadata', key: 'myMetadataHandle', dateFilter: {gte: '2023-07-04T00:00:00.000Z', lt: '2023-07-05T00:00:00.000Z', optional: true}}


{or: [
  {key: 'metadata.myMetadataHandle', exists: false},
  {key: 'metadata.myMetadataHandle', range: {gte: '2023-07-04T00:00:00.000Z', lt: '2023-07-05T00:00:00.000Z'}}

See Date Range for more examples.


The 'locale' filter type has been renamed to 'language':


{type: 'locale', value: 'en'}
{type: 'locale', value: ['en', 'de']}


{key: 'language', term: 'en'}
{key: 'language', term: ['en', 'de']}


For NOT queries you can use the new logical operator in the Filter Query DSL:


{type: 'notContentType', value: 'author'}
{type: 'notContentType', value: ['author', 'page']}


{not: {key: 'contentType', term: 'author'}}
{not: {key: 'contentType', term: ['author','page']}}


The new range property supports gte, lte, gt, and lt:


{type: 'dateRange', key: 'myDateHandle', gte: '2023-07-04T00:00:00.000Z'}
{type: 'dateRange', key: 'myDateHandle', gte: '2023-07-04T00:00:00.000Z', lte: '2023-07-04T23:59:59.999Z'}
{type: 'dateRange', key: 'myDateHandle', gt: '2023-07-03T23:59:59.999Z', lt: '2023-07-05T00:00:00.000Z'}
{type: 'dateRange', key: 'myDateHandle', gte: '2023-07-04T00:00:00.000Z', lt: '2023-07-05T00:00:00.000Z'}


{key: 'myDateHandle', range: {gte: '2023-07-04T00:00:00.000Z'}}
{key: 'myDateHandle', range: {gte: '2023-07-04T00:00:00.000Z'}}
{key: 'myDateHandle', range: {gte: '2023-07-04T00:00:00.000Z', lte: '2023-07-04T23:59:59.999Z'}}
{key: 'myDateHandle', range: {gt: '2023-07-03T23:59:59.999Z', lt: '2023-07-05T00:00:00.000Z'}}
{key: 'myDateHandle', range: {gte: '2023-07-04T00:00:00.000Z', lt: '2023-07-05T00:00:00.000Z'}}

Instead of from and to, gte and lte should be used:


{type: 'dateRange', key: 'myDateHandle', from: '2023-07-04T00:00:00.000Z', to: '2023-07-04T23:59:59.999Z'}


{key: 'myDateHandle', range: {gte: '2023-07-04T00:00:00.000Z', lte: '2023-07-04T23:59:59.999Z'}}


To see if a document is currently published you can check if lastPublicationId exists:


{type: 'published', value: true}
{type: 'published', value: false}


{key: 'lastPublicationId', exists: true}
{key: 'lastPublicationId', exists: false}


The various 'documentState' filters can be replicated using other available properties:


{type: 'documentState', value: 'published'}
{type: 'documentState', value: 'unpublished'}
{type: 'documentState', value: 'draft'}
{type: 'documentState', value: 'publishedWithDraft'}


{key: 'lastPublicationId', exists: true}
{key: 'lastPublicationId', exists: false}
{key: 'hasDraft', term: true}
{and: [{key: 'hasDraft', term: true}, {key: 'lastPublicationId', exists: true}]}


To recreate the 'userInTeam' filter type there is a special termPattern property which uses the current user’s id:


{type: 'userInTeam', key: 'myTeamHandle'}


{key: 'metadata.myTeamHandle.activeUsers.id', termPattern: '{{ userId }}'}


The 'buyInNotExpired' replacement uses a relative time range:


{type: 'buyInNotExpired', key: 'myBuyInHandle'}


{key: 'metadata.myBuyInHandle.workflow.expiryDate', range: {gte: 'now/d'}}

The /d rounds to the start of the day. Along with 'now/d' there is also support for 'now', 'now-1d', and 'now-1d/d'.