Skip to main content
Version: 2.22-unstable

MultiQueryTextRetriever

Retrieves documents using multiple queries in parallel with a text-based Retriever.

Most common position in a pipelineAfter a QueryExpander component, before a ChatPromptBuilder in RAG pipelines
Mandatory init variablesretriever: A text-based Retriever (such as InMemoryBM25Retriever)
Mandatory run variablesqueries: A list of query strings
Output variablesdocuments: A list of retrieved documents sorted by relevance score
API referenceRetrievers
GitHub linkhttps://github.com/deepset-ai/haystack/blob/main/haystack/components/retrievers/multi_query_text_retriever.py

Overview

MultiQueryTextRetriever improves retrieval recall by searching for documents using multiple queries in parallel. It wraps a text-based Retriever (such as InMemoryBM25Retriever) and processes multiple query strings simultaneously using a thread pool.

The component:

  • Processes queries in parallel for better performance
  • Automatically deduplicates results based on document content
  • Sorts the final results by relevance score

This Retriever is particularly effective when combined with QueryExpander, which generates multiple query variations from a single user query. By searching with these variations, you can find documents that use different keywords than the original query.

Use MultiQueryTextRetriever when your documents use different words than your users' queries, or when you want to use query expansion with keyword-based search (BM25). Running multiple queries takes more time, but you can speed it up by increasing max_workers to run queries in parallel.

When to use MultiQueryEmbeddingRetriever instead

If you need semantic search where meaning matters more than exact keyword matches, use MultiQueryEmbeddingRetriever instead. It works with embedding-based Retrievers and requires a Text Embedder.

Passing Additional Retriever Parameters

You can pass additional parameters to the underlying Retriever using retriever_kwargs:

python
result = multiquery_retriever.run(
queries=["renewable energy", "sustainable power"],
retriever_kwargs={"top_k": 5}
)

Usage

On its own

In this example, we pass three queries manually to the Retriever: "renewable energy", "geothermal", and "hydropower". The Retriever runs a BM25 search for each query (retrieving up to 2 documents per query), then combines all results, removes duplicates, and sorts them by score.

python
from haystack import Document
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.retrievers import InMemoryBM25Retriever, MultiQueryTextRetriever

documents = [
Document(content="Renewable energy is energy that is collected from renewable resources."),
Document(content="Solar energy is a type of green energy that is harnessed from the sun."),
Document(content="Wind energy is another type of green energy that is generated by wind turbines."),
Document(content="Hydropower is a form of renewable energy using the flow of water to generate electricity."),
Document(content="Geothermal energy is heat that comes from the sub-surface of the earth."),
]

document_store = InMemoryDocumentStore()
document_store.write_documents(documents)

retriever = MultiQueryTextRetriever(
retriever=InMemoryBM25Retriever(document_store=document_store, top_k=2)
)

results = retriever.run(queries=["renewable energy", "geothermal", "hydropower"])

for doc in results["documents"]:
print(f"Content: {doc.content}, Score: {doc.score:.4f}")

In a pipeline with QueryExpander

This pipeline takes a single query "sustainable power" and expands it into multiple variations using an LLM (for example: "renewable energy sources", "green electricity", "clean power"). The Retriever then searches for each variation and combines the results. This way, documents about "solar energy" or "hydropower" can be found even though they don't contain the words "sustainable power".

python
from haystack import Document, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.query import QueryExpander
from haystack.components.retrievers import InMemoryBM25Retriever, MultiQueryTextRetriever

documents = [
Document(content="Renewable energy is energy that is collected from renewable resources."),
Document(content="Solar energy is a type of green energy that is harnessed from the sun."),
Document(content="Wind energy is another type of green energy that is generated by wind turbines."),
Document(content="Hydropower is a form of renewable energy using the flow of water to generate electricity."),
Document(content="Geothermal energy is heat that comes from the sub-surface of the earth."),
]

document_store = InMemoryDocumentStore()
document_store.write_documents(documents)

pipeline = Pipeline()
pipeline.add_component("query_expander", QueryExpander(n_expansions=3))
pipeline.add_component(
"retriever",
MultiQueryTextRetriever(
retriever=InMemoryBM25Retriever(document_store=document_store, top_k=2)
)
)
pipeline.connect("query_expander.queries", "retriever.queries")

result = pipeline.run({"query_expander": {"query": "sustainable power"}})

for doc in result["retriever"]["documents"]:
print(f"Score: {doc.score:.3f} | {doc.content}")

In a RAG pipeline

This RAG pipeline answers questions using query expansion. When a user asks "What types of energy come from natural sources?", the pipeline:

  1. Expands the question into multiple search queries using an LLM
  2. Retrieves relevant documents for each query variation
  3. Builds a prompt containing the retrieved documents and the original question
  4. Sends the prompt to an LLM to generate an answer

The question is sent to both the query_expander (for generating search queries) and the prompt_builder (for the final prompt to the LLM).

python
from haystack import Document, Pipeline
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.builders import ChatPromptBuilder
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.query import QueryExpander
from haystack.components.retrievers import InMemoryBM25Retriever, MultiQueryTextRetriever
from haystack.dataclasses import ChatMessage

documents = [
Document(content="Renewable energy is energy that is collected from renewable resources."),
Document(content="Solar energy is a type of green energy that is harnessed from the sun."),
Document(content="Wind energy is another type of green energy that is generated by wind turbines."),
]

document_store = InMemoryDocumentStore()
document_store.write_documents(documents)

prompt_template = [
ChatMessage.from_system("You are a helpful assistant that answers questions based on the provided documents."),
ChatMessage.from_user(
"Given these documents, answer the question.\n"
"Documents:\n"
"{% for doc in documents %}"
"{{ doc.content }}\n"
"{% endfor %}\n"
"Question: {{ question }}"
)
]

# Note: This assumes OPENAI_API_KEY environment variable is set
rag_pipeline = Pipeline()
rag_pipeline.add_component("query_expander", QueryExpander(n_expansions=2))
rag_pipeline.add_component(
"retriever",
MultiQueryTextRetriever(
retriever=InMemoryBM25Retriever(document_store=document_store, top_k=2)
)
)
rag_pipeline.add_component(
"prompt_builder",
ChatPromptBuilder(template=prompt_template, required_variables=["documents", "question"])
)
rag_pipeline.add_component("llm", OpenAIChatGenerator())

rag_pipeline.connect("query_expander.queries", "retriever.queries")
rag_pipeline.connect("retriever.documents", "prompt_builder.documents")
rag_pipeline.connect("prompt_builder.prompt", "llm.messages")

question = "What types of energy come from natural sources?"
result = rag_pipeline.run({
"query_expander": {"query": question},
"prompt_builder": {"question": question}
})

print(result["llm"]["replies"][0].text)