 Hi, this is Lance from Langchain. This is the 11th part of our Rag from Scratch video series focused on query construction. So we've previously talked through query translation, which is the process of taking a question and converting it or translating it into a question that's better optimized for retrieval. Then we talked about routing, which is the process of taking that question and routing it to the right source, it a given vector store graph db or sql db for example now we're going to talk about the process of query construction which is basically taking natural language and converting it into particular domain specific language for one of these sources now we're going to talk specifically about the process of going from natural language to metadata filters for vector stores. The problem statement is basically this. Let's imagine we had an index of langchain video transcripts. You might want to ask a question. Give me, you know, find me videos on chat langchain published after 2024, for example. published after 2024, for example. The process of query structuring basically converts this natural language question into a structured query that can be applied to the metadata filters on your vector store. So most vector stores will have some kind of metadata filters that can do kind of structured querying on top of the chunks that are indexed. So for example, this type of query will retrieve all chunks that talk about the topic of chat langchain published after the date 2024. That's kind of the problem statement. And to do this, we're going to use function calling. In this case, you can use, for example, OpenAI or other providers to do that. And what we're going to do is, at a high level, take the metadata fields that are present in our vector store and provide them to the model as kind of information. And the model then can take those and produce queries that adhere to the schema provided. And then we can parse those out to a structured object, like a Pydentic object, which can then be be used in search so that's kind of problem statement and let's actually walk through code so here's our notebook which we've kind of gone through previously and I'll just show you as an example let's take an example YouTube video and let's look at the metadata that you get with the transcript so you can see you get stuff like description, URL, yeah, publish date, length, things like that. Now, let's say we had an index that had basically had a number of different metadata fields and filters that allowed us to do range filtering on like view count, publication date, the video length or unstructured search on contents and title. So those are kind of like the, imagine we had an index that had those kind of filters available to us. What we can do is capture that information about the available filters in an object. So we're calling that this tutorial search object kind of encapsulates that information about the available searches that we can do and so we basically enumerated here content search and title search or semantic searches that can be done over those fields and then these filters then are various types of structured searches we can do on like the length the view count and so forth and so we can just kind of build that object now we can set this up really easily with a basic simple prompt that says, you know, you're an expert, can bring natural language into database queries, you have access to the database tutorial videos, given a question, return a database query, optimize retrieval. So that's kind of it. Now, here's the key point, though. When you call this LLM with structured output, you're binding this pydantic object, which contains all the information about our index, to the LLM, which is exactly what we talked about previously. It's really this process right here. You're taking this object, you're converting it into a function schema, for example, OpenAI, you're binding that to your model, and then you're going to be able to get structured object object out versus a JSON string from a natural language question, which can then be parsed into a pydantic object, which you get out. So that's really the flow, and it's taking advantage of function calling, as we said. So if we go back down, we set up our query analyzer chain right here. Now let's try to run that just on a purely semantic input. So rag from scratch. Let's run that. And you can see this just does like a content search and a title search. That's exactly what we would expect. Now if we pass a question that includes like a date filter, let's just see if that would work. And there we go. So you kind of still get that semantic search, but you also get search over, for example, published date, earliest and latest published date, kind of as you would expect. Let's try another one here. So videos focus on the topic of chat lagging chain. They're published before 2024. This is just kind of rewrite of this question in slightly different way using a different date filter. And then you can see we can get content search, title search, and then we can get kind of a date search. So this is a very general strategy that can be applied kind of broadly to different kinds of querying you wanna do. It's really the process of going from an unstructured input to a structured query object out, following an arbitrary schema that you provide. And so as noted, really this whole thing we created here, this tutorial search is based upon the specifics of our vector store of interest. And if you wanna learn more about this, I linked to some documentation here that talks a lot about different types of integrations we have with different vector store providers to do exactly this. So it's a very useful trick. It allows you to do kind of query metadata filtering on the fly from a natural language question. It's a very convenient trick that works with many different vector DBs. So encourage you to play with it. Thanks.
RAG from scratch: Part 11 (Query Structuring)
Our RAG From Scratch video series walks through impt RAG concepts in short / focused videos w/ code.  

Problem: We interact w/ databases using domain-specific languages (e.g., SQL, Cypher for Relational and Graph DBs). And, many vectorstores have metadata that can allow for structured queries to filter chunks. But RAG systems ingest questions in natural language.

Idea: A great deal of work has focused on query structuring, the process of text-to-DSL where DSL is a domain specific language required to interact with a given database. This converts user questions into structured queries. Below are links that dive into text-to-SQL/Cypher, and the below video overviews query structuring for vectorstores using function calling.

Code: 
https://github.com/langchain-ai/rag-from-scratch/blob/main/rag_from_scratch_10_and_11.ipynb

References:
1/ Blog with links to various tutorials and templates:
https://blog.langchain.dev/query-construction/
2/ Deep dive on graphDBs (c/o @neo4j):
https://blog.langchain.dev/enhancing-rag-based-applications-accuracy-by-constructing-and-leveraging-knowledge-graphs/
3/ Query structuring docs:
https://python.langchain.com/docs/use_cases/query_analysis/techniques/structuring
4/ Self-query retriever docs:
https://python.langchain.com/docs/modules/data_connection/retrievers/self_query