MultiQueryTextRetriever
Retrieves documents using multiple queries in parallel with a text-based Retriever.
| Most common position in a pipeline | After a QueryExpander component, before a ChatPromptBuilder in RAG pipelines |
| Mandatory init variables | retriever: A text-based Retriever (such as InMemoryBM25Retriever) |
| Mandatory run variables | queries: A list of query strings |
| Output variables | documents: A list of retrieved documents sorted by relevance score |
| API reference | Retrievers |
| GitHub link | https://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.
MultiQueryEmbeddingRetriever insteadIf 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:
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.
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
- YAML
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}")
components:
query_expander:
type: haystack.components.query.query_expander.QueryExpander
init_parameters:
n_expansions: 3
retriever:
type: haystack.components.retrievers.multi_query_text_retriever.MultiQueryTextRetriever
init_parameters:
retriever:
type: haystack.components.retrievers.in_memory.bm25_retriever.InMemoryBM25Retriever
init_parameters:
document_store:
type: haystack.document_stores.in_memory.document_store.InMemoryDocumentStore
init_parameters: {}
top_k: 2
connections:
- sender: query_expander.queries
receiver: retriever.queries
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:
- Expands the question into multiple search queries using an LLM
- Retrieves relevant documents for each query variation
- Builds a prompt containing the retrieved documents and the original question
- 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
- YAML
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)
components:
query_expander:
type: haystack.components.query.query_expander.QueryExpander
init_parameters:
n_expansions: 2
retriever:
type: haystack.components.retrievers.multi_query_text_retriever.MultiQueryTextRetriever
init_parameters:
retriever:
type: haystack.components.retrievers.in_memory.bm25_retriever.InMemoryBM25Retriever
init_parameters:
document_store:
type: haystack.document_stores.in_memory.document_store.InMemoryDocumentStore
init_parameters: {}
top_k: 2
prompt_builder:
type: haystack.components.builders.chat_prompt_builder.ChatPromptBuilder
init_parameters:
required_variables:
- documents
- question
llm:
type: haystack.components.generators.chat.openai.OpenAIChatGenerator
init_parameters: {}
connections:
- sender: query_expander.queries
receiver: retriever.queries
- sender: retriever.documents
receiver: prompt_builder.documents
- sender: prompt_builder.prompt
receiver: llm.messages