Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions docs/developer-docs/6.x/headless-cms/advanced-filtering.ai.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
AI Context: Advanced Filtering (advanced-filtering.mdx)

## Source of Information

1. `docs/developer-docs/5.x/headless-cms/basics/using-graphql-api-advanced-filtering.mdx` — original v5 article; ported and updated for v6
2. `docs/developer-docs/6.x/headless-cms/using-webiny-sdk.mdx` — SDK usage patterns (listEntries, where syntax)

## Key Changes from v5 Article

1. Raw GraphQL queries replaced with `sdk.cms.listEntries({ where: ... })` — v6 docs use SDK, not raw GraphQL
2. User field filters now use `values.` prefix (e.g. `values.title_contains`, `values.category`) — in v5 fields were at the root
3. System meta fields (`createdOn`, `savedOn`, etc.) do NOT use `values.` prefix — they stay at the root
4. Removed "In the 5.x version of Webiny we introduced..." — no version references
5. Removed link to `date-time-and-identity-meta-fields` reference (no v6 equivalent) — replaced with inline note
6. Removed `meta` block from v5 raw GraphQL examples (not relevant in SDK context)
7. Reference field filter values are strings (entry IDs), not integers — updated examples accordingly

## Understanding

### Filter key convention
- User-defined fields: `values.{fieldId}_{operator}` (e.g. `values.title_contains`, `values.price_gte`)
- System fields: `{fieldId}_{operator}` (e.g. `createdOn_between`, `savedOn_gte`)
- Reference fields: filter by ID string, e.g. `"values.category": "some-entry-id"`

### AND semantics
- All conditions in the array must match
- Multiple root-level filters behave as implicit AND
- Use AND array when you need to repeat the same operator key (not possible at root level)

### OR semantics
- At least one condition must match
- Multiple filters within a single OR branch are implicitly ANDed together

### Nesting
- AND and OR can be nested indefinitely
- Deep nesting has performance implications, especially on DynamoDB-only deployments

### SDK usage
```typescript
sdk.cms.listEntries<Article>({
modelId: "article",
fields: [...],
where: {
OR: [...],
AND: [...],
"values.someField_operator": value
}
})
```

## Related Documents

- `using-webiny-sdk.mdx` — full SDK reference for listEntries, filters, pagination
- `graphql-api-overview.mdx` — overview of the three APIs
- `content-models-via-code.mdx` — field IDs used in filter keys come from content model definitions

## Tone Guidelines

- Lead each example with a plain-English description of what is being searched for
- Show the "equivalent condition" as a pseudo-SQL string after each query — helps readers verify understanding
- Keep examples practical (article/category/author model)
- No analogies, no marketing language
273 changes: 273 additions & 0 deletions docs/developer-docs/6.x/headless-cms/advanced-filtering.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
---
id: hcms6af01
title: Advanced Filtering
description: Learn how to use AND and OR conditionals to build complex filters when querying Headless CMS entries.
---

import { Alert } from "@/components/Alert";

<Alert type="success" title="WHAT YOU'LL LEARN">

- How to use `AND` and `OR` conditionals when filtering entries
- How nested `AND` / `OR` queries work
- How to combine both conditionals in a single query

</Alert>

## Overview

Both `AND` and `OR` conditionals are arrays of filter conditions. The available filter keys depend on the fields defined on the model you are querying. All user-defined field filters use the `values.` prefix (e.g. `values.title_contains`).

The examples below use the Webiny SDK. The `where` object is passed directly to `sdk.cms.listEntries()`.

## The `AND` Conditional

`AND` requires **all** conditions in the array to match. It behaves the same as placing filters at the root of `where`, with the added ability to repeat the same operator multiple times (which is not possible at the root level).

### Simple `AND` Examples

#### Search for entries where the title contains both "headless" and "cms"

At the root level you can only use `values.title_contains` once. Wrapping conditions in `AND` lets you apply the same filter key multiple times:

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
AND: [{ "values.title_contains": "headless" }, { "values.title_contains": "cms" }]
}
});
```

Equivalent condition: `(values.title contains "headless" AND values.title contains "cms")`

#### Combine a root-level filter with `AND`

Root-level filters and `AND` are applied together — all must match:

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
"values.category": "cat-id-1",
AND: [{ "values.title_contains": "headless" }, { "values.title_contains": "cms" }]
}
});
```

Equivalent condition: `(values.category = "cat-id-1" AND values.title contains "headless" AND values.title contains "cms")`

### Complex `AND` Example

Search for articles that:

- are in category `cat-id-1`
- have both "headless" and "cms" in the title
- are authored by one of three authors
- were created in 2022

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
"values.category": "cat-id-1",
AND: [
{ "values.title_contains": "headless" },
{ "values.title_contains": "cms" },
{
AND: [
{ "values.author_in": ["author-5", "author-6", "author-7"] },
{ createdOn_between: ["2022-01-01", "2022-12-31"] }
]
}
]
}
});
```

Equivalent condition: `(values.category = "cat-id-1" AND values.title contains "headless" AND values.title contains "cms" AND (values.author in [...] AND createdOn between 2022))`

<Alert type="info">

`createdOn` is a system meta field available on all entries. It does not use the `values.` prefix.

</Alert>

The same query can be flattened when there is no need for repeated keys:

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
"values.category": "cat-id-1",
AND: [{ "values.title_contains": "headless" }, { "values.title_contains": "cms" }],
"values.author_in": ["author-5", "author-6", "author-7"],
createdOn_between: ["2022-01-01", "2022-12-31"]
}
});
```

This produces the same result as the nested version above.

## The `OR` Conditional

`OR` requires **at least one** condition in the array to match.

### Simple `OR` Examples

#### Search for entries where the title contains "headless" or "cms"

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
OR: [{ "values.title_contains": "headless" }, { "values.title_contains": "cms" }]
}
});
```

Equivalent condition: `(values.title contains "headless" OR values.title contains "cms")`

#### Multiple filters inside one `OR` branch

When an `OR` branch contains more than one filter, all filters in that branch must match (implicit AND within the branch):

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
OR: [
{ "values.title_contains": "headless", "values.category": "cat-id-1" },
{ "values.title_contains": "cms" }
]
}
});
```

Equivalent condition: `((values.title contains "headless" AND values.category = "cat-id-1") OR values.title contains "cms")`

### Complex `OR` Example

Search for articles that match any of:

- title contains "headless"
- title contains "cms"
- category is `cat-id-1` or `cat-id-2`

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
OR: [
{ "values.title_contains": "headless" },
{ "values.title_contains": "cms" },
{
OR: [{ "values.category": "cat-id-1" }, { "values.category": "cat-id-2" }]
}
]
}
});
```

Equivalent condition: `(values.title contains "headless" OR values.title contains "cms" OR (values.category = "cat-id-1" OR values.category = "cat-id-2"))`

## Mixing `AND` and `OR`

### `OR` at the root with nested `AND` and `OR`

Search for articles that match any of:

- title contains "headless"
- title contains "cms"
- title contains both "webiny" and "serverless", and was created in January 2021 or January 2022

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
OR: [
{ "values.title_contains": "headless" },
{ "values.title_contains": "cms" },
{
AND: [
{ "values.title_contains": "webiny" },
{ "values.title_contains": "serverless" },
{
OR: [
{ createdOn_between: ["2021-01-01", "2021-01-31"] },
{ createdOn_between: ["2022-01-01", "2022-01-31"] }
]
}
]
}
]
}
});
```

### `AND` at the root with nested `OR` and `AND`

Search for articles that match all of:

- title contains "headless"
- title contains "cms"
- title contains "webiny" or "serverless", or was created in January 2021 or January 2022

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
AND: [
{ "values.title_contains": "headless" },
{ "values.title_contains": "cms" },
{
OR: [
{ "values.title_contains": "webiny" },
{ "values.title_contains": "serverless" },
{
AND: [
{ createdOn_between: ["2021-01-01", "2021-01-31"] },
{ createdOn_between: ["2022-01-01", "2022-01-31"] }
]
}
]
}
]
}
});
```

### `OR` and `AND` both at the root level

Search for articles that:

- are written by author `author-1` OR are in category `cat-id-2`
- AND have both "headless" and "cms" in the title

```typescript
const result = await sdk.cms.listEntries<Article>({
modelId: "article",
fields: ["id", "entryId", "values.title"],
where: {
OR: [{ "values.author": "author-1" }, { "values.category": "cat-id-2" }],
AND: [{ "values.title_contains": "headless" }, { "values.title_contains": "cms" }]
}
});
```

Equivalent condition: `((values.author = "author-1" OR values.category = "cat-id-2") AND (values.title contains "headless" AND values.title contains "cms"))`

<Alert type="warning">

`AND` and `OR` conditionals can be nested indefinitely, but deep nesting may result in performance issues — particularly on DynamoDB-only deployments. Keep nesting shallow where possible.

</Alert>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
AI Context: Date/Time and Identity Meta Fields (date-time-and-identity-meta-fields.mdx)

## Source of Information

1. `docs/developer-docs/5.x/headless-cms/references/date-time-and-identity-meta-fields.mdx` — original v5 article; ported for v6
2. `docs/developer-docs/6.x/headless-cms/using-webiny-sdk.mdx` — SDK query patterns (listEntries, fields, where)

## Key Changes from v5 Article

1. Removed "Can I use this? Available since v5.39.0" alert — no version references in v6 docs
2. Removed v5 GraphQL query example — replaced with SDK-based example using `sdk.cms.listEntries()`
3. Removed v5 lifecycle events code example (`ContextPlugin`) — v6 uses class-based event handlers; no replacement example added (would bloat this reference article)
4. "Introduction" heading → "Overview" to match v6 article conventions
5. Meta fields do NOT use the `values.` prefix — they are top-level system fields

## Understanding

### Two levels of meta fields

- **Revision-level**: prefixed with `revision` — describe a specific revision; change with each revision operation
- **Entry-level**: no prefix — describe the entry as a whole; `createdOn` is set once and never changes

### Nullability rules

- `createdOn`, `savedOn`, `revisionCreatedOn`, `revisionSavedOn`, `createdBy`, `savedBy`, `revisionCreatedBy`, `revisionSavedBy` — never null
- All `modified*`, `*PublishedOn`, `*PublishedBy`, `deleted*`, `restored*` — can be null

### modified vs saved

- `savedOn` is updated on every write (including create)
- `modifiedOn` is only set after the first update — null if entry was never updated
- After first update: `modifiedOn === savedOn` always

### SDK usage

Meta fields are top-level fields — no `values.` prefix:
```typescript
fields: ["id", "entryId", "createdOn", "lastPublishedOn", "createdBy", "values.title"]
where: { createdOn_between: ["2024-01-01", "2024-12-31"] }
sort: ["createdOn_DESC"]
```

## Related Documents

- `using-webiny-sdk.mdx` — full SDK query/mutation reference
- `migrating-to-webiny.mdx` — links here for meta field migration guidance
- `advanced-filtering.mdx` — filtering by meta fields (createdOn_between, etc.)

## Tone Guidelines

- Reference article — concise, table-driven, minimal prose
- FAQ section kept from v5 — it answers the most common confusion (modified vs saved)
Loading