> ## Documentation Index
> Fetch the complete documentation index at: https://mem0-feature-memo-claude-plugin-v1.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Enhanced Metadata Filtering

> Fine-grained metadata queries for precise OSS memory retrieval.

Enhanced metadata filtering in Mem0 lets you run complex queries across memory metadata. Combine comparisons, logical operators, and wildcard matches to zero in on the exact memories your agent needs.

<Info>
  This page covers the self-hosted `Memory` / `AsyncMemory` grammar. Sibling top-level keys are implicitly ANDed on both self-hosted and the hosted Platform API, so a flat filter like `{"user_id": "alice", "category": "work"}` works without wrapping it in `AND` on either. Two real differences remain: the `nin` operator documented below is not part of the Platform contract, and Platform validates every top-level key against a fixed allow-list, rejecting anything else with a 400. See [Memory Filters (Platform)](/platform/features/v2-memory-filters) for the hosted grammar.
</Info>

***

## Feature anatomy

<AccordionGroup>
  <Accordion title="Operator quick reference">
    | Operator                 | Meaning                                           | When to use it                                                                                                                             |
    | ------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
    | `eq` / `ne`              | Equals / not equals                               | Exact matches on strings, numbers, or booleans.                                                                                            |
    | `gt` / `gte`             | Greater than / greater than or equal              | Rank results by score, confidence, or any numeric field.                                                                                   |
    | `lt` / `lte`             | Less than / less than or equal                    | Cap numeric values (e.g., ratings, timestamps).                                                                                            |
    | `in` / `nin`             | In list / not in list                             | Pre-approve or block sets of values without chaining multiple filters.                                                                     |
    | `contains` / `icontains` | Case-sensitive / case-insensitive substring match | Scan text fields for keywords.                                                                                                             |
    | `*`                      | Wildcard                                          | Match regardless of value. Exact semantics (field-must-exist vs. no-op) vary by vector store, see [Wildcard matching](#wildcard-matching). |
    | `AND` / `OR` / `NOT`     | Combine filters                                   | Build logic trees so multiple conditions work together.                                                                                    |
  </Accordion>
</AccordionGroup>

### Metadata selectors

Start with key-value filters when you need direct matches on metadata fields.

```python theme={null}
from mem0 import Memory

m = Memory()

# Search with simple metadata filters
results = m.search(
    "What are my preferences?",
    filters={"user_id": "alice", "category": "preferences"}
)
```

<Info icon="check">
  Expect only memories tagged with `category="preferences"` to return for the given `user_id`.
</Info>

### Comparison operators

Layer greater-than/less-than comparisons to rank results by score, confidence, or any numeric field. Equality helpers (`eq`, `ne`) keep string and boolean checks explicit.

```python theme={null}
# Greater than / Less than
results = m.search(
    "recent activities",
    filters={
        "user_id": "alice",
        "score": {"gt": 0.8},
        "priority": {"gte": 5},
        "confidence": {"lt": 0.9},
        "rating": {"lte": 3}
    }
)

# Equality operators
results = m.search(
    "specific content",
    filters={
        "user_id": "alice",
        "status": {"eq": "active"},
        "archived": {"ne": True}
    }
)
```

### List-based operators

Use `in` and `nin` when you want to pre-approve or exclude specific values without writing multiple equality checks.

```python theme={null}
# In / Not in operators
results = m.search(
    "multi-category search",
    filters={
        "user_id": "alice",
        "category": {"in": ["food", "travel", "entertainment"]},
        "status": {"nin": ["deleted", "archived"]}
    }
)
```

<Info icon="check">
  Verify the response includes only memories in the whitelisted categories and omits any with archived or deleted status.
</Info>

### String operators

`contains` and `icontains` capture substring matches, making it easy to scan descriptions or tags for keywords without retrieving irrelevant memories.

```python theme={null}
# Text matching operators
results = m.search(
    "content search",
    filters={
        "user_id": "alice",
        "title": {"contains": "meeting"},
        "description": {"icontains": "important"},
        "tags": {"contains": "urgent"}
    }
)
```

### Wildcard matching

Match any value for a field, handy when the mere presence of a field matters.

```python theme={null}
# Match any value for a field
results = m.search(
    "all with category",
    filters={
        "user_id": "alice",
        "category": "*"
    }
)
```

<Warning>
  Wildcard semantics differ by vector store. pgvector requires the key to exist in the payload (a real "field exists" check). Qdrant and Chroma have no native "field exists" filter, so `*` is a no-op there: the field condition is dropped and every record passes, including ones where the field is missing entirely. Do not rely on `*` to exclude records with a missing field unless you have confirmed your store's behavior in `mem0/vector_stores/<provider>.py`.
</Warning>

### Logical combinations

Combine filters with `AND`, `OR`, and `NOT` to express complex decision trees. Nest logical operators to encode multi-branch workflows.

<Warning>
  The examples on this page use `search()`. `get_all()` accepts the same entity and comparison-operator filters, but the `AND` / `OR` / `NOT` wrapper is only translated at the `search()` layer before it reaches the vector store. Whether it also works on `get_all()` depends on your vector store: Qdrant recognizes raw `AND` / `OR` / `NOT` keys natively, but pgvector does not, so a logical wrapper passed to `get_all()` on pgvector silently matches nothing. Stick to `search()` for logical trees, or use plain sibling keys (implicitly ANDed) with `get_all()`.
</Warning>

```python theme={null}
# Logical AND
results = m.search(
    "complex query",
    filters={
        "user_id": "alice",
        "AND": [
            {"category": "work"},
            {"priority": {"gte": 7}},
            {"status": {"ne": "completed"}}
        ]
    }
)

# Logical OR
results = m.search(
    "flexible query",
    filters={
        "user_id": "alice",
        "OR": [
            {"category": "urgent"},
            {"priority": {"gte": 9}},
            {"deadline": {"contains": "today"}}
        ]
    }
)

# Logical NOT
results = m.search(
    "exclusion query",
    filters={
        "user_id": "alice",
        "NOT": [
            {"category": "archived"},
            {"status": "deleted"}
        ]
    }
)

# Complex nested logic
results = m.search(
    "advanced query",
    filters={
        "user_id": "alice",
        "AND": [
            {
                "OR": [
                    {"category": "work"},
                    {"category": "personal"}
                ]
            },
            {"priority": {"gte": 5}},
            {
                "NOT": [
                    {"status": "archived"}
                ]
            }
        ]
    }
)
```

<Info icon="check">
  Inspect the response metadata: each returned memory should satisfy the combined logic tree exactly. If results look too broad, log the raw filters sent to your vector store.
</Info>

***

## Configure it

Tune your vector store so filter-heavy queries stay fast. Index fields you frequently filter on and keep complex checks for later in the evaluation order.

```python theme={null}
# Ensure your vector store supports indexing on filtered fields
config = {
    "vector_store": {
        "provider": "qdrant",
        "config": {
            "host": "localhost",
            "port": 6333,
            "indexed_fields": ["category", "priority", "status", "user_id"]
        }
    }
}
```

<Info icon="check">
  After enabling indexing, benchmark the same query: latency should drop once the store can prune documents on indexed fields before vector scoring.
</Info>

<Tip>
  Put simple key=value filters on indexed fields before your range or text conditions so the store trims results early.
</Tip>

```python theme={null}
# More efficient: Filter on indexed fields first
good_filters = {
    "user_id": "alice",
    "AND": [
        {"category": "work"},
        {"content": {"contains": "meeting"}}
    ]
}

# Less efficient: Complex operations first
avoid_filters = {
    "user_id": "alice",
    "AND": [
        {"description": {"icontains": "complex text search"}}
    ]
}
```

<Info icon="check">
  When you reorder filters so indexed fields come first (`good_filters` example), queries typically return faster than the `avoid_filters` pattern where expensive text searches run before simple checks.
</Info>

Vector store support varies widely. This table reflects what each provider's filter-translation code (`mem0/vector_stores/<provider>.py`) actually implements, confirm before shipping if you use a store not listed:

| Store    | `eq`/`ne`/`gt`/`gte`/`lt`/`lte`/`in`/`nin`                                                                                                                | `contains`/`icontains`              | `AND`/`OR`/`NOT`                                                      | `*` wildcard                                                                  |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Qdrant   | Full support (`in`/`nin` must be a list, or the Qdrant client raises a validation error)                                                                  | Yes                                 | Yes, nested                                                           | No-op: matches every record regardless of whether the field is present        |
| pgvector | Full support (`in`/`nin` must be a list, or the call raises `ValueError`)                                                                                 | Yes, via SQL `LIKE`/`ILIKE`         | Yes, nested                                                           | Requires the key to exist in the payload                                      |
| Chroma   | Full support                                                                                                                                              | No: silently falls back to equality | Yes, one level of nesting                                             | No-op: the filter is dropped                                                  |
| Pinecone | Full support                                                                                                                                              | Not implemented                     | Not implemented: filters are only ANDed field-by-field                | Not implemented: `"*"` is matched as the literal string `"*"`, not a wildcard |
| Weaviate | Not implemented: only `user_id`, `agent_id` and `run_id` are filtered on, as exact equality. Every other key, including all metadata, is silently dropped | Not implemented                     | Not implemented: only an implicit AND across the three supported keys | Not implemented                                                               |

<Warning>
  If an operator is unsupported, most stores silently ignore it or fall back to equality rather than raising an error. Test filters against your actual store instead of assuming operator parity with Qdrant.
</Warning>

### Migrate from earlier filters

```python theme={null}
# Before (v0.x) - simple key-value filtering only
results = m.search(
    "query",
    filters={"user_id": "alice", "category": "work", "status": "active"}
)

# After (v1.0.0) - enhanced filtering with operators
results = m.search(
    "query",
    filters={
        "user_id": "alice",
        "AND": [
            {"category": "work"},
            {"status": {"ne": "archived"}},
            {"priority": {"gte": 5}}
        ]
    }
)
```

<Note>
  Existing equality filters continue to work; add new operator branches gradually so agents can adopt richer queries without downtime.
</Note>

***

## See it in action

### Project management filtering

```python theme={null}
# Find high-priority active tasks
results = m.search(
    "What tasks need attention?",
    filters={
        "user_id": "project_manager",
        "AND": [
            {"project": {"in": ["alpha", ""]}},
            {"priority": {"gte": 8}},
            {"status": {"ne": "completed"}},
            {
                "OR": [
                    {"assignee": "alice"},
                    {"assignee": "bob"}
                ]
            }
        ]
    }
)
```

<Info icon="check">
  Tasks returned should belong to the targeted projects, remain incomplete, and be assigned to one of the listed teammates.
</Info>

### Customer support filtering

```python theme={null}
# Find recent unresolved tickets
results = m.search(
    "pending support issues",
    filters={
        "agent_id": "support_bot",
        "AND": [
            {"ticket_status": {"ne": "resolved"}},
            {"priority": {"in": ["high", "critical"]}},
            {"created_date": {"gte": "2024-01-01"}},
            {
                "NOT": [
                    {"category": "spam"}
                ]
            }
        ]
    }
)
```

<Tip>
  Pair agent ID filters with ticket-specific metadata so shared support bots return only the tickets they can act on in the current session.
</Tip>

### Content recommendation filtering

```python theme={null}
# Personalized content filtering
results = m.search(
    "recommend content",
    filters={
        "user_id": "reader123",
        "AND": [
            {
                "OR": [
                    {"genre": {"in": ["sci-fi", "fantasy"]}},
                    {"author": {"contains": "favorite"}}
                ]
            },
            {"rating": {"gte": 4.0}},
            {"read_status": {"ne": "completed"}},
            {"language": "english"}
        ]
    }
)
```

<Info icon="check">
  Confirm personalized feeds show only unread titles that meet the rating and language criteria.
</Info>

### Handle invalid operators

```python theme={null}
try:
    results = m.search(
        "test query",
        filters={
            "user_id": "alice",
            "invalid_operator": {"unknown": "value"}
        }
    )
except ValueError as e:
    print(f"Filter error: {e}")
    results = m.search(
        "test query",
        filters={"user_id": "alice", "category": "general"}
    )
```

<Warning>
  Validate filters before executing searches so you can catch typos or unsupported operators during development instead of at runtime.
</Warning>

***

## Verify the feature is working

* Log the filters sent to your vector store and confirm the response metadata matches every clause.
* Benchmark queries before and after indexing to ensure latency improvements materialize.
* Add analytics or debug logging to track how often fallbacks execute when operators fail validation.

***

## Best practices

1. **Use indexed fields first:** Order filters so equality checks run before complex string operations.
2. **Combine operators intentionally:** Keep logical trees readable: large nests are harder to debug.
3. **Test performance regularly:** Benchmark critical queries with production-like payloads.
4. **Plan graceful degradation:** Provide fallback filters when an operator isn’t available.
5. **Validate syntax early:** Catch malformed filters during development to protect agents at runtime.

***

<CardGroup cols={2}>
  <Card title="Explore Vector Store Options" icon="database" href="/components/vectordbs/overview">
    Compare operator coverage and indexing strategies across supported stores.
  </Card>

  <Card title="Tag and Organize Memories" icon="tag" href="/cookbooks/essentials/tagging-and-organizing-memories">
    Practice building workflows that label and retrieve memories with clear metadata filters.
  </Card>

  <Card title="Memory Filters (Platform)" icon="cloud" href="/platform/features/v2-memory-filters">
    Using the hosted API instead? See the allow-listed top-level fields, the missing `nin` operator, and Platform-only fields like `created_at` and `memory_ids`.
  </Card>
</CardGroup>
