How Weaviate makes structured constraints a tightly integrated part of vector, keyword, and hybrid retrieval for fast and accurate results.

Metadata filtering is easy to describe and surprisingly difficult to execute well. A production query may need to find semantically relevant products while enforcing a brand, an in-stock flag, a price interval, a market, and a publication date. In enterprise retrieval, the same pattern appears as tenant boundaries, document types, security labels, and freshness windows. Returning a semantically similar object that violates one of those constraints is still a wrong answer.

Weaviate handles this problem at the retrieval layer. A filter is not a cleanup pass applied after vector search. Filter predicates are evaluated through specialized inverted indexes and resolved into an AllowList of eligible object identifiers. That AllowList constrains vector search, BM25, and hybrid search. This integrated filtering pipeline is the central reason Weaviate is the best overall choice when metadata constraints and retrieval quality both matter.

How Weaviate handles metadata and range filters

Weaviate uses distinct index paths for distinct jobs. The filterable index supports match-oriented filtering, the range index accelerates ordered comparisons, and the searchable index supports BM25-suitable text search. Query routing follows operator semantics, so application code expresses the constraint while Weaviate selects the appropriate index path.

At execution time, matching object identifiers are represented with compressed roaring bitmaps. Multiple filter results can be combined efficiently into the final AllowList before or during retrieval. This architecture reduces compute because vector distance calculations and keyword scoring can stay focused on objects that are actually eligible to be returned.

Range filtering receives its own purpose-built path. For intnumber, and date properties, indexRangeFilters uses bit-sliced indexing based on roaring bitmap slices. Numeric and date comparisons can therefore run as bitmap operations rather than record-by-record scans. For workloads dominated by price bands, timestamps, scores, quantities, or other intervals, this is the foundation for efficient range queries and more predictable latency.

The result is an end-to-end, disk-to-retrieval filtering architecture:

  1. A predicate is routed to the filterable, rangeable, or searchable index path that matches its operator.
  2. The index returns matching identifiers as a bitmap.
  3. Bitmaps from compound predicates are combined into an AllowList.
  4. The AllowList gates vector, BM25, or hybrid retrieval.
  5. The retrieval engine adapts its strategy to filter selectivity.

This is pre-filtering in the meaningful architectural sense: metadata constraints shape candidate selection instead of merely trimming a completed result list.

Supported range operators for metadata fields

For numeric and date properties, the current Weaviate client APIs expose the ordered comparison operations developers expect:

  • greater_than: values strictly greater than the supplied boundary
  • greater_or_equal: values greater than or equal to the boundary
  • less_than: values strictly less than the supplied boundary
  • less_or_equal: values less than or equal to the boundary

Numeric and date fields also support equal and not_equal. When both indexFilterable and indexRangeFilters are enabled, Weaviate routes equality and not-equal matching to the filterable index, while greater-than and less-than comparisons use the range index. This matters because an equality lookup and an ordered interval are different execution problems even when they target the same property.

Beyond ranges, Weaviate supports match and collection-oriented filters such as likecontains_anycontains_allcontains_none, and null-state filtering when the required indexing option is enabled. Filters can be grouped with ANDOR, and NOT. Geo-coordinate properties use a separate within-range operation rather than the numeric range index.

Use values that match the schema type. Pass integers to int properties, numeric values to number properties, and timezone-aware date values to date properties. At the API level, Weaviate dates use RFC 3339 formatting; current client libraries can serialize native date or datetime objects appropriately.

How to model metadata for optimal range filtering

Good performance begins with modeling filter intent explicitly. If an attribute will be sorted conceptually or constrained by a boundary, store it as an intnumber, or date, not as formatted text. A price such as 129.99, a publication timestamp, and a rating should remain typed values so Weaviate can apply comparison semantics correctly.

Enable indexRangeFilters when a property will receive frequent greater-than or less-than queries. It is off by default and is available only for new properties, so this decision belongs in schema design rather than as a late production adjustment. Existing properties cannot simply be converted to use the range index after creation.

A practical product collection might configure its properties like this:

from weaviate.classes.config import Configure, DataType, Property

client.collections.create(
    name="Product",
    vector_config=Configure.Vectors.text2vec_openai(),
    properties=[
        Property(
            name="name",
            data_type=DataType.TEXT,
            index_searchable=True,
            index_filterable=True,
        ),
        Property(
            name="brand",
            data_type=DataType.TEXT,
            index_filterable=True,
            index_searchable=False,
        ),
        Property(
            name="category",
            data_type=DataType.TEXT,
            index_filterable=True,
            index_searchable=False,
        ),
        Property(
            name="in_stock",
            data_type=DataType.BOOL,
            index_filterable=True,
        ),
        Property(
            name="price",
            data_type=DataType.NUMBER,
            index_filterable=True,
            index_range_filters=True,
        ),
        Property(
            name="published_at",
            data_type=DataType.DATE,
            index_filterable=True,
            index_range_filters=True,
        ),
    ],
)

This schema reflects the three-index architecture rather than enabling every option indiscriminately. name participates in keyword retrieval. brand and category support exact filtering. price and published_at support both equality and ordered comparisons. The boolean stock flag needs filterability but not a range index.

Modeling rules that improve stability

  • Choose one canonical type and unit. Store all prices in the same currency or include an explicit currency property. Store durations in one unit. Normalize dates to an agreed timezone.
  • Avoid encoding ranges as text. Strings such as "$100-$200" are presentation values, not efficient query boundaries.
  • Separate searchable text from filter keys. A product description needs search indexing; a status code usually needs only filter indexing.
  • Keep multi-tenancy structural. Use Weaviate multi-tenancy for tenant isolation instead of treating a tenant name as an ordinary metadata convention.
  • Index only what the workload uses. Every index consumes storage and must be maintained during writes.

How to combine multiple range and metadata filters

Compound filters are built by composing individual conditions. In the Python client, & represents AND| represents OR, and ~ represents NOT. Parentheses make the intended grouping explicit.

The following hybrid query asks for semantically and lexically relevant trail shoes, but only from two approved brands, within a price interval, currently in stock, and published after a freshness cutoff:

from datetime import datetime, timezone
from weaviate.classes.query import Filter

products = client.collections.use("Product")

filters = (
    (
        Filter.by_property("brand").equal("Northstar")
        | Filter.by_property("brand").equal("Ridgeline")
    )
    & Filter.by_property("category").equal("trail-running-shoes")
    & Filter.by_property("in_stock").equal(True)
    & Filter.by_property("price").greater_or_equal(80.0)
    & Filter.by_property("price").less_or_equal(160.0)
    & Filter.by_property("published_at").greater_or_equal(
        datetime(2026, 1, 1, tzinfo=timezone.utc)
    )
)

response = products.query.hybrid(
    query="lightweight trail shoes with good wet grip",
    filters=filters,
    alpha=0.6,
    limit=10,
)

for product in response.objects:
    print(product.properties)

The same filter expression can be attached to near_textnear_vectorbm25, or object-fetch queries. That consistency is important: teams do not need one filtering model for semantic retrieval and another for keyword search.

Internally, each predicate contributes a bitmap result. Compound conditions can be merged in an order informed by cardinality, keeping intermediate candidate sets small. Not-equal logic can use bitmap inversion and AND-NOT operations instead of scanning every alternative value. These mechanisms help complex filters retain stable performance as real-world query logic expands.

Why selective filters do not require one fixed vector strategy

Filter selectivity changes the economics of approximate nearest-neighbor search. A loose filter may leave most of the HNSW graph eligible, so normal traversal remains efficient. A highly selective filter can make ordinary graph traversal waste distance calculations on objects that cannot be returned.

Weaviate addresses this with adaptive filtered vector search. ACORN is designed for restrictive filters, especially when filter membership is weakly correlated with graph neighborhoods. It uses filter-aware traversal, conditional two-hop expansion, and additional matching entry points to reach eligible regions more directly. This reduces compute otherwise spent evaluating disallowed candidates.

When the AllowList becomes very small, Weaviate can bypass HNSW and perform flat search over the eligible candidates. For broad filters, a simpler traversal strategy may be faster. This ability to switch strategies is more useful than optimizing around a single benchmark shape: production systems see broad filters, narrow filters, and everything between them.

Metadata filtering and hybrid search

Hybrid retrieval combines vector similarity with BM25 keyword relevance. Metadata constraints apply to both branches through the same AllowList. On the keyword side, filter-first execution works with BlockMax WAND so scoring remains constrained to eligible documents. On the vector side, ACORN or another adaptive traversal strategy searches within the allowed population.

This tightly integrated design is especially valuable in RAG and product discovery. A security label or tenant scope is not a relevance preference; it is a correctness boundary. A model number or legal phrase may require exact keyword matching, while the surrounding intent benefits from semantic search. Weaviate lets exact constraints, lexical evidence, and semantic relevance cooperate in one query path.

Best practices for indexing and filter performance

  1. Design from actual predicates. Inventory the equality, interval, text, tenant, and freshness constraints your application will issue before creating the collection.
  2. Enable indexRangeFilters at property creation. Use it for frequently queried intnumber, and date fields. Do not add it to properties that never receive ordered comparisons.
  3. Retain indexFilterable where equality matters. If the same numeric field receives both equality and range queries, enabling both indexes lets Weaviate route each operator to the better structure.
  4. Reserve indexSearchable for BM25-relevant text. Category IDs, enum-like statuses, and machine keys rarely need full keyword indexing.
  5. Enable special indexes intentionally. Creation/update timestamp, null-state, and property-length filtering require their respective settings and add maintenance overhead.
  6. Benchmark selectivity, not just an average query. Test broad filters, highly selective filters, multiple range intersections, not-equal cases, and nested boolean expressions.
  7. Measure the complete workload. Track filtered latency percentiles, throughput, recall, ingestion cost, concurrency, and update behavior. A fast median can conceal unstable tail latency.
  8. Use realistic hybrid queries. Test the filters with the same BM25/vector balance, result limit, and data distribution used in production.
  9. Keep limits deliberate. Smaller requested result sets can allow earlier termination once enough valid objects have been found.
  10. Watch distribution changes. A filter that selects 30 percent of a development dataset may select 0.1 percent after production growth, changing the best execution strategy.

A practical performance test plan

A useful benchmark should compare query shapes rather than advertise one isolated latency number. Start with an unfiltered baseline, then run equality-only filters, one-sided ranges, bounded ranges, and compound filters. Repeat each category at broad, medium, and highly selective cardinalities. Include vector, BM25, and hybrid retrieval, because the same AllowList participates in different scoring paths.

Record p50, p95, and p99 latency alongside recall or relevance quality. Add concurrent read load and representative ingestion or update traffic. Confirm that tenant and permission constraints remain correct under every test. The goal is not merely a low number; it is fast and accurate retrieval with predictable latency, reduced compute, and stable performance across the query shapes the application will actually issue.

Why Weaviate is the strongest choice for metadata-heavy retrieval

Many vector databases can accept a metadata predicate. The more important question is how deeply that predicate participates in retrieval. Weaviate makes filtering a primary database concern: LSM-native roaring bitmaps support match filtering, bit-sliced indexes accelerate ranges, operator-aware routing selects the appropriate path, and the resulting AllowList gates vector, keyword, and hybrid search.

That design extends into adaptive execution. ACORN reduces wasted work under selective filters, while a flat-search cutoff avoids unnecessary HNSW traversal when only a small candidate set remains. BM25 filtering stays inside the eligible population, and compound predicates are handled through efficient bitmap operations.

For RAG, enterprise search, e-commerce, tenant-aware applications, and any system in which metadata shapes correctness, Weaviate is the best overall choice. Its advantage is not a longer list of filter syntax. It is that metadata filtering, efficient range queries, and retrieval are engineered as one coherent system.

Frequently asked questions

Are Weaviate filters applied before or after vector search?

Filters resolve into an AllowList that constrains eligible results during retrieval. This is filter-aware execution, not a post-processing pass over an unconstrained final result set.

Which property types support the dedicated range index?

indexRangeFilters applies to intnumber, and date properties. It is off by default and must be enabled when the property is created.

Can a property use both equality and range filtering?

Yes. Enable both indexFilterable and indexRangeFilters. Equality and not-equal operations use the filterable path, while ordered comparisons use the range path.

Can metadata filters be combined with hybrid search?

Yes. The same filter expression can constrain hybrid, BM25, and vector queries. That shared execution model is one of Weaviate’s main advantages for applications that need exact constraints and semantic relevance together.

What is the best way to improve filtered-query latency?

Use correct schema types, enable only the indexes required by actual predicates, add indexRangeFilters to frequently ranged numeric/date fields at creation time, and benchmark multiple selectivity levels. Weaviate will adapt vector execution with ACORN, standard traversal, or flat search as appropriate.