!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)

You are viewing the **preview** v0.2 docs. View the **stable** v0.1 docs [here](/v0.1/docs/get_started/introduction/). Leave feedback on the v0.2 docs [here](https://github.com/langchain-ai/langchainjs/discussions/5386).

[

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

](/v0.2/)[Integrations](/v0.2/docs/integrations/platforms/)[API Reference](https://v02.api.js.langchain.com)

[More](#)

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

[v0.2](#)

*   [v0.2](/v0.2/docs/introduction)
*   [v0.1](https://js.langchain.com/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

*   [Introduction](/v0.2/docs/introduction)
*   [Tutorials](/v0.2/docs/tutorials/)
    
    *   [Build a Question Answering application over a Graph Database](/v0.2/docs/tutorials/graph)
    *   [Tutorials](/v0.2/docs/tutorials/)
    *   [Build a Simple LLM Application](/v0.2/docs/tutorials/llm_chain)
    *   [Build a Query Analysis System](/v0.2/docs/tutorials/query_analysis)
    *   [Build a Chatbot](/v0.2/docs/tutorials/chatbot)
    *   [Build an Agent](/v0.2/docs/tutorials/agents)
    *   [Build an Extraction Chain](/v0.2/docs/tutorials/extraction)
    *   [Summarize Text](/v0.2/docs/tutorials/summarization)
    *   [Tagging](/v0.2/docs/tutorials/classification)
    *   [Build a Local RAG Application](/v0.2/docs/tutorials/local_rag)
    *   [Conversational RAG](/v0.2/docs/tutorials/qa_chat_history)
    *   [Build a Retrieval Augmented Generation (RAG) App](/v0.2/docs/tutorials/rag)
    *   [Build a Question/Answering system over SQL data](/v0.2/docs/tutorials/sql_qa)
*   [How-to guides](/v0.2/docs/how_to/)
    
    *   [How-to guides](/v0.2/docs/how_to/)
    *   [How to use example selectors](/v0.2/docs/how_to/example_selectors)
    *   [Installation](/v0.2/docs/how_to/installation)
    *   [How to stream responses from an LLM](/v0.2/docs/how_to/streaming_llm)
    *   [How to stream chat model responses](/v0.2/docs/how_to/chat_streaming)
    *   [How to embed text data](/v0.2/docs/how_to/embed_text)
    *   [How to use few shot examples in chat models](/v0.2/docs/how_to/few_shot_examples_chat)
    *   [How to cache model responses](/v0.2/docs/how_to/llm_caching)
    *   [How to cache chat model responses](/v0.2/docs/how_to/chat_model_caching)
    *   [How to create a custom LLM class](/v0.2/docs/how_to/custom_llm)
    *   [How to use few shot examples](/v0.2/docs/how_to/few_shot_examples)
    *   [How to use output parsers to parse an LLM response into structured format](/v0.2/docs/how_to/output_parser_structured)
    *   [How to return structured data from a model](/v0.2/docs/how_to/structured_output)
    *   [How to add ad-hoc tool calling capability to LLMs and Chat Models](/v0.2/docs/how_to/tools_prompting)
    *   [How to create a custom chat model class](/v0.2/docs/how_to/custom_chat)
    *   [How to do per-user retrieval](/v0.2/docs/how_to/qa_per_user)
    *   [How to track token usage](/v0.2/docs/how_to/chat_token_usage_tracking)
    *   [How to track token usage](/v0.2/docs/how_to/llm_token_usage_tracking)
    *   [How to pass through arguments from one step to the next](/v0.2/docs/how_to/passthrough)
    *   [How to compose prompts together](/v0.2/docs/how_to/prompts_composition)
    *   [How to use legacy LangChain Agents (AgentExecutor)](/v0.2/docs/how_to/agent_executor)
    *   [How to add values to a chain's state](/v0.2/docs/how_to/assign)
    *   [How to attach runtime arguments to a Runnable](/v0.2/docs/how_to/binding)
    *   [How to cache embedding results](/v0.2/docs/how_to/caching_embeddings)
    *   [How to split by character](/v0.2/docs/how_to/character_text_splitter)
    *   [How to manage memory](/v0.2/docs/how_to/chatbots_memory)
    *   [How to do retrieval](/v0.2/docs/how_to/chatbots_retrieval)
    *   [How to use tools](/v0.2/docs/how_to/chatbots_tools)
    *   [How to split code](/v0.2/docs/how_to/code_splitter)
    *   [How to do retrieval with contextual compression](/v0.2/docs/how_to/contextual_compression)
    *   [How to write a custom retriever class](/v0.2/docs/how_to/custom_retriever)
    *   [How to create custom Tools](/v0.2/docs/how_to/custom_tools)
    *   [How to debug your LLM apps](/v0.2/docs/how_to/debugging)
    *   [How to load CSV data](/v0.2/docs/how_to/document_loader_csv)
    *   [How to write a custom document loader](/v0.2/docs/how_to/document_loader_custom)
    *   [How to load data from a directory](/v0.2/docs/how_to/document_loader_directory)
    *   [How to load PDF files](/v0.2/docs/how_to/document_loader_pdf)
    *   [How to load JSON data](/v0.2/docs/how_to/document_loaders_json)
    *   [How to select examples by length](/v0.2/docs/how_to/example_selectors_length_based)
    *   [How to select examples by similarity](/v0.2/docs/how_to/example_selectors_similarity)
    *   [How to use reference examples](/v0.2/docs/how_to/extraction_examples)
    *   [How to handle long text](/v0.2/docs/how_to/extraction_long_text)
    *   [How to do extraction without using function calling](/v0.2/docs/how_to/extraction_parse)
    *   [Fallbacks](/v0.2/docs/how_to/fallbacks)
    *   [Few Shot Prompt Templates](/v0.2/docs/how_to/few_shot)
    *   [How to run custom functions](/v0.2/docs/how_to/functions)
    *   [How to construct knowledge graphs](/v0.2/docs/how_to/graph_constructing)
    *   [How to map values to a database](/v0.2/docs/how_to/graph_mapping)
    *   [How to improve results with prompting](/v0.2/docs/how_to/graph_prompting)
    *   [How to add a semantic layer over the database](/v0.2/docs/how_to/graph_semantic)
    *   [How to reindex data to keep your vectorstore in-sync with the underlying data source](/v0.2/docs/how_to/indexing)
    *   [How to get log probabilities](/v0.2/docs/how_to/logprobs)
    *   [How to add message history](/v0.2/docs/how_to/message_history)
    *   [How to generate multiple embeddings per document](/v0.2/docs/how_to/multi_vector)
    *   [How to generate multiple queries to retrieve data for](/v0.2/docs/how_to/multiple_queries)
    *   [How to parse JSON output](/v0.2/docs/how_to/output_parser_json)
    *   [How to retry when output parsing errors occur](/v0.2/docs/how_to/output_parser_retry)
    *   [How to parse XML output](/v0.2/docs/how_to/output_parser_xml)
    *   [How to invoke runnables in parallel](/v0.2/docs/how_to/parallel)
    *   [How to retrieve the whole document for a chunk](/v0.2/docs/how_to/parent_document_retriever)
    *   [How to partially format prompt templates](/v0.2/docs/how_to/prompts_partial)
    *   [How to add chat history to a question-answering chain](/v0.2/docs/how_to/qa_chat_history_how_to)
    *   [How to return citations](/v0.2/docs/how_to/qa_citations)
    *   [How to return sources](/v0.2/docs/how_to/qa_sources)
    *   [How to stream from a question-answering chain](/v0.2/docs/how_to/qa_streaming)
    *   [How to construct filters](/v0.2/docs/how_to/query_constructing_filters)
    *   [How to add examples to the prompt](/v0.2/docs/how_to/query_few_shot)
    *   [How to deal with high cardinality categorical variables](/v0.2/docs/how_to/query_high_cardinality)
    *   [How to handle multiple queries](/v0.2/docs/how_to/query_multiple_queries)
    *   [How to handle multiple retrievers](/v0.2/docs/how_to/query_multiple_retrievers)
    *   [How to handle cases where no queries are generated](/v0.2/docs/how_to/query_no_queries)
    *   [How to recursively split text by characters](/v0.2/docs/how_to/recursive_text_splitter)
    *   [How to reduce retrieval latency](/v0.2/docs/how_to/reduce_retrieval_latency)
    *   [How to route execution within a chain](/v0.2/docs/how_to/routing)
    *   [How to chain runnables](/v0.2/docs/how_to/sequence)
    *   [How to split text by tokens](/v0.2/docs/how_to/split_by_token)
    *   [How to deal with large databases](/v0.2/docs/how_to/sql_large_db)
    *   [How to use prompting to improve results](/v0.2/docs/how_to/sql_prompting)
    *   [How to do query validation](/v0.2/docs/how_to/sql_query_checking)
    *   [How to stream](/v0.2/docs/how_to/streaming)
    *   [How to create a time-weighted retriever](/v0.2/docs/how_to/time_weighted_vectorstore)
    *   [How to use a chat model to call tools](/v0.2/docs/how_to/tool_calling)
    *   [How to call tools with multi-modal data](/v0.2/docs/how_to/tool_calls_multi_modal)
    *   [How to use LangChain tools](/v0.2/docs/how_to/tools_builtin)
    *   [How use a vector store to retrieve data](/v0.2/docs/how_to/vectorstore_retriever)
    *   [How to create and query vector stores](/v0.2/docs/how_to/vectorstores)
*   [Conceptual guide](/v0.2/docs/concepts)
*   Ecosystem
    
    *   [🦜🛠️ LangSmith](/v0.2/docs/langsmith/)
        
    *   [🦜🕸️LangGraph.js](/v0.2/docs/langgraph)
*   Versions
    
    *   [Overview](/v0.2/docs/versions/overview)
    *   [v0.2](/v0.2/docs/versions/v0_2)
    *   [Release Policy](/v0.2/docs/versions/release_policy)
    *   [Packages](/v0.2/docs/versions/packages)
*   [Security](/v0.2/docs/security)

*   [](/v0.2/)
*   [Tutorials](/v0.2/docs/tutorials/)
*   Build a Query Analysis System

On this page

Build a Query Analysis System
=============================

This page will show how to use query analysis in a basic end-to-end example. This will cover creating a simple search engine, showing a failure mode that occurs when passing a raw user question to that search, and then an example of how query analysis can help address that issue. There are MANY different query analysis techniques and this end-to-end example will not show all of them.

For the purpose of this example, we will do retrieval over the LangChain YouTube videos.

Setup[​](#setup "Direct link to Setup")
---------------------------------------

#### Install dependencies[​](#install-dependencies "Direct link to Install dependencies")

tip

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

*   npm
*   yarn
*   pnpm

    npm i langchain @langchain/community @langchain/openai youtubei.js chromadb youtube-transcript

    yarn add langchain @langchain/community @langchain/openai youtubei.js chromadb youtube-transcript

    pnpm add langchain @langchain/community @langchain/openai youtubei.js chromadb youtube-transcript

#### Set environment variables[​](#set-environment-variables "Direct link to Set environment variables")

We’ll use OpenAI in this example:

    OPENAI_API_KEY=your-api-key# Optional, use LangSmith for best-in-class observabilityLANGSMITH_API_KEY=your-api-keyLANGCHAIN_TRACING_V2=true

### Load documents[​](#load-documents "Direct link to Load documents")

We can use the `YouTubeLoader` to load transcripts of a few LangChain videos:

    import { DocumentInterface } from "@langchain/core/documents";import { YoutubeLoader } from "langchain/document_loaders/web/youtube";import { getYear } from "date-fns";const urls = [  "https://www.youtube.com/watch?v=HAn9vnJy6S4",  "https://www.youtube.com/watch?v=dA1cHGACXCo",  "https://www.youtube.com/watch?v=ZcEMLz27sL4",  "https://www.youtube.com/watch?v=hvAPnpSfSGo",  "https://www.youtube.com/watch?v=EhlPDL4QrWY",  "https://www.youtube.com/watch?v=mmBo8nlu2j0",  "https://www.youtube.com/watch?v=rQdibOsL1ps",  "https://www.youtube.com/watch?v=28lC4fqukoc",  "https://www.youtube.com/watch?v=es-9MgxB-uc",  "https://www.youtube.com/watch?v=wLRHwKuKvOE",  "https://www.youtube.com/watch?v=ObIltMaRJvY",  "https://www.youtube.com/watch?v=DjuXACWYkkU",  "https://www.youtube.com/watch?v=o7C9ld6Ln-M",];let docs: Array<DocumentInterface> = [];for await (const url of urls) {  const doc = await YoutubeLoader.createFromUrl(url, {    language: "en",    addVideoInfo: true,  }).load();  docs = docs.concat(doc);}console.log(docs.length);/*13 */// Add some additional metadata: what year the video was published// The JS API does not provide publish date, so we can use a// hardcoded array with the dates instead.const dates = [  new Date("Jan 31, 2024"),  new Date("Jan 26, 2024"),  new Date("Jan 24, 2024"),  new Date("Jan 23, 2024"),  new Date("Jan 16, 2024"),  new Date("Jan 5, 2024"),  new Date("Jan 2, 2024"),  new Date("Dec 20, 2023"),  new Date("Dec 19, 2023"),  new Date("Nov 27, 2023"),  new Date("Nov 22, 2023"),  new Date("Nov 16, 2023"),  new Date("Nov 2, 2023"),];docs.forEach((doc, idx) => {  // eslint-disable-next-line no-param-reassign  doc.metadata.publish_year = getYear(dates[idx]);  // eslint-disable-next-line no-param-reassign  doc.metadata.publish_date = dates[idx];});// Here are the titles of the videos we've loaded:console.log(docs.map((doc) => doc.metadata.title));/*[  'OpenGPTs',  'Building a web RAG chatbot: using LangChain, Exa (prev. Metaphor), LangSmith, and Hosted Langserve',  'Streaming Events: Introducing a new `stream_events` method',  'LangGraph: Multi-Agent Workflows',  'Build and Deploy a RAG app with Pinecone Serverless',  'Auto-Prompt Builder (with Hosted LangServe)',  'Build a Full Stack RAG App With TypeScript',  'Getting Started with Multi-Modal LLMs',  'SQL Research Assistant',  'Skeleton-of-Thought: Building a New Template from Scratch',  'Benchmarking RAG over LangChain Docs',  'Building a Research Assistant from Scratch',  'LangServe and LangChain Templates Webinar']  */

#### API Reference:

*   [DocumentInterface](https://v02.api.js.langchain.com/interfaces/langchain_core_documents.DocumentInterface.html) from `@langchain/core/documents`
*   [YoutubeLoader](https://v02.api.js.langchain.com/classes/langchain_document_loaders_web_youtube.YoutubeLoader.html) from `langchain/document_loaders/web/youtube`

Here’s the metadata associated with each video.

We can see that each document also has a title, view count, publication date, and length:

    import { getDocs } from "./docs.js";const docs = await getDocs();console.log(docs[0].metadata);/**{  source: 'HAn9vnJy6S4',  description: 'OpenGPTs is an open-source platform aimed at recreating an experience like the GPT Store - but with any model, any tools, and that you can self-host.\n' +    '\n' +    'This video covers both how to use it as well as how to build it.\n' +    '\n' +    'GitHub: https://github.com/langchain-ai/opengpts',  title: 'OpenGPTs',  view_count: 7262,  author: 'LangChain'} */// And here's a sample from a document's contents:console.log(docs[0].pageContent.slice(0, 500));/*hello today I want to talk about open gpts open gpts is a project that we built here at linkchain uh that replicates the GPT store in a few ways so it creates uh end user-facing friendly interface to create different Bots and these Bots can have access to different tools and they can uh be given files to retrieve things over and basically it's a way to create a variety of bots and expose the configuration of these Bots to end users it's all open source um it can be used with open AI it can be us */

#### API Reference:

### Indexing documents[​](#indexing-documents "Direct link to Indexing documents")

Whenever we perform retrieval we need to create an index of documents that we can query. We’ll use a vector store to index our documents, and we’ll chunk them first to make our retrievals more concise and precise:

    import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";import { OpenAIEmbeddings } from "@langchain/openai";import { Chroma } from "@langchain/community/vectorstores/chroma";import { getDocs } from "./docs.js";const docs = await getDocs();const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 2000 });const chunkedDocs = await textSplitter.splitDocuments(docs);const embeddings = new OpenAIEmbeddings({  model: "text-embedding-3-small",});const vectorStore = await Chroma.fromDocuments(chunkedDocs, embeddings, {  collectionName: "yt-videos",});

#### API Reference:

*   [RecursiveCharacterTextSplitter](https://v02.api.js.langchain.com/classes/langchain_textsplitters.RecursiveCharacterTextSplitter.html) from `@langchain/textsplitters`
*   [OpenAIEmbeddings](https://v02.api.js.langchain.com/classes/langchain_openai.OpenAIEmbeddings.html) from `@langchain/openai`
*   [Chroma](https://v02.api.js.langchain.com/classes/langchain_community_vectorstores_chroma.Chroma.html) from `@langchain/community/vectorstores/chroma`

Then later, you can retrieve the index without having to re-query and embed:

    import "chromadb";import { OpenAIEmbeddings } from "@langchain/openai";import { Chroma } from "@langchain/community/vectorstores/chroma";const embeddings = new OpenAIEmbeddings({  model: "text-embedding-3-small",});const vectorStore = await Chroma.fromExistingCollection(embeddings, {  collectionName: "yt-videos",});

    [Module: null prototype] {  AdminClient: [class AdminClient],  ChromaClient: [class ChromaClient],  CloudClient: [class CloudClient extends ChromaClient],  CohereEmbeddingFunction: [class CohereEmbeddingFunction],  Collection: [class Collection],  DefaultEmbeddingFunction: [class _DefaultEmbeddingFunction],  GoogleGenerativeAiEmbeddingFunction: [class _GoogleGenerativeAiEmbeddingFunction],  HuggingFaceEmbeddingServerFunction: [class HuggingFaceEmbeddingServerFunction],  IncludeEnum: {    Documents: "documents",    Embeddings: "embeddings",    Metadatas: "metadatas",    Distances: "distances"  },  JinaEmbeddingFunction: [class JinaEmbeddingFunction],  OpenAIEmbeddingFunction: [class _OpenAIEmbeddingFunction],  TransformersEmbeddingFunction: [class _TransformersEmbeddingFunction]}

Retrieval without query analysis[​](#retrieval-without-query-analysis "Direct link to Retrieval without query analysis")
------------------------------------------------------------------------------------------------------------------------

We can perform similarity search on a user question directly to find chunks relevant to the question:

    const searchResults = await vectorStore.similaritySearch(  "how do I build a RAG agent");console.log(searchResults[0].metadata.title);console.log(searchResults[0].pageContent.slice(0, 500));

    OpenGPTshardcoded that it will always do a retrieval step here the assistant decides whether to do a retrieval step or not sometimes this is good sometimes this is bad sometimes it you don't need to do a retrieval step when I said hi it didn't need to call it tool um but other times you know the the llm might mess up and not realize that it needs to do a retrieval step and so the rag bot will always do a retrieval step so it's more focused there because this is also a simpler architecture so it's always

This works pretty okay! Our first result is somewhat relevant to the question.

What if we wanted to search for results from a specific time period?

    const searchResults = await vectorStore.similaritySearch(  "videos on RAG published in 2023");console.log(searchResults[0].metadata.title);console.log(searchResults[0].metadata.publish_year);console.log(searchResults[0].pageContent.slice(0, 500));

    OpenGPTs2024hardcoded that it will always do a retrieval step here the assistant decides whether to do a retrieval step or not sometimes this is good sometimes this is bad sometimes it you don't need to do a retrieval step when I said hi it didn't need to call it tool um but other times you know the the llm might mess up and not realize that it needs to do a retrieval step and so the rag bot will always do a retrieval step so it's more focused there because this is also a simpler architecture so it's always

Our first result is from 2024, and not very relevant to the input. Since we’re just searching against document contents, there’s no way for the results to be filtered on any document attributes.

This is just one failure mode that can arise. Let’s now take a look at how a basic form of query analysis can fix it!

Query analysis[​](#query-analysis "Direct link to Query analysis")
------------------------------------------------------------------

To handle these failure modes we’ll do some query structuring. This will involve defining a **query schema** that contains some date filters and use a function-calling model to convert a user question into a structured queries.

### Query schema[​](#query-schema "Direct link to Query schema")

In this case we’ll have explicit min and max attributes for publication date so that it can be filtered on.

    import { z } from "zod";const searchSchema = z  .object({    query: z      .string()      .describe("Similarity search query applied to video transcripts."),    publish_year: z.number().optional().describe("Year of video publication."),  })  .describe(    "Search over a database of tutorial videos about a software library."  );

### Query generation[​](#query-generation "Direct link to Query generation")

To convert user questions to structured queries we’ll make use of OpenAI’s function-calling API. Specifically we’ll use the new [ChatModel.withStructuredOutput()](https://v02.api.js.langchain.com/classes/langchain_core_language_models_base.BaseLanguageModel.html#withStructuredOutput) constructor to handle passing the schema to the model and parsing the output.

    import { ChatPromptTemplate } from "@langchain/core/prompts";import { ChatOpenAI } from "@langchain/openai";import {  RunnablePassthrough,  RunnableSequence,} from "@langchain/core/runnables";const system = `You are an expert at converting user questions into database queries.You have access to a database of tutorial videos about a software library for building LLM-powered applications.Given a question, return a list of database queries optimized to retrieve the most relevant results.If there are acronyms or words you are not familiar with, do not try to rephrase them.`;const prompt = ChatPromptTemplate.fromMessages([  ["system", system],  ["human", "{question}"],]);const llm = new ChatOpenAI({  model: "gpt-3.5-turbo-0125",  temperature: 0,});const structuredLLM = llm.withStructuredOutput(searchSchema, {  name: "search",});const queryAnalyzer = RunnableSequence.from([  {    question: new RunnablePassthrough(),  },  prompt,  structuredLLM,]);

Let’s see what queries our analyzer generates for the questions we searched earlier:

    console.log(await queryAnalyzer.invoke("How do I build a rag agent"));

    { query: "build a rag agent" }

    console.log(await queryAnalyzer.invoke("videos on RAG published in 2023"));

    { query: "RAG", publish_year: 2023 }

Retrieval with query analysis[​](#retrieval-with-query-analysis "Direct link to Retrieval with query analysis")
---------------------------------------------------------------------------------------------------------------

Our query analysis looks pretty good; now let’s try using our generated queries to actually perform retrieval.

**Note:** in our example, we specified `tool_choice: "Search"`. This will force the LLM to call one - and only one - function, meaning that we will always have one optimized query to look up. Note that this is not always the case - see other guides for how to deal with situations when no - or multiple - optimized queries are returned.

    import { DocumentInterface } from "@langchain/core/documents";const retrieval = async (input: {  query: string;  publish_year?: number;}): Promise<DocumentInterface[]> => {  let _filter: Record<string, any> = {};  if (input.publish_year) {    // This syntax is specific to Chroma    // the vector database we are using.    _filter = {      publish_year: {        $eq: input.publish_year,      },    };  }  return vectorStore.similaritySearch(input.query, undefined, _filter);};

    import { RunnableLambda } from "@langchain/core/runnables";const retrievalChain = queryAnalyzer.pipe(  new RunnableLambda({    func: async (input) =>      retrieval(input as unknown as { query: string; publish_year?: number }),  }));

We can now run this chain on the problematic input from before, and see that it yields only results from that year!

    const results = await retrievalChain.invoke("RAG tutorial published in 2023");

    console.log(  results.map((doc) => ({    title: doc.metadata.title,    year: doc.metadata.publish_date,  })));

    [  {    title: "Getting Started with Multi-Modal LLMs",    year: "2023-12-20T08:00:00.000Z"  },  {    title: "LangServe and LangChain Templates Webinar",    year: "2023-11-02T07:00:00.000Z"  },  {    title: "Getting Started with Multi-Modal LLMs",    year: "2023-12-20T08:00:00.000Z"  },  {    title: "Building a Research Assistant from Scratch",    year: "2023-11-16T08:00:00.000Z"  }]

* * *

#### Was this page helpful?

  

#### You can leave detailed feedback [on GitHub](https://github.com/langchain-ai/langchainjs/issues/new?assignees=&labels=03+-+Documentation&projects=&template=documentation.yml&title=DOC%3A+%3CPlease+write+a+comprehensive+title+after+the+%27DOC%3A+%27+prefix%3E).

[

Previous

Build a Simple LLM Application

](/v0.2/docs/tutorials/llm_chain)[

Next

Build a Chatbot

](/v0.2/docs/tutorials/chatbot)

*   [Setup](#setup)
    *   [Load documents](#load-documents)
    *   [Indexing documents](#indexing-documents)
*   [Retrieval without query analysis](#retrieval-without-query-analysis)
*   [Query analysis](#query-analysis)
    *   [Query schema](#query-schema)
    *   [Query generation](#query-generation)
*   [Retrieval with query analysis](#retrieval-with-query-analysis)

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.