Back to Llama Index

Optimizing for relevance using MongoDB and LlamaIndex

docs/examples/cookbooks/mongodb_retrieval_strategies.ipynb

0.14.217.7 KB
Original Source

Optimizing for relevance using MongoDB and LlamaIndex

In this notebook, we will explore and tune different retrieval options in MongoDB's LlamaIndex integration to get the most relevant results.

Step 1: Install libraries

  • pymongo: Python package to interact with MongoDB databases and collections
<p> - **llama-index**: Python package for the LlamaIndex LLM framework <p> - **llama-index-llms-openai**: Python package to use OpenAI models via their LlamaIndex integration <p> - **llama-index-vector-stores-mongodb**: Python package for MongoDB’s LlamaIndex integration
python
!pip install -qU pymongo llama-index llama-index-llms-openai llama-index-vector-stores-mongodb

Step 2: Setup prerequisites

  • Set the MongoDB connection string: Follow the steps here to get the connection string from the Atlas UI.

  • Set the OpenAI API key: Steps to obtain an API key as here

python
import os
import getpass
from pymongo import MongoClient, AsyncMongoClient
python
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")
python
MONGODB_URI = getpass.getpass("Enter your MongoDB URI: ")
mongodb_client = MongoClient(
    MONGODB_URI, appname="devrel.content.retrieval_strategies_llamaindex"
)
async_mongodb_client = AsyncMongoClient(
    MONGODB_URI, appname="devrel.content.retrieval_strategies_llamaindex"
)

Step 3: Load and process the dataset

python
from datasets import load_dataset
import pandas as pd
from llama_index.core import Document
python
data = load_dataset("MongoDB/embedded_movies", split="train")
data = pd.DataFrame(data)
python
data.head()
python
# Fill Nones in the dataframe
data = data.fillna(
    {"genres": "[]", "languages": "[]", "cast": "[]", "imdb": "{}"}
)
python
documents = []

for _, row in data.iterrows():
    # Extract required fields
    title = row["title"]
    rating = row["imdb"].get("rating", 0)
    languages = row["languages"]
    cast = row["cast"]
    genres = row["genres"]
    # Create the metadata attribute
    metadata = {"title": title, "rating": rating, "languages": languages}
    # Create the text attribute
    text = f"Title: {title}\nPlot: {row['fullplot']}\nCast: {', '.join(item for item in cast)}\nGenres: {', '.join(item for item in  genres)}\nLanguages: {', '.join(item for item in languages)}\nRating: {rating}"
    documents.append(Document(text=text, metadata=metadata))
python
print(documents[0].text)
python
print(documents[0].metadata)

Step 4: Create MongoDB Atlas vector store

python
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.mongodb import MongoDBAtlasVectorSearch
from llama_index.core.settings import Settings
from llama_index.core import VectorStoreIndex, StorageContext
from pymongo.operations import SearchIndexModel
from pymongo.errors import OperationFailure
python
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
python
VS_INDEX_NAME = "vector_index"
FTS_INDEX_NAME = "fts_index"
DB_NAME = "llamaindex"
COLLECTION_NAME = "hybrid_search"
collection = mongodb_client[DB_NAME][COLLECTION_NAME]
python
vector_store = MongoDBAtlasVectorSearch(
    mongodb_client=mongodb_client,
    async_mongodb_client=async_mongodb_client,
    db_name=DB_NAME,
    collection_name=COLLECTION_NAME,
    vector_index_name=VS_INDEX_NAME,
    fulltext_index_name=FTS_INDEX_NAME,
    embedding_key="embedding",
    text_key="text",
)
# If the collection has documents with embeddings already, create the vector store index from the vector store
if collection.count_documents({}) > 0:
    vector_store_index = VectorStoreIndex.from_vector_store(vector_store)
# If the collection does not have documents, embed and ingest them into the vector store
else:
    vector_store_context = StorageContext.from_defaults(
        vector_store=vector_store
    )
    vector_store_index = VectorStoreIndex.from_documents(
        documents, storage_context=vector_store_context, show_progress=True
    )

Step 5: Create Atlas Search indexes

python
vs_model = SearchIndexModel(
    definition={
        "fields": [
            {
                "type": "vector",
                "path": "embedding",
                "numDimensions": 1536,
                "similarity": "cosine",
            },
            {"type": "filter", "path": "metadata.rating"},
            {"type": "filter", "path": "metadata.language"},
        ]
    },
    name=VS_INDEX_NAME,
    type="vectorSearch",
)
python
fts_model = SearchIndexModel(
    definition={
        "mappings": {"dynamic": False, "fields": {"text": {"type": "string"}}}
    },
    name=FTS_INDEX_NAME,
    type="search",
)
python
for model in [vs_model, fts_model]:
    try:
        collection.create_search_index(model=model)
    except OperationFailure:
        print(
            f"Duplicate index found for model {model}. Skipping index creation."
        )

Step 6: Get movie recommendations

python
def get_recommendations(query: str, mode: str, **kwargs) -> None:
    """
    Get movie recommendations

    Args:
        query (str): User query
        mode (str): Retrieval mode. One of (default, text_search, hybrid)
    """
    query_engine = vector_store_index.as_query_engine(
        similarity_top_k=5, vector_store_query_mode=mode, **kwargs
    )
    response = query_engine.query(query)
    nodes = response.source_nodes
    for node in nodes:
        title = node.metadata["title"]
        rating = node.metadata["rating"]
        score = node.score
        print(f"Title: {title} | Rating: {rating} | Relevance Score: {score}")
python
get_recommendations(
    query="Action movies about humans fighting machines",
    mode="text_search",
)
python
get_recommendations(
    query="Action movies about humans fighting machines", mode="default"
)
python
# Vector and full-text search weighted equal by default
get_recommendations(
    query="Action movies about humans fighting machines", mode="hybrid"
)
python
# Higher alpha, vector search dominates
get_recommendations(
    query="Action movies about humans fighting machines",
    mode="hybrid",
    alpha=0.7,
)
python
# Lower alpha, full-text search dominates
get_recommendations(
    query="Action movies about humans fighting machines",
    mode="hybrid",
    alpha=0.3,
)
python
from llama_index.core.vector_stores import (
    MetadataFilter,
    MetadataFilters,
    FilterOperator,
    FilterCondition,
)
python
filters = MetadataFilters(
    filters=[
        MetadataFilter(
            key="metadata.rating", value=7, operator=FilterOperator.GT
        ),
        MetadataFilter(
            key="metadata.languages",
            value="English",
            operator=FilterOperator.EQ,
        ),
    ],
    condition=FilterCondition.AND,
)
python
get_recommendations(
    query="Action movies about humans fighting machines",
    mode="hybrid",
    alpha=0.7,
    filters=filters,
)