Constructing Search Queries

How to construct search queries in Zep’s Collection and Memory search

You are viewing the Zep Open Source v0.x documentation. This version is no longer supported, and documentation is provided as a reference only.

The current documentation for Zep Community Edition is available here.

Introduction

Zep’s Collection and Memory search supports semantic similarity search and similarity search re-ranked by Maximal Marginal Relevance. Both of these search types can be filtered by JSONPath-based metadata filters. Memory search also supports querying by message or summary creation date.

Simple, Text-based Semantic Queries

The simplest form of search query is a text-based semantic similarity query. No metadata filter is required, and the query is simply a string of text. Zep will convert the query into an embedding and find semantically similar documents, chat history summaries, or chat messages.

Below is an example search against a chat session using only search text.

1const searchPayload = new MemorySearchPayload({
2 metadata: {},
3 text: searchText,
4});
5const searchResults = await zepClient.memory.searchMemory(
6 sessionID,
7 searchPayload
8);

Read more about chat message history search.

Maximal Marginal Relevance Re-Ranking

Zep supports re-ranking search results using Maximal Marginal Relevance (MMR) over both chat history memory and document collections.

Maximal Marginal Relevance (MMR) helps balance relevance and diversity in results returned during information retrieval, which is useful in vector similarity searches for Retrieval-Augmented Generation (RAG) type applications.

A similarity search may return many highly ranked results that are very similar to each other. Since each subsequent result doesn’t add much new information to a prompt, adding these may not be very useful.

MMR helps to reduce redundancy in the results by re-ranking the results to promote diversity, with very similar results downranked in favor of different, but still relevant, results.

How Zep’s MMR Re-Ranking Works

When you run a search re-ranked by MMR, Zep retrieves double the number of results, K, you requested. The entire resultset is then reranked using MMR, and the top K results are returned.

If you request fewer than 10 results, Zep will return 10 results, but still return to you the top K results you requested.

Zep’s MMR algorithm is SIMD-hardware accelerated on amd64 architecture CPUs, ensuring that MMR adds little overhead to your search.

Constructing an MMR Re-Ranked Search Query

Search over Chat History

Python
1from zep_python import (
2 MemorySearchPayload,
3 ZepClient,
4)
5search_payload = MemorySearchPayload(
6 text=query,
7 search_scope="summary", # This could be messages or summary
8 search_type="mmr",
9 mmr_lambda=0.5, # tune diversity vs relevance
10)
11
12search_results = client.memory.search_memory(
13 session_id, search_payload, limit=3
14)
TypeScript
1import { MemorySearchPayload, ZepClient } from "@getzep/zep-js";
2
3const searchPayload = new MemorySearchPayload({
4 text: searchText,
5 search_scope: "summary", // This could be messages or summary
6 search_type: "mmr",
7 mmr_lambda: 0.5, // tune diversity vs relevance
8});
9const searchResults = await client.memory.searchMemory(
10 sessionID,
11 searchPayload,
12 3
13);

Search over Document Collections

Python
1# This assumes you've created a collection and added documents to it
2docs = collection.search(
3 text=query,
4 search_type="mmr",
5 mmr_lambda=0.5,
6 limit=3,
7)
TypeScript
1const mmrSearchQuery: ISearchQuery = {
2 text: query,
3 searchType: "mmr",
4 mmrLambda: 0.5,
5};
6
7// This assumes you've created a collection and added documents to it
8const mmrSearchResults = await collection.search(mmrSearchQuery, 3);

LangChain and MMR

Search over Chat History

Zep’s LangChain ZepRetriever supports MMR re-ranking of search results over historical chat summaries or messages.

LangChain.js
1import { ZepRetriever } from "langchain/retrievers/zep";
2
3const mmrRetriever = new ZepRetriever({
4 url: process.env.ZEP_URL || "http://localhost:8000",
5 sessionId: sessionID,
6 topK: 3,
7 searchType: "mmr",
8 searchScope: "summary",
9 mmrLambda: 0.5,
10});
11const mmrDocs = await mmrRetriever.getRelevantDocuments(query);
LangChain
1from langchain.retrievers import ZepRetriever
2from langchain.retrievers.zep import SearchType, SearchScope
3
4zep_retriever = ZepRetriever(
5 session_id=session_id,
6 url=ZEP_API_URL,
7 api_key=zep_api_key,
8 top_k=3,
9 search_scope=SearchScope.summary,
10 search_type=SearchType.mmr,
11 mmr_lambda=0.5,
12)
13
14docs = await zep_retriever.aget_relevant_documents("Who wrote Parable of the Sower?")

Search over Document Collections

MMR re-ranking is also supported for document collections via the ZepVectorStore. Currently, we utilize LangChain’s MMR implementation for this, but we plan to add a native implementation in the future.

Filtering using Metadata

Zep supports filtering search queries by metadata. Metadata filters are JSONPath queries augmented by a simple boolean logic overlay.

JSONPath queries allow for building sophisticated filters that match on elements at any level of the JSON document. The boolean logic overlay allows for combining multiple JSONPath queries using and and or operators.

Useful resources for building and testing JSONPath queries

Constructing a JSONPath Query Filter

The simplest form of a metadata filter looks as follows:

1{
2 "where": { "jsonpath": "$[*] ? (@.foo == \"bar\")" }
3}

If a metadata field foo is equal to the string "bar", then the document or message will be returned in the search results.

Executing the above query against a chat session looks as follows:

1const searchPayload = new MemorySearchPayload({
2 text: "Is Lauren Olamina a character in a book",
3 metadata: {
4 where: { jsonpath: '$[*] ? (@.author == "Octavia Butler")' },
5 },
6});
7
8const searchResults = await zepClient.memory.searchMemory(
9 sessionID,
10 searchPayload
11);

Or, in the case of querying the MemoryStore using Python:

1search_payload = MemorySearchPayload(
2 text="Is Lauren Olamina a character in a book",
3 metadata={
4 "where": {
5 "jsonpath": '$[*] ? (@.author == "Octavia Butler")'
6 }
7 }
8)
9
10search_results = client.memory.search_memory(session_id, search_payload)

Combining multiple JSONPath filters using boolean logic

Multiple JSONPath queries can be combined using boolean logic. The following example will return documents or messages where the author field is equal to "Octavia Butler" and the title field is equal to "Parable of the Sower".

1{
2 "where": {
3 "and": [
4 { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" },
5 { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" }
6 ]
7 }
8}

Similarly, the following example will return documents or messages where the author field is equal to "Octavia Butler" or the title field is equal to "Parable of the Sower".

1{
2 "where": {
3 "or": [
4 { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" },
5 { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" }
6 ]
7 }
8}

Filter logic can be combined to create arbitrarily complex filters. For example, the following filter will return documents or messages where:

  • the author field is equal to ("Octavia Butler" and the title field is equal to "Parable of the Sower")
  • or the title field is equal to "Parable of the Talents".
1{
2 "where": {
3 "or": [
4 {
5 "and": [
6 { "jsonpath": "$[*] ? (@.author == \"Octavia Butler\")" },
7 { "jsonpath": "$[*] ? (@.title == \"Parable of the Sower\")" }
8 ]
9 },
10 { "jsonpath": "$[*] ? (@.title == \"Parable of the Talents\")" }
11 ]
12 }
13}

Querying by Message Creation Date

Memory search supports querying by message creation date. The following example will return documents or messages created between June 1, 2023 and June 31, 2023.

Datetime strings must be in ISO 8601 format.

1{
2 "start_date": "2023-06-01",
3 "end_date": "2023-06-31"
4}