Skip to main content

Milvus Filter Expression

PyPI version Python 3.8+ License: MIT

Type-safe, MongoDB-style filter builder for Milvus vector database. Build complex filter expressions with an intuitive, Pythonic API.

✨ Features

  • 🎯 Type-safe: Full type hints and IDE autocomplete support
  • 🚀 Flexible: Dictionary, builder, or shortcut syntax - use what fits your style
  • 📦 Zero dependencies: Pure Python, optional Milvus integration
  • 🧪 Well-tested: 95%+ test coverage
  • 📖 Well-documented: Comprehensive examples and API documentation
  • 🔧 Milvus-native: Generates expressions following Milvus official syntax

🚀 Quick Start

Installation

pip install milvus-filter-expression

Basic

from milvus_filter_expression import FilterBuilder, to_expression

# Dictionary style (most flexible)
filters = {"age": 25, "status": "active"}
expr = to_expression(filters)
# Result: (age == 25) AND (status == "active")

# Builder style (type-safe, chainable)
fb = FilterBuilder().eq("age", 25).eq("status", "active")
expr = to_expression(fb.build())

# Use with Milvus
from pymilvus import Collection

collection = Collection("users")
results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    expr=expr,  # ← Use your filter here
    limit=10
)

📚 Documentation

Comparison Operators

Operator Method Dictionary Example
Equal .eq(key, val) {"key": val} age == 25
Not Equal .ne(key, val) {"key": {"$ne": val}} age != 25
Greater Than .gt(key, val) {"key": {"$gt": val}} age > 25
Greater or Equal .gte(key, val) {"key": {"$gte": val}} age >= 25
Less Than .lt(key, val) {"key": {"$lt": val}} age < 25
Less or Equal .lte(key, val) {"key": {"$lte": val}} age <= 25
IN .in_(key, list) {"key": {"$in": list}} status IN ["a", "b"]
Range .between(key, min, max) {"key": {"$gte": min, "$lte": max}} (age >= 18) AND (age <= 65)

String Operators

# LIKE pattern matching
filters = {"email": {"$like": "%@gmail.com"}}
expr = to_expression(filters)
# Result: email LIKE "%@gmail.com"

# Using builder
fb = FilterBuilder().like("name", "John%")  # Starts with "John"

Patterns:

  • %: Zero or more characters
  • _: Exactly one character

NULL Operators

# IS NULL
filters = {"deleted_at": {"$is_null": True}}
# Result: deleted_at IS NULL

# IS NOT NULL
filters = {"created_at": {"$is_not_null": True}}
# Result: created_at IS NOT NULL

# Using None value
filters = {"field": None}
# Result: field IS NULL

Logical Operators

OR Condition

# Dictionary style
filters = {
    "$or": [
        {"status": "active"},
        {"status": "pending"}
    ]
}
# Result: (status == "active") OR (status == "pending")

# Builder style
fb = FilterBuilder().or_([
    {"status": "active"},
    {"status": "pending"}
])

AND Condition (Explicit)

filters = {
    "$and": [
        {"age": {"$gte": 18}},
        {"age": {"$lte": 65}}
    ]
}
# Result: (age >= 18) AND (age <= 65)

# Note: Multiple filters are automatically combined with AND
filters = {"age": 25, "status": "active"}
# Result: (age == 25) AND (status == "active")

NOT Condition

filters = {
    "$not": {"status": "deleted"}
}
# Result: NOT (status == "deleted")

Complex Nested Filters

# Combine OR, AND, and other operators
filters = {
    "$or": [
        {"category": "ml"},
        {"category": "ai"}
    ],
    "score": {"$gte": 80},
    "published_year": {"$gte": 2020},
    "is_verified": True
}

expr = to_expression(filters)
# Result: ((category == "ml") OR (category == "ai")) AND (score >= 80) 
#         AND (published_year >= 2020) AND (is_verified == true)

🎨 Three Ways to Build Filters

1. Dictionary Style (Most Flexible)

filters = {
    "age": {"$gte": 18, "$lte": 65},
    "status": {"$in": ["active", "pending"]},
    "$or": [
        {"role": "admin"},
        {"role": "moderator"}
    ]
}
expr = to_expression(filters)

Pros: Flexible, can be serialized to JSON, easy to generate dynamically
Cons: No type checking, easy to make typos

2. Builder Style (Type-safe)

fb = (FilterBuilder()
    .between("age", 18, 65)
    .in_("status", ["active", "pending"])
    .or_([
        {"role": "admin"},
        {"role": "moderator"}
    ])
)
expr = to_expression(fb.build())

Pros: Type-safe, IDE autocomplete, chainable, harder to make mistakes
Cons: More verbose

3. Shortcut Functions (Quick & Clean)

from milvus_filter_expression import eq, between, or_

filters = {
    **between("age", 18, 65),
    **or_(
        {"role": "admin"},
        {"role": "moderator"}
    )
}
expr = to_expression(filters)

Pros: Concise, functional style
Cons: Limited to simple cases

💡 Real-World Examples

fb = (FilterBuilder()
    .eq("category", "electronics")
    .between("price", 100, 1000)
    .gte("rating", 4.0)
    .eq("in_stock", True)
)

# Search products with vector similarity + filters
results = collection.search(
    data=[product_embedding],
    anns_field="embedding",
    expr=to_expression(fb.build()),
    limit=20
)

Document Retrieval with RAG

# Filter for recent, relevant documents
filters = {
    "document_type": {"$in": ["article", "paper"]},
    "published_year": {"$gte": 2020},
    "language": "en",
    "$or": [
        {"category": "machine_learning"},
        {"category": "artificial_intelligence"}
    ]
}

# Use with LangChain
from langchain.vectorstores import Milvus

vectorstore = Milvus(collection_name="documents")
docs = vectorstore.similarity_search(
    "explain transformers",
    k=5,
    expr=to_expression(filters)
)

Multi-tenant Application

def get_tenant_filter(tenant_id: str, user_id: str) -> str:
    """Ensure data isolation"""
    fb = FilterBuilder()
    
    # Tenant isolation
    fb.eq("tenant_id", tenant_id)
    
    # User permissions
    fb.or_([
        {"visibility": "public"},
        {"$and": [
            {"visibility": "private"},
            {"owner_id": user_id}
        ]}
    ])
    
    # Only active records
    fb.is_null("deleted_at")
    
    return to_expression(fb.build())

Content Moderation

# Find content that needs review
filters = {
    "$or": [
        {"status": "flagged"},
        {"report_count": {"$gte": 3}}
    ],
    "reviewed_at": {"$is_null": True},
    "auto_approved": {"$ne": True}
}

flagged_content = collection.search(
    data=[query_vector],
    expr=to_expression(filters),
    limit=100
)

🔧 Field Prefix Support

Useful when your metadata is nested:

filters = {"age": 25, "city": "NYC"}

# Add prefix for nested fields
expr = to_expression(filters, field_prefix="metadata.")
# Result: (metadata.age == 25) AND (metadata.city == "NYC")

🧪 Testing

# Run tests
pytest

# With coverage
pytest --cov=src/milvus_filter_expression --cov-report=html

# Run examples
python examples/basic_usage.py
python examples/advanced_usage.py

📖 More Examples

Check out the examples/ directory:

🤝 Integration with Frameworks

LangChain

from langchain.vectorstores import Milvus
from milvus_filter_expression import to_expression

filters = {"category": "tech", "year": {"$gte": 2020}}
vectorstore = Milvus(collection_name="docs")

results = vectorstore.similarity_search(
    "machine learning",
    k=5,
    expr=to_expression(filters)
)

Direct Milvus

from pymilvus import Collection
from milvus_filter_expression import FilterBuilder

collection = Collection("my_collection")

fb = FilterBuilder().eq("status", "active").gte("score", 0.8)

results = collection.search(
    data=[[0.1, 0.2, ...]],
    anns_field="embedding",
    param={"metric_type": "IP", "params": {"nprobe": 10}},
    expr=to_expression(fb.build()),
    limit=10,
    output_fields=["id", "text", "metadata"]
)

Metadata

Release files for milvus-filter-expression 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for milvus-filter-expression 0.1.0
File Size Uploaded
milvus_filter_expression-0.1.0.tar.gz 12.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for milvus-filter-expression 0.1.0
File Interpreter ABI Platform
milvus_filter_expression-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.5 kB

Release files / milvus_filter_expression-0.1.0.tar.gz

Download URL milvus_filter_expression-0.1.0.tar.gz
Size 12.1 kB
Tags Source
SHA-256 checksum
How to use checksums
904eb29f3c7a939a863811c71fa36a0fff6da7dfb4ee8b69cf44168cb73ddabe
BLAKE2b-256 checksum
How to use checksums
46ea6c1e131e0b3068d295f8b4884787a3b92392f10568ae1fa40a092f3f6ede
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.11

Release files / milvus_filter_expression-0.1.0-py3-none-any.whl

Download URL milvus_filter_expression-0.1.0-py3-none-any.whl
Size 9.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
377340be8df62bf112b4bf1d6a159adf1d7937d6406b37081dff59176586e6ea
BLAKE2b-256 checksum
How to use checksums
82fe9a266c9e283f2fac773c38970b0ff019fd10e9f1d2997c60eed9eca6c090
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.11

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page