!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}return t}()||function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}(),document.documentElement.setAttribute("data-announcement-bar-initially-dismissed",function(){try{return"true"===localStorage.getItem("docusaurus.announcement.dismiss")}catch(t){}return!1}())

[Skip to main content](#__docusaurus_skipToContent_fallback)

LangChain v0.2 is coming soon! Preview the new docs [here](/v0.2/docs/introduction/).

[

![🦜️🔗 Langchain](/v0.1/img/brand/wordmark.png)![🦜️🔗 Langchain](/v0.1/img/brand/wordmark-dark.png)

](/v0.1/)[Docs](/v0.1/docs/get_started/introduction/)[Use cases](/v0.1/docs/use_cases/)[Integrations](/v0.1/docs/integrations/platforms/)[API Reference](https://api.js.langchain.com)

[More](#)

*   [People](/v0.1/docs/people/)
*   [Community](/v0.1/docs/community/)
*   [Tutorials](/v0.1/docs/additional_resources/tutorials/)
*   [Contributing](/v0.1/docs/contributing/)

[v0.1](#)

*   [v0.2](https://js.langchain.com/v0.2/docs/introduction)
*   [v0.1](/v0.1/docs/get_started/introduction/)

[🦜🔗](#)

*   [LangSmith](https://smith.langchain.com)
*   [LangSmith Docs](https://docs.smith.langchain.com)
*   [LangChain Hub](https://smith.langchain.com/hub)
*   [LangServe](https://github.com/langchain-ai/langserve)
*   [Python Docs](https://python.langchain.com/)

[Chat](https://chatjs.langchain.com)[](https://github.com/langchain-ai/langchainjs)

Search

*   [Get started](/v0.1/docs/get_started/)
    
    *   [Introduction](/v0.1/docs/get_started/introduction/)
    *   [Installation](/v0.1/docs/get_started/installation/)
    *   [Quickstart](/v0.1/docs/get_started/quickstart/)
*   [LangChain Expression Language](/v0.1/docs/expression_language/)
    
    *   [Get started](/v0.1/docs/expression_language/get_started/)
    *   [Why use LCEL?](/v0.1/docs/expression_language/why/)
    *   [Interface](/v0.1/docs/expression_language/interface/)
    *   [Streaming](/v0.1/docs/expression_language/streaming/)
    *   [How to](/v0.1/docs/expression_language/how_to/routing/)
        
    *   [Cookbook](/v0.1/docs/expression_language/cookbook/)
        
    *   [LangChain Expression Language (LCEL)](/v0.1/docs/expression_language/)
*   [Modules](/v0.1/docs/modules/)
    
    *   [Model I/O](/v0.1/docs/modules/model_io/)
        
    *   [Retrieval](/v0.1/docs/modules/data_connection/)
        
        *   [Document loaders](/v0.1/docs/modules/data_connection/document_loaders/)
            
        *   [Text Splitters](/v0.1/docs/modules/data_connection/document_transformers/)
            
        *   [Retrievers](/v0.1/docs/modules/data_connection/retrievers/)
            
            *   [Custom retrievers](/v0.1/docs/modules/data_connection/retrievers/custom/)
            *   [Contextual compression](/v0.1/docs/modules/data_connection/retrievers/contextual_compression/)
            *   [Matryoshka Retriever](/v0.1/docs/modules/data_connection/retrievers/matryoshka_retriever/)
            *   [MultiQuery Retriever](/v0.1/docs/modules/data_connection/retrievers/multi-query-retriever/)
            *   [MultiVector Retriever](/v0.1/docs/modules/data_connection/retrievers/multi-vector-retriever/)
            *   [Parent Document Retriever](/v0.1/docs/modules/data_connection/retrievers/parent-document-retriever/)
            *   [Self-querying](/v0.1/docs/modules/data_connection/retrievers/self_query/)
                
            *   [Similarity Score Threshold](/v0.1/docs/modules/data_connection/retrievers/similarity-score-threshold-retriever/)
            *   [Time-weighted vector store retriever](/v0.1/docs/modules/data_connection/retrievers/time_weighted_vectorstore/)
            *   [Vector store-backed retriever](/v0.1/docs/modules/data_connection/retrievers/vectorstore/)
        *   [Retrieval](/v0.1/docs/modules/data_connection/)
        *   [Text embedding models](/v0.1/docs/modules/data_connection/text_embedding/)
            
        *   [Vector stores](/v0.1/docs/modules/data_connection/vectorstores/)
            
        *   [Indexing](/v0.1/docs/modules/data_connection/indexing/)
        *   [Experimental](/v0.1/docs/modules/data_connection/experimental/multimodal_embeddings/google_vertex_ai/)
            
    *   [Chains](/v0.1/docs/modules/chains/)
    *   [Agents](/v0.1/docs/modules/agents/)
        
    *   [More](/v0.1/docs/modules/memory/)
        
*   [Security](/v0.1/docs/security/)
*   [Guides](/v0.1/docs/guides/)
    
*   [Ecosystem](/v0.1/docs/ecosystem/)
    
*   [LangGraph](/v0.1/docs/langgraph/)
*   * * *
    

*   [](/v0.1/)
*   [Modules](/v0.1/docs/modules/)
*   [Retrieval](/v0.1/docs/modules/data_connection/)
*   [Retrievers](/v0.1/docs/modules/data_connection/retrievers/)
*   MultiVector Retriever

MultiVector Retriever
=====================

It can often be beneficial to store multiple vectors per document. LangChain has a base MultiVectorRetriever which makes querying this type of setup easier!

A lot of the complexity lies in how to create the multiple vectors per document. This notebook covers some of the common ways to create those vectors and use the MultiVectorRetriever.

Some methods to create multiple vectors per document include:

*   smaller chunks: split a document into smaller chunks, and embed those (e.g. the [ParentDocumentRetriever](/v0.1/docs/modules/data_connection/retrievers/parent-document-retriever/))
*   summary: create a summary for each document, embed that along with (or instead of) the document
*   hypothetical questions: create hypothetical questions that each document would be appropriate to answer, embed those along with (or instead of) the document

Note that this also enables another method of adding embeddings - manually. This is great because you can explicitly add questions or queries that should lead to a document being recovered, giving you more control.

Smaller chunks[​](#smaller-chunks "Direct link to Smaller chunks")
------------------------------------------------------------------

Often times it can be useful to retrieve larger chunks of information, but embed smaller chunks. This allows for embeddings to capture the semantic meaning as closely as possible, but for as much context as possible to be passed downstream. NOTE: this is what the ParentDocumentRetriever does. Here we show what is going on under the hood.

tip

See [this section for general instructions on installing integration packages](/v0.1/docs/get_started/installation/#installing-integration-packages).

*   npm
*   Yarn
*   pnpm

    npm install @langchain/openai @langchain/community

    yarn add @langchain/openai @langchain/community

    pnpm add @langchain/openai @langchain/community

    import * as uuid from "uuid";import { MultiVectorRetriever } from "langchain/retrievers/multi_vector";import { FaissStore } from "@langchain/community/vectorstores/faiss";import { OpenAIEmbeddings } from "@langchain/openai";import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";import { InMemoryStore } from "langchain/storage/in_memory";import { TextLoader } from "langchain/document_loaders/fs/text";import { Document } from "@langchain/core/documents";const textLoader = new TextLoader("../examples/state_of_the_union.txt");const parentDocuments = await textLoader.load();const splitter = new RecursiveCharacterTextSplitter({  chunkSize: 10000,  chunkOverlap: 20,});const docs = await splitter.splitDocuments(parentDocuments);const idKey = "doc_id";const docIds = docs.map((_) => uuid.v4());const childSplitter = new RecursiveCharacterTextSplitter({  chunkSize: 400,  chunkOverlap: 0,});const subDocs = [];for (let i = 0; i < docs.length; i += 1) {  const childDocs = await childSplitter.splitDocuments([docs[i]]);  const taggedChildDocs = childDocs.map((childDoc) => {    // eslint-disable-next-line no-param-reassign    childDoc.metadata[idKey] = docIds[i];    return childDoc;  });  subDocs.push(...taggedChildDocs);}// The byteStore to use to store the original chunksconst byteStore = new InMemoryStore<Uint8Array>();// The vectorstore to use to index the child chunksconst vectorstore = await FaissStore.fromDocuments(  subDocs,  new OpenAIEmbeddings());const retriever = new MultiVectorRetriever({  vectorstore,  byteStore,  idKey,  // Optional `k` parameter to search for more child documents in VectorStore.  // Note that this does not exactly correspond to the number of final (parent) documents  // retrieved, as multiple child documents can point to the same parent.  childK: 20,  // Optional `k` parameter to limit number of final, parent documents returned from this  // retriever and sent to LLM. This is an upper-bound, and the final count may be lower than this.  parentK: 5,});const keyValuePairs: [string, Document][] = docs.map((originalDoc, i) => [  docIds[i],  originalDoc,]);// Use the retriever to add the original chunks to the document storeawait retriever.docstore.mset(keyValuePairs);// Vectorstore alone retrieves the small chunksconst vectorstoreResult = await retriever.vectorstore.similaritySearch(  "justice breyer");console.log(vectorstoreResult[0].pageContent.length);/*  390*/// Retriever returns larger resultconst retrieverResult = await retriever.invoke("justice breyer");console.log(retrieverResult[0].pageContent.length);/*  9770*/

#### API Reference:

*   [MultiVectorRetriever](https://api.js.langchain.com/classes/langchain_retrievers_multi_vector.MultiVectorRetriever.html) from `langchain/retrievers/multi_vector`
*   [FaissStore](https://api.js.langchain.com/classes/langchain_community_vectorstores_faiss.FaissStore.html) from `@langchain/community/vectorstores/faiss`
*   [OpenAIEmbeddings](https://api.js.langchain.com/classes/langchain_openai.OpenAIEmbeddings.html) from `@langchain/openai`
*   [RecursiveCharacterTextSplitter](https://api.js.langchain.com/classes/langchain_textsplitters.RecursiveCharacterTextSplitter.html) from `langchain/text_splitter`
*   [InMemoryStore](https://api.js.langchain.com/classes/langchain_core_stores.InMemoryStore.html) from `langchain/storage/in_memory`
*   [TextLoader](https://api.js.langchain.com/classes/langchain_document_loaders_fs_text.TextLoader.html) from `langchain/document_loaders/fs/text`
*   [Document](https://api.js.langchain.com/classes/langchain_core_documents.Document.html) from `@langchain/core/documents`

Summary[​](#summary "Direct link to Summary")
---------------------------------------------

Oftentimes a summary may be able to distill more accurately what a chunk is about, leading to better retrieval. Here we show how to create summaries, and then embed those.

    import * as uuid from "uuid";import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";import { MultiVectorRetriever } from "langchain/retrievers/multi_vector";import { FaissStore } from "@langchain/community/vectorstores/faiss";import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";import { InMemoryStore } from "langchain/storage/in_memory";import { TextLoader } from "langchain/document_loaders/fs/text";import { PromptTemplate } from "@langchain/core/prompts";import { StringOutputParser } from "@langchain/core/output_parsers";import { RunnableSequence } from "@langchain/core/runnables";import { Document } from "@langchain/core/documents";const textLoader = new TextLoader("../examples/state_of_the_union.txt");const parentDocuments = await textLoader.load();const splitter = new RecursiveCharacterTextSplitter({  chunkSize: 10000,  chunkOverlap: 20,});const docs = await splitter.splitDocuments(parentDocuments);const chain = RunnableSequence.from([  { content: (doc: Document) => doc.pageContent },  PromptTemplate.fromTemplate(`Summarize the following document:\n\n{content}`),  new ChatOpenAI({    maxRetries: 0,  }),  new StringOutputParser(),]);const summaries = await chain.batch(docs, {  maxConcurrency: 5,});const idKey = "doc_id";const docIds = docs.map((_) => uuid.v4());const summaryDocs = summaries.map((summary, i) => {  const summaryDoc = new Document({    pageContent: summary,    metadata: {      [idKey]: docIds[i],    },  });  return summaryDoc;});// The byteStore to use to store the original chunksconst byteStore = new InMemoryStore<Uint8Array>();// The vectorstore to use to index the child chunksconst vectorstore = await FaissStore.fromDocuments(  summaryDocs,  new OpenAIEmbeddings());const retriever = new MultiVectorRetriever({  vectorstore,  byteStore,  idKey,});const keyValuePairs: [string, Document][] = docs.map((originalDoc, i) => [  docIds[i],  originalDoc,]);// Use the retriever to add the original chunks to the document storeawait retriever.docstore.mset(keyValuePairs);// We could also add the original chunks to the vectorstore if we wish// const taggedOriginalDocs = docs.map((doc, i) => {//   doc.metadata[idKey] = docIds[i];//   return doc;// });// retriever.vectorstore.addDocuments(taggedOriginalDocs);// Vectorstore alone retrieves the small chunksconst vectorstoreResult = await retriever.vectorstore.similaritySearch(  "justice breyer");console.log(vectorstoreResult[0].pageContent.length);/*  1118*/// Retriever returns larger resultconst retrieverResult = await retriever.invoke("justice breyer");console.log(retrieverResult[0].pageContent.length);/*  9770*/

#### API Reference:

*   [ChatOpenAI](https://api.js.langchain.com/classes/langchain_openai.ChatOpenAI.html) from `@langchain/openai`
*   [OpenAIEmbeddings](https://api.js.langchain.com/classes/langchain_openai.OpenAIEmbeddings.html) from `@langchain/openai`
*   [MultiVectorRetriever](https://api.js.langchain.com/classes/langchain_retrievers_multi_vector.MultiVectorRetriever.html) from `langchain/retrievers/multi_vector`
*   [FaissStore](https://api.js.langchain.com/classes/langchain_community_vectorstores_faiss.FaissStore.html) from `@langchain/community/vectorstores/faiss`
*   [RecursiveCharacterTextSplitter](https://api.js.langchain.com/classes/langchain_textsplitters.RecursiveCharacterTextSplitter.html) from `langchain/text_splitter`
*   [InMemoryStore](https://api.js.langchain.com/classes/langchain_core_stores.InMemoryStore.html) from `langchain/storage/in_memory`
*   [TextLoader](https://api.js.langchain.com/classes/langchain_document_loaders_fs_text.TextLoader.html) from `langchain/document_loaders/fs/text`
*   [PromptTemplate](https://api.js.langchain.com/classes/langchain_core_prompts.PromptTemplate.html) from `@langchain/core/prompts`
*   [StringOutputParser](https://api.js.langchain.com/classes/langchain_core_output_parsers.StringOutputParser.html) from `@langchain/core/output_parsers`
*   [RunnableSequence](https://api.js.langchain.com/classes/langchain_core_runnables.RunnableSequence.html) from `@langchain/core/runnables`
*   [Document](https://api.js.langchain.com/classes/langchain_core_documents.Document.html) from `@langchain/core/documents`

Hypothetical queries[​](#hypothetical-queries "Direct link to Hypothetical queries")
------------------------------------------------------------------------------------

An LLM can also be used to generate a list of hypothetical questions that could be asked of a particular document. These questions can then be embedded and used to retrieve the original document:

    import * as uuid from "uuid";import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";import { MultiVectorRetriever } from "langchain/retrievers/multi_vector";import { FaissStore } from "@langchain/community/vectorstores/faiss";import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";import { InMemoryStore } from "langchain/storage/in_memory";import { TextLoader } from "langchain/document_loaders/fs/text";import { JsonKeyOutputFunctionsParser } from "langchain/output_parsers";import { PromptTemplate } from "@langchain/core/prompts";import { RunnableSequence } from "@langchain/core/runnables";import { Document } from "@langchain/core/documents";const textLoader = new TextLoader("../examples/state_of_the_union.txt");const parentDocuments = await textLoader.load();const splitter = new RecursiveCharacterTextSplitter({  chunkSize: 10000,  chunkOverlap: 20,});const docs = await splitter.splitDocuments(parentDocuments);const functionsSchema = [  {    name: "hypothetical_questions",    description: "Generate hypothetical questions",    parameters: {      type: "object",      properties: {        questions: {          type: "array",          items: {            type: "string",          },        },      },      required: ["questions"],    },  },];const functionCallingModel = new ChatOpenAI({  maxRetries: 0,  model: "gpt-4",}).bind({  functions: functionsSchema,  function_call: { name: "hypothetical_questions" },});const chain = RunnableSequence.from([  { content: (doc: Document) => doc.pageContent },  PromptTemplate.fromTemplate(    `Generate a list of 3 hypothetical questions that the below document could be used to answer:\n\n{content}`  ),  functionCallingModel,  new JsonKeyOutputFunctionsParser<string[]>({ attrName: "questions" }),]);const hypotheticalQuestions = await chain.batch(docs, {  maxConcurrency: 5,});const idKey = "doc_id";const docIds = docs.map((_) => uuid.v4());const hypotheticalQuestionDocs = hypotheticalQuestions  .map((questionArray, i) => {    const questionDocuments = questionArray.map((question) => {      const questionDocument = new Document({        pageContent: question,        metadata: {          [idKey]: docIds[i],        },      });      return questionDocument;    });    return questionDocuments;  })  .flat();// The byteStore to use to store the original chunksconst byteStore = new InMemoryStore<Uint8Array>();// The vectorstore to use to index the child chunksconst vectorstore = await FaissStore.fromDocuments(  hypotheticalQuestionDocs,  new OpenAIEmbeddings());const retriever = new MultiVectorRetriever({  vectorstore,  byteStore,  idKey,});const keyValuePairs: [string, Document][] = docs.map((originalDoc, i) => [  docIds[i],  originalDoc,]);// Use the retriever to add the original chunks to the document storeawait retriever.docstore.mset(keyValuePairs);// We could also add the original chunks to the vectorstore if we wish// const taggedOriginalDocs = docs.map((doc, i) => {//   doc.metadata[idKey] = docIds[i];//   return doc;// });// retriever.vectorstore.addDocuments(taggedOriginalDocs);// Vectorstore alone retrieves the small chunksconst vectorstoreResult = await retriever.vectorstore.similaritySearch(  "justice breyer");console.log(vectorstoreResult[0].pageContent);/*  "What measures will be taken to crack down on corporations overcharging American businesses and consumers?"*/// Retriever returns larger resultconst retrieverResult = await retriever.invoke("justice breyer");console.log(retrieverResult[0].pageContent.length);/*  9770*/

#### API Reference:

*   [ChatOpenAI](https://api.js.langchain.com/classes/langchain_openai.ChatOpenAI.html) from `@langchain/openai`
*   [OpenAIEmbeddings](https://api.js.langchain.com/classes/langchain_openai.OpenAIEmbeddings.html) from `@langchain/openai`
*   [MultiVectorRetriever](https://api.js.langchain.com/classes/langchain_retrievers_multi_vector.MultiVectorRetriever.html) from `langchain/retrievers/multi_vector`
*   [FaissStore](https://api.js.langchain.com/classes/langchain_community_vectorstores_faiss.FaissStore.html) from `@langchain/community/vectorstores/faiss`
*   [RecursiveCharacterTextSplitter](https://api.js.langchain.com/classes/langchain_textsplitters.RecursiveCharacterTextSplitter.html) from `langchain/text_splitter`
*   [InMemoryStore](https://api.js.langchain.com/classes/langchain_core_stores.InMemoryStore.html) from `langchain/storage/in_memory`
*   [TextLoader](https://api.js.langchain.com/classes/langchain_document_loaders_fs_text.TextLoader.html) from `langchain/document_loaders/fs/text`
*   [JsonKeyOutputFunctionsParser](https://api.js.langchain.com/classes/langchain_output_parsers.JsonKeyOutputFunctionsParser.html) from `langchain/output_parsers`
*   [PromptTemplate](https://api.js.langchain.com/classes/langchain_core_prompts.PromptTemplate.html) from `@langchain/core/prompts`
*   [RunnableSequence](https://api.js.langchain.com/classes/langchain_core_runnables.RunnableSequence.html) from `@langchain/core/runnables`
*   [Document](https://api.js.langchain.com/classes/langchain_core_documents.Document.html) from `@langchain/core/documents`

* * *

#### Help us out by providing feedback on this documentation page:

[

Previous

MultiQuery Retriever

](/v0.1/docs/modules/data_connection/retrievers/multi-query-retriever/)[

Next

Parent Document Retriever

](/v0.1/docs/modules/data_connection/retrievers/parent-document-retriever/)

Community

*   [Discord](https://discord.gg/cU2adEyC7w)
*   [Twitter](https://twitter.com/LangChainAI)

GitHub

*   [Python](https://github.com/langchain-ai/langchain)
*   [JS/TS](https://github.com/langchain-ai/langchainjs)

More

*   [Homepage](https://langchain.com)
*   [Blog](https://blog.langchain.dev)

Copyright © 2024 LangChain, Inc.