---
title: Enhanced Metadata Filtering
description: Fine-grained metadata queries for precise OSS memory retrieval.
icon: "filter"
---

Enhanced metadata filtering in Deja Vu 1.0.0 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>
  **You’ll use this when…**
  - Retrieval must respect multiple metadata conditions before returning context.
  - You need to mix numeric, boolean, and string filters in a single query.
  - Agents rely on deterministic filtering instead of broad semantic search alone.
</Info>

<Warning>
  Enhanced filtering requires Deja Vu 1.0.0 or later and a vector store that supports the operators you enable. Unsupported operators fall back to simple equality filters.
</Warning>

<Note>
  The TypeScript SDK accepts the same filter shape shown here—transpose the dictionaries to objects and reuse the keys unchanged.
</Note>

---

## 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 | Require that a field exists, regardless of value. |
    | `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
from dejavu 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
# 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
# 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
# Text matching operators
results = m.search(
    "content search",
    filters={
        "user_id": "alice",
        "title": {"contains": "meeting"},
        "description": {"icontains": "important"},
        "tags": {"contains": "urgent"}
    }
)
```

### Wildcard matching

Allow any value for a field while still requiring the field to exist—handy when the mere presence of a field matters.

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

### Logical combinations

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

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

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

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

# Complex nested logic
results = m.search(
    "advanced query",
    filters={
        "AND": [
            {"user_id": "alice"},
            {
                "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
# 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
# More efficient: Filter on indexed fields first
good_filters = {
    "AND": [
        {"user_id": "alice"},
        {"category": "work"},
        {"content": {"contains": "meeting"}}
    ]
}

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

<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. Confirm operator coverage before shipping:

<AccordionGroup>
  <Accordion title="Qdrant">
    Full comparison, list, and logical support. Handles deeply nested boolean logic efficiently.
  </Accordion>
  <Accordion title="Chroma">
    Equality and basic comparisons only. Limited nesting—break large trees into smaller calls.
  </Accordion>
  <Accordion title="Pinecone">
    Comparisons plus `in`/`nin`. Text operators are constrained; rely on tags where possible.
  </Accordion>
  <Accordion title="Weaviate">
    Full operator coverage with advanced text filters. Best option when you need hybrid text + metadata queries.
  </Accordion>
</AccordionGroup>

<Warning>
  If an operator is unsupported, most stores silently ignore that branch. Add validation before execution so you can fall back to simpler queries instead of returning empty results.
</Warning>

### Migrate from earlier filters

```python
# 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={
        "AND": [
            {"user_id": "alice"},
            {"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
# Find high-priority active tasks
results = m.search(
    "What tasks need attention?",
    filters={
        "AND": [
            {"user_id": "project_manager"},
            {"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
# Find recent unresolved tickets
results = m.search(
    "pending support issues",
    filters={
        "AND": [
            {"agent_id": "support_bot"},
            {"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
# Personalized content filtering
results = m.search(
    "recommend content",
    filters={
        "AND": [
            {"user_id": "reader123"},
            {
                "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
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>
</CardGroup>
