---
name: mem0-filter-syntax
source: https://app.decimal.ai/s/mem0-filter-syntax@1/SKILL.md
source_sha256: 8645998f2f86
---

# mem0 Filter Syntax

## Contract
When you call the mem0 Python SDK (`MemoryClient` / `Memory`) to `search` or `get_all` memories,
emit mem0's OWN filter grammar — never MongoDB / Mongoose syntax. Apply whenever you build a
`filters=` object or pick the search arguments.

## Rules

1. **Search arguments.** `client.search(query, filters=..., top_k=..., threshold=..., rerank=...)`.
   - `query` — the natural-language semantic query string.
   - `filters` — the filter object (see rule 3+).
   - `top_k` — max results (NOT `limit`, `k`, `n`, `max_results`, `count`, or `size`).
   - `threshold` — minimum semantic score, a float (default 0.1); pass `0.0` to disable score
     filtering. NOT `min_score`, `min_relevance`, `score`, or `cutoff`.
   - `rerank` — a boolean; turns on the managed reranker. NOT `use_rerank`, `reorder`, `rerankers`.
   - Listing without a semantic query uses `client.get_all(filters=...)` (NOT `list`, `all`,
     `get_memories`).

2. **Entity scopes are snake_case and at least one is required.** The scoping keys are exactly
   `user_id`, `agent_id`, `app_id`, `run_id` — snake_case, never camelCase (`userId`, `agentId`)
   and never `id` / `application_id` / `session_id`. Every filter must carry at least one entity
   id. A bare `{"user_id": "alice"}` is equality shorthand.

3. **Logical operators are BARE UPPERCASE keys — no `$`.** Combine conditions with `AND`, `OR`,
   `NOT` (never `$and` / `$or` / `$not`, never lowercase `and` / `or`).
   - `AND` — value is a JSON **array** of condition objects (all must match).
   - `OR` — value is a JSON **array** of condition objects (any match).
   - `NOT` — value is a **single** condition object (must not match).
   - Nest freely: an `AND` array may contain an `OR` object and vice-versa.

4. **Comparison operators are BARE keys — no `$`.** Nest an operator object on a field:
   - `in` — membership in a list: `{"categories": {"in": ["finance", "health"]}}` (NOT `$in`).
   - `gte` / `lte` / `gt` / `lt` — numeric/date range (NOT `$gte` etc.).
   - `contains` — case-SENSITIVE substring.
   - `icontains` — case-INSENSITIVE substring (this is the ONLY case-insensitive form — NOT
     `$regex`, `regex`, `like`, `ilike`).
   - `ne` — not-equal (NOT `$ne`, `neq`, `!=`).

5. **Wildcard "any non-null value" is the literal string `"*"`.** e.g. `{"run_id": "*"}` matches
   records where `run_id` is present. NEVER `$exists`, `{"ne": null}`, or a boolean-exists form.

6. **Metadata is a sub-object supporting only eq / contains / ne.** Write
   `{"metadata": {"type": "decision"}}` — never a dotted `"metadata.type"` key. Metadata does NOT
   support range operators (`gte`/`lte`) — only bare equality, `contains`, and `ne`.

7. **Dates are ISO-8601 strings** on `created_at` / `updated_at` with `gte`/`lte`/`gt`/`lt`, e.g.
   `{"created_at": {"gte": "2024-01-01T00:00:00Z"}}`.

## Worked examples

Simple scoped search — base reaches for a flat call + `limit`:
```
# BEFORE (base default)                    # AFTER (mem0)
m.search("payment retries",                m.search("payment retries",
         user_id="u_42", limit=5)                   filters={"user_id": "u_42"}, top_k=5)
```

Compound AND with metadata — base reaches for MongoDB `$and` + dotted key:
```
# BEFORE  filters={"$and": [{"user_id": "u_42"}, {"metadata.kind": "note"}]}
# AFTER   filters={"AND": [{"user_id": "u_42"}, {"metadata": {"kind": "note"}}]}
```

Case-insensitive substring — base reaches for `$regex`:
```
# BEFORE  {"summary": {"$regex": "timeout", "$options": "i"}}
# AFTER   {"summary": {"icontains": "timeout"}}
```

Membership + wildcard + date — base reaches for `$in` / `$exists` / `$gte`:
```
# BEFORE  {"$and": [{"labels": {"$in": ["a","b"]}}, {"run_id": {"$exists": true}},
#                   {"created_at": {"$gte": "2025-01-01T00:00:00Z"}}]}
# AFTER   {"AND": [{"labels": {"in": ["a","b"]}}, {"run_id": "*"},
#                  {"created_at": {"gte": "2025-01-01T00:00:00Z"}}]}
```

Negation — base reaches for `$not` / `$ne`:
```
# BEFORE  {"$and": [{"user_id": "u_42"}, {"tier": {"$ne": "trial"}}]}
# AFTER   {"AND": [{"user_id": "u_42"}, {"NOT": {"tier": "trial"}}]}
```

## Edge cases & exceptions
- **No entity id → invalid.** A metadata-only or category-only filter is rejected; add a
  `user_id` / `agent_id` / `app_id` / `run_id` scope.
- **Metadata numbers need range → not supported.** Metadata only does eq/contains/ne; promote the
  value to a top-level field or filter it after retrieval.
- **`get_all` paginates with `page` / `page_size`; `search` caps with `top_k`.** Don't put `top_k`
  on `get_all` or `page` on `search`.
- **Python is snake_case; the TypeScript SDK is camelCase.** This convention is the Python form.
- **`threshold=0.0`** returns everything above score 0 (filtering effectively off), not "top 0".

## Do / Don't
- DO use bare `AND` / `OR` / `NOT`. DON'T use `$and` / `$or` / `$not`.
- DO use bare `in` / `gte` / `icontains` / `ne`. DON'T prefix operators with `$`.
- DO use `icontains` for case-insensitive match. DON'T use `$regex` / `like`.
- DO use `"*"` for any-non-null. DON'T use `$exists` / `{"ne": null}`.
- DO use `top_k` and `threshold`. DON'T use `limit` / `k` / `min_score`.
- DO nest metadata as `{"metadata": {...}}`. DON'T use dotted `"metadata.key"`.
- DO keep entity ids snake_case. DON'T camelCase them in Python.

## Common mistakes
- MongoDB carryover: `$and`, `$or`, `$gte`, `$lte`, `$in`, `$ne`, `$regex`, `$exists`, `$options`.
- `limit=` / `k=` / `n=` instead of `top_k=`; `min_score=` instead of `threshold=`.
- `userId` / `agentId` / `appId` (camelCase) in the Python SDK.
- Dotted metadata paths (`"metadata.type"`) instead of the `{"metadata": {...}}` sub-object.
- Using `contains` when case-insensitivity is required (that needs `icontains`).
- `{"run_id": {"$exists": true}}` instead of `{"run_id": "*"}`.

## Quick checklist
- [ ] Logical keys are bare `AND` / `OR` / `NOT` (arrays for AND/OR, object for NOT).
- [ ] No `$`-prefixed operator anywhere.
- [ ] Operators: `in` / `gte` / `lte` / `gt` / `lt` / `contains` / `icontains` / `ne`.
- [ ] At least one snake_case entity id (`user_id` / `agent_id` / `app_id` / `run_id`).
- [ ] Any-non-null is `"*"`; metadata is a sub-object (eq/contains/ne only); dates are ISO-8601.
- [ ] Result cap is `top_k`; min score is `threshold`; reranker is `rerank`.
