Skip to main content
Enhanced metadata filtering in Mem0 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.
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.
Enhanced filtering requires Mem0 1.0.0 or later and a vector store that supports the operators you enable. Unsupported operators fall back to simple equality filters.
The TypeScript SDK accepts the same filter shape shown here—transpose the dictionaries to objects and reuse the keys unchanged.

Feature anatomy

OperatorMeaningWhen to use it
eq / neEquals / not equalsExact matches on strings, numbers, or booleans.
gt / gteGreater than / greater than or equalRank results by score, confidence, or any numeric field.
lt / lteLess than / less than or equalCap numeric values (e.g., ratings, timestamps).
in / ninIn list / not in listPre-approve or block sets of values without chaining multiple filters.
contains / icontainsCase-sensitive / case-insensitive substring matchScan text fields for keywords.
*WildcardRequire that a field exists, regardless of value.
AND / OR / NOTCombine filtersBuild logic trees so multiple conditions work together.

Metadata selectors

Start with key-value filters when you need direct matches on metadata fields.
from mem0 import Memory

m = Memory()

# Search with simple metadata filters
results = m.search(
    "What are my preferences?",
    user_id="alice",
    filters={"category": "preferences"}
)
Expect only memories tagged with category="preferences" to return for the given user_id.

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.
# Greater than / Less than
results = m.search(
    "recent activities",
    user_id="alice",
    filters={
        "score": {"gt": 0.8},
        "priority": {"gte": 5},
        "confidence": {"lt": 0.9},
        "rating": {"lte": 3}
    }
)

# Equality operators
results = m.search(
    "specific content",
    user_id="alice",
    filters={
        "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.
# In / Not in operators
results = m.search(
    "multi-category search",
    user_id="alice",
    filters={
        "category": {"in": ["food", "travel", "entertainment"]},
        "status": {"nin": ["deleted", "archived"]}
    }
)
Verify the response includes only memories in the whitelisted categories and omits any with archived or deleted status.

String operators

contains and icontains capture substring matches, making it easy to scan descriptions or tags for keywords without retrieving irrelevant memories.
# Text matching operators
results = m.search(
    "content search",
    user_id="alice",
    filters={
        "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.
# Match any value for a field
results = m.search(
    "all with category",
    user_id="alice",
    filters={
        "category": "*"
    }
)

Logical combinations

Combine filters with AND, OR, and NOT to express complex decision trees. Nest logical operators to encode multi-branch workflows.
# Logical AND
results = m.search(
    "complex query",
    user_id="alice",
    filters={
        "AND": [
            {"category": "work"},
            {"priority": {"gte": 7}},
            {"status": {"ne": "completed"}}
        ]
    }
)

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

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

# Complex nested logic
results = m.search(
    "advanced query",
    user_id="alice",
    filters={
        "AND": [
            {
                "OR": [
                    {"category": "work"},
                    {"category": "personal"}
                ]
            },
            {"priority": {"gte": 5}},
            {
                "NOT": [
                    {"status": "archived"}
                ]
            }
        ]
    }
)
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.

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.
# 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"]
        }
    }
}
After enabling indexing, benchmark the same query—latency should drop once the store can prune documents on indexed fields before vector scoring.
Put simple key=value filters on indexed fields before your range or text conditions so the store trims results early.
# 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"}
    ]
}
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.
Vector store support varies. Confirm operator coverage before shipping:
Full comparison, list, and logical support. Handles deeply nested boolean logic efficiently.
Equality and basic comparisons only. Limited nesting—break large trees into smaller calls.
Comparisons plus in/nin. Text operators are constrained; rely on tags where possible.
Full operator coverage with advanced text filters. Best option when you need hybrid text + metadata queries.
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.

Migrate from earlier filters

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

# After (v1.0.0) - enhanced filtering with operators
results = m.search(
    "query",
    user_id="alice",
    filters={
        "AND": [
            {"category": "work"},
            {"status": {"ne": "archived"}},
            {"priority": {"gte": 5}}
        ]
    }
)
Existing equality filters continue to work; add new operator branches gradually so agents can adopt richer queries without downtime.

See it in action

Project management filtering

# Find high-priority active tasks
results = m.search(
    "What tasks need attention?",
    user_id="project_manager",
    filters={
        "AND": [
            {"project": {"in": ["alpha", ""]}},
            {"priority": {"gte": 8}},
            {"status": {"ne": "completed"}},
            {
                "OR": [
                    {"assignee": "alice"},
                    {"assignee": "bob"}
                ]
            }
        ]
    }
)
Tasks returned should belong to the targeted projects, remain incomplete, and be assigned to one of the listed teammates.

Customer support filtering

# Find recent unresolved tickets
results = m.search(
    "pending support issues",
    agent_id="support_bot",
    filters={
        "AND": [
            {"ticket_status": {"ne": "resolved"}},
            {"priority": {"in": ["high", "critical"]}},
            {"created_date": {"gte": "2024-01-01"}},
            {
                "NOT": [
                    {"category": "spam"}
                ]
            }
        ]
    }
)
Pair agent_id filters with ticket-specific metadata so shared support bots return only the tickets they can act on in the current session.

Content recommendation filtering

# Personalized content filtering
results = m.search(
    "recommend content",
    user_id="reader123",
    filters={
        "AND": [
            {
                "OR": [
                    {"genre": {"in": ["sci-fi", "fantasy"]}},
                    {"author": {"contains": "favorite"}}
                ]
            },
            {"rating": {"gte": 4.0}},
            {"read_status": {"ne": "completed"}},
            {"language": "english"}
        ]
    }
)
Confirm personalized feeds show only unread titles that meet the rating and language criteria.

Handle invalid operators

try:
    results = m.search(
        "test query",
        user_id="alice",
        filters={
            "invalid_operator": {"unknown": "value"}
        }
    )
except ValueError as e:
    print(f"Filter error: {e}")
    results = m.search(
        "test query",
        user_id="alice",
        filters={"category": "general"}
    )
Validate filters before executing searches so you can catch typos or unsupported operators during development instead of at runtime.

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.