Skip to main content
Chroma provides powerful querying capabilities to find relevant documents using semantic similarity and metadata filtering.

Query vs Get

Chroma has two main retrieval methods: query() finds the most similar records using vector similarity:
results = collection.query(
    query_texts=["artificial intelligence"],
    n_results=10
)
Use query when you want to find semantically similar documents.

Get: Direct Retrieval

get() retrieves specific records by ID or metadata filters:
results = collection.get(
    ids=["doc1", "doc2"],
    where={"category": "news"}
)
Use get when you know which documents you want or need to filter without similarity.

Basic Query

Find similar documents using text:
results = collection.query(
    query_texts=["machine learning algorithms"],
    n_results=5
)

print(results['documents'])    # The matching documents
print(results['distances'])    # Similarity distances
print(results['metadatas'])    # Metadata for each result

Query Parameters

Query Input Types

You can query with different input types:
# Query with text (automatically embedded)
results = collection.query(
    query_texts=["search query"],
    n_results=10
)

# Query with pre-computed embeddings
results = collection.query(
    query_embeddings=[[0.1, 0.2, 0.3, ...]],
    n_results=10
)

# Query with images
import numpy as np
from PIL import Image

image = np.array(Image.open("photo.jpg"))
results = collection.query(
    query_images=[image],
    n_results=10
)

# Query with URIs
results = collection.query(
    query_uris=["https://example.com/image.jpg"],
    n_results=10
)
Provide exactly one query input type per call. Don’t mix query_texts, query_embeddings, query_images, or query_uris.

Number of Results

Control how many results to return:
results = collection.query(
    query_texts=["search query"],
    n_results=20  # Return top 20 most similar
)

Including Fields

Specify which fields to include in results:
results = collection.query(
    query_texts=["search query"],
    n_results=10,
    include=["documents", "metadatas", "distances", "embeddings"]
)
Available fields:
  • documents: The document text
  • metadatas: Metadata for each record
  • distances: Similarity distances
  • embeddings: The embedding vectors
  • uris: URIs if stored
Default: ["documents", "metadatas", "distances"]

Batch Queries

Query multiple queries at once:
results = collection.query(
    query_texts=[
        "machine learning",
        "deep learning",
        "neural networks"
    ],
    n_results=5
)

# Results are batched - one result set per query
for i, (ids, docs, distances) in enumerate(
    zip(results['ids'], results['documents'], results['distances'])
):
    print(f"Query {i}: {docs}")

Filtering Queries

Combine similarity search with metadata filtering:

Metadata Filters

results = collection.query(
    query_texts=["artificial intelligence"],
    n_results=10,
    where={"category": "research", "year": {"$gte": 2020}}
)
This finds similar documents that also match the metadata criteria.

Document Filters

results = collection.query(
    query_texts=["neural networks"],
    n_results=10,
    where_document={"$contains": "tensorflow"}
)

Combined Filters

results = collection.query(
    query_texts=["machine learning"],
    n_results=10,
    where={
        "$and": [
            {"category": "tutorial"},
            {"difficulty": {"$in": ["beginner", "intermediate"]}}
        ]
    },
    where_document={"$contains": "python"}
)

Filter by IDs

Restrict search to specific IDs:
results = collection.query(
    query_texts=["search query"],
    ids=["doc1", "doc2", "doc5"],  # Only search these documents
    n_results=2
)

Query Results Format

Query results are returned in a batched, columnar format:
results = collection.query(
    query_texts=["query1", "query2"],
    n_results=3
)

# Results structure
results = {
    'ids': [                    # List of result lists (one per query)
        ['id1', 'id2', 'id3'],  # Results for query1
        ['id4', 'id5', 'id6']   # Results for query2
    ],
    'documents': [
        ['doc1', 'doc2', 'doc3'],
        ['doc4', 'doc5', 'doc6']
    ],
    'distances': [
        [0.1, 0.2, 0.3],
        [0.15, 0.25, 0.35]
    ],
    'metadatas': [
        [{...}, {...}, {...}],
        [{...}, {...}, {...}]
    ],
    'included': ['documents', 'metadatas', 'distances']
}
Iterate over results:
# Process each query's results
for batch_ids, batch_docs, batch_dists in zip(
    results['ids'], results['documents'], results['distances']
):
    # Process each result in the batch
    for id, doc, dist in zip(batch_ids, batch_docs, batch_dists):
        print(f"ID: {id}, Distance: {dist}")
        print(f"Document: {doc}")

Get Operations

Retrieve documents directly without similarity search:

Get by IDs

results = collection.get(
    ids=["doc1", "doc2", "doc3"]
)

Get with Filters

results = collection.get(
    where={"category": "news"},
    limit=100,
    offset=0
)

Get with Pagination

# Get first 50 documents
results = collection.get(
    limit=50,
    offset=0
)

# Get next 50 documents
results = collection.get(
    limit=50,
    offset=50
)

Get Results Format

Get results are in columnar format (no batching):
results = {
    'ids': ['id1', 'id2', 'id3'],
    'documents': ['doc1', 'doc2', 'doc3'],
    'metadatas': [{...}, {...}, {...}],
    'embeddings': None,  # Unless included
    'included': ['documents', 'metadatas']
}

The Search API (Advanced)

The Search API provides a more expressive query language for hybrid search, combining vector similarity with complex filtering and ranking.
The Search API is experimental and only available in distributed and hosted Chroma.
from chromadb.execution.expression import Search, K, Knn

search = Search().where(
    K("category") == "science"
).rank(
    Knn(query=[0.1, 0.2, 0.3])
).limit(10)

results = collection.search(search)

Key Components

The Search API uses:
  • K or Key: Reference to fields (metadata or special fields)
  • where(): Filter conditions
  • rank(): Ranking expression (e.g., KNN, RRF)
  • limit(): Pagination
  • select(): Choose which fields to return

Where Expressions

Build complex filters:
from chromadb.execution.expression import K

# Simple equality
search = Search().where(K("category") == "science")

# Comparison operators
search = Search().where(K("score") > 0.5)

# Logical combinations
search = Search().where(
    (K("category") == "science") & (K("score") > 0.5)
)

# Complex logic
search = Search().where(
    (K("category") == "science") & 
    ((K("score") > 0.8) | (K("priority") == "high"))
)

Ranking Expressions

Combine multiple ranking strategies:
from chromadb.execution.expression import Knn, Val

# Simple KNN
search = Search().rank(Knn(query=[0.1, 0.2, 0.3]))

# Weighted hybrid ranking
search = Search().rank(
    Knn(query=[0.1, 0.2, 0.3]) * 0.7 + Val(0.5) * 0.3
)

# Reciprocal Rank Fusion
from chromadb.execution.expression import Rrf

search = Search().rank(
    Rrf([
        Knn(query=[0.1, 0.2], return_rank=True),
        Knn(query=[0.3, 0.4], key="sparse_embedding", return_rank=True)
    ])
)

Select Fields

search = Search().select(
    K.DOCUMENT, K.SCORE, "title", "author"
)
Predefined keys:
  • K.DOCUMENT: Document text
  • K.EMBEDDING: Embedding vector
  • K.METADATA: All metadata
  • K.SCORE: Similarity score
  • K.ID: Record ID

Group By

Group results by metadata fields:
from chromadb.execution.expression import GroupBy, MinK

# Top 3 per category
search = Search().group_by(
    GroupBy(
        keys=K("category"),
        aggregate=MinK(keys=K.SCORE, k=3)
    )
)

Read Levels

Control whether to read from the write-ahead log:
from chromadb.api.types import ReadLevel

# Default: read from index + WAL (all writes visible)
results = collection.search(search)

# Faster: skip WAL (recent writes may not be visible)
results = collection.search(
    search,
    read_level=ReadLevel.INDEX_ONLY
)

Search Result Format

Search returns column-major results:
results = collection.search([search1, search2])

# Access results
results['ids']        # List[List[str]]
results['documents']  # List[Optional[List[Optional[str]]]]
results['scores']     # List[Optional[List[Optional[float]]]]

# Convert to row format
for payload_rows in results.rows():
    for row in payload_rows:
        print(row['id'])
        print(row['document'])
        print(row['score'])

Best Practices

Balance between performance and recall:
# Good - reasonable number of results
results = collection.query(
    query_texts=["query"],
    n_results=10  # Fast and usually sufficient
)

# Be careful - very large n_results
results = collection.query(
    query_texts=["query"],
    n_results=1000  # Slower, use only if needed
)
Combine similarity with metadata filters:
# More efficient - filter first
results = collection.query(
    query_texts=["query"],
    n_results=10,
    where={"year": {"$gte": 2020}}  # Narrows search space
)
Process multiple queries in one call:
# Efficient - single batched call
results = collection.query(
    query_texts=["query1", "query2", "query3"],
    n_results=10
)

# Avoid - multiple separate calls
# results1 = collection.query(query_texts=["query1"], ...)
# results2 = collection.query(query_texts=["query2"], ...)
Reduce data transfer by including only required fields:
# Efficient - only needed fields
results = collection.query(
    query_texts=["query"],
    n_results=10,
    include=["documents", "distances"]  # Skip embeddings
)

Next Steps

Metadata

Learn about metadata filtering

Embeddings

Understand how embeddings work

Performance

Optimize query performance

Filtering Guide

Advanced filtering techniques

Build docs developers (and LLMs) love