Skip to main content

Vector and keyword search

Beyond selecting the right search type, you can fine-tune search behavior to improve result quality.

Combining searches with filters

Filters can be combined with searches to narrow results. Weaviate applies pre-filtering: the filter is evaluated first, using the inverted index to build an allow-list of eligible objects, and the search then runs over only those candidates.

This matters. The alternative (post-filtering, where the search runs first and non-matching objects are discarded afterwards) can leave you with far fewer results than you asked for, or none at all.

Weaviate allows multiple filters to be combined, as well as metadata-based filtering.

This example shows how to use filters, so that a search for articles about "artificial intelligence" is limited to those that:

  • contain the word "automotive", and
  • were created after May 1, 2025.
from weaviate.classes.query import Filter
from datetime import datetime, timezone

articles = client.collections.use("Articles")

filters = (
Filter.by_property("title").like("*automotive*") &
Filter.by_creation_time().greater_or_equal(datetime(2025, 5, 1, tzinfo=timezone.utc))
)

articles.query.near_text(
query="artificial intelligence",
limit=10,
filters=filters
)
Indexing metadata

By default, metadata of timestamps, null states, and property lengths are not indexed. Enable them first in the inverted index configuration before using them in filters.

For exactly this reason, the Articles collection queried above is defined with index_timestamps=True. It also defines a vectorizer, since the query runs near_text:

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

client.collections.create(
name="Articles",
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="content", data_type=DataType.TEXT),
],
vector_config=Configure.Vectors.text2vec_weaviate(
source_properties=["title", "content"]
),
# Timestamps are not indexed by default - enable them to filter on creation time
inverted_index_config=Configure.inverted_index(
index_timestamps=True
)
)

Vector search strategies

You've already learned how selecting the right embedding model can impact search quality. But there are many other ways to optimize vector search.

Property selection for vectorization

By default, all eligible properties are vectorized. But not all properties should contribute to the semantic meaning.

Metadata fields like IDs, timestamps, or internal codes can add noise. In other cases, you may only want to vectorize certain properties to support your search strategy.

So, carefully consider which source properties to include in your vector.

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

client.collections.create(
name="Products",
properties=[
Property(name="name", data_type=DataType.TEXT),
Property(name="description", data_type=DataType.TEXT),
Property(name="category", data_type=DataType.TEXT_ARRAY),
Property(
name="product_id",
data_type=DataType.TEXT,
skip_vectorization=True, # Exclude from vector
tokenization=Tokenization.FIELD
),
Property(
name="product_image",
data_type=DataType.BLOB
)
],
# Vector includes only: name + description + category
# Result: Cleaner semantic representation
vector_config=Configure.Vectors.text2vec_weaviate(
source_properties=["name", "description", "category"]
)
)

Skip vectorization for metadata, such as:

  • IDs and internal codes
  • Timestamps and dates
  • Status flags (active/inactive)

Consider including:

  • Content descriptions and titles
  • Categories and tags (if semantically meaningful)
  • Additional context for embedding

Multiple vectors per object

Each object can have multiple independent vectors, each with its own index.

As a result, each vector can be optimized for a different use case.

Example:

  • A product with a text vector for semantic search, and an image vector for image similarity.
from weaviate.classes.config import Property, DataType, Configure, Tokenization

client.collections.create(
name="Products",
properties=[
Property(name="name", data_type=DataType.TEXT),
Property(name="description", data_type=DataType.TEXT),
Property(name="category", data_type=DataType.TEXT_ARRAY),
Property(
name="product_id",
data_type=DataType.TEXT,
skip_vectorization=True, # Exclude from vector
tokenization=Tokenization.FIELD
),
Property(
name="product_image",
data_type=DataType.BLOB
)
],
# Create two vectors per object: One for text, one for image
vector_config=[
Configure.Vectors.text2vec_weaviate(
name="text",
source_properties=["name", "description", "category"]
),
Configure.Vectors.multi2vec_cohere(
name="image",
image_fields=["product_image"],
model="embed-v4.0"
),
]
)

Multi-target vector search allows you to search on multiple vectors at once.

This can blend similarities against multiple vectors, and provide a robust retrieval solution for more complex use cases, or where a user behavior may be unpredictable.

Here is an example of how to perform multi-target vector search.

from weaviate.classes.query import TargetVectors

products = client.collections.use("Products")
response = products.query.near_text(
query="red minimalist wireless headphones",
target_vectors=TargetVectors.minimum(["text", "image"]), # How to weight the vector distances
limit=10
)

Note that the distance from each vector can be weighted according to your use case. Use the TargetVectors class to weight the vectors, such as using a .minimum distance, or a .average distance, or even using a set of .manual_weights.

What's next?

Now let's explore keyword and hybrid search strategies, as well as re-rankers in the next lesson.

Login to track your progress