diff --git a/content/docs/protocol/kernel/http-protocol.mdx b/content/docs/protocol/kernel/http-protocol.mdx index b4880e72c2..925c1fbf48 100644 --- a/content/docs/protocol/kernel/http-protocol.mdx +++ b/content/docs/protocol/kernel/http-protocol.mdx @@ -394,7 +394,7 @@ GET /api/data/account?filter={"status":"active"} - `$endsWith` - String ends with - `$between` - Between two values (tuple) - `$null` - Null check (`{ "$null": true }` for IS NULL, `{ "$null": false }` for IS NOT NULL) -- `$exists` - Field existence check +- `$exists` - Field has a value — the inverse of `$null` (`{ "$exists": true }` is `IS NOT NULL`, `{ "$exists": false }` is `IS NULL`). Not a key-presence test: a stored `null` counts as no value on every backend, MongoDB included **OR conditions:** ```json diff --git a/content/docs/protocol/objectql/query-syntax.mdx b/content/docs/protocol/objectql/query-syntax.mdx index fb586b1d77..d627e567f7 100644 --- a/content/docs/protocol/objectql/query-syntax.mdx +++ b/content/docs/protocol/objectql/query-syntax.mdx @@ -261,7 +261,7 @@ const query: QueryAST = { | `$ilike` | Same pattern language, **ignoring ASCII case** | `{ name: { $ilike: '%industries' } }` | | `$between` | Range (inclusive) | `{ close_date: { $between: ['2024-01-01', '2024-12-31'] } }` | | `$null` | Null check | `{ manager_id: { $null: true } }` / `{ phone: { $null: false } }` | -| `$exists` | Field exists (NoSQL) | `{ metadata: { $exists: true } }` | +| `$exists` | Field has a value — the inverse of `$null` | `{ metadata: { $exists: true } }` | ### `$like` is not a spelling of `$contains` @@ -514,10 +514,21 @@ where: { manager_id: { $null: true } } // Field IS NOT NULL where: { phone: { $null: false } } -// Field exists (NoSQL) +// Field has a value — the same rows as { $null: false } where: { metadata: { $exists: true } } ``` + +**`$exists` is not a key-presence test.** It asks whether the field **has a +value**, so a stored `null` counts as *no value* on every backend: +`{ $exists: true }` compiles to `IS NOT NULL` and `{ $exists: false }` to +`IS NULL`. It is the exact inverse of `$null` — `{ $exists: b }` selects the +same rows as `{ $null: !b }` — and it is portable, not a NoSQL-only operator. +On MongoDB the driver lowers it to `{ $ne: null }` / `{ $eq: null }` and never +emits MongoDB's own `$exists`, which *would* have been key presence and would +have kept a `null`-valued field. + + ### Filtering Across Relationships