title: Vectorizers

title: HFTextVectorizer

title: `class HFTextVectorizer(model='sentence-transformers/all-mpnet-base-v2', dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The HFTextVectorizer class leverages Hugging Face’s Sentence Transformers
for generating vector embeddings from text input.

This vectorizer is particularly useful in scenarios where advanced natural language
processing and understanding are required, and ideal for running on your own
hardware without usage fees.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

Utilizing this vectorizer involves specifying a pre-trained model from
Hugging Face’s vast collection of Sentence Transformers. These models are
trained on a variety of datasets and tasks, ensuring versatility and
robust performance across different embedding needs.

Requirements:
: - The sentence-transformers library must be installed with pip.

[code example]

Initialize the Hugging Face text vectorizer.

* **Parameters:**
  * **model** (*str*) – The pre-trained model from Hugging Face’s Sentence
    Transformers to be used for embedding. Defaults to
    ‘sentence-transformers/all-mpnet-base-v2’.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **\*\*kwargs** – Additional parameters to pass to the SentenceTransformer
    constructor.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ImportError** – If the sentence-transformers library is not installed.
  * **ValueError** – If there is an error setting the embedding model dimensions.
  * **ValueError** – If an invalid dtype is provided.

#### `model_post_init(context, /)`

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

* **Parameters:**
  * **self** (*BaseModel*) – The BaseModel instance.
  * **context** (*Any*) – The context.
* **Return type:**
  None

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: OpenAITextVectorizer

title: `class OpenAITextVectorizer(model='text-embedding-ada-002', api_config=None, dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The OpenAITextVectorizer class utilizes OpenAI’s API to generate
embeddings for text data.

This vectorizer is designed to interact with OpenAI’s embeddings API,
requiring an API key for authentication. The key can be provided directly
in the api_config dictionary or through the OPENAI_API_KEY environment
variable. Users must obtain an API key from OpenAI’s website
([https://api.openai.com/](https://api.openai.com/)). Additionally, the openai python client must be
installed with pip install openai>=1.13.0.

The vectorizer supports both synchronous and asynchronous operations,
allowing for batch processing of texts and flexibility in handling
preprocessing tasks.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the OpenAI vectorizer.

* **Parameters:**
  * **model** (*str*) – Model to use for embedding. Defaults to
    ‘text-embedding-ada-002’.
  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the
    API key and any additional OpenAI API options. Defaults to None.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ImportError** – If the openai library is not installed.
  * **ValueError** – If the OpenAI API key is not provided.
  * **ValueError** – If an invalid dtype is provided.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: AzureOpenAITextVectorizer

title: `class AzureOpenAITextVectorizer(model='text-embedding-ada-002', api_config=None, dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The AzureOpenAITextVectorizer class utilizes AzureOpenAI’s API to generate
embeddings for text data.

This vectorizer is designed to interact with AzureOpenAI’s embeddings API,
requiring an API key, an AzureOpenAI deployment endpoint and API version.
These values can be provided directly in the api_config dictionary with
the parameters ‘azure_endpoint’, ‘api_version’ and ‘api_key’ or through the
environment variables ‘AZURE_OPENAI_ENDPOINT’, ‘OPENAI_API_VERSION’, and ‘AZURE_OPENAI_API_KEY’.
Users must obtain these values from the ‘Keys and Endpoints’ section in their Azure OpenAI service.
Additionally, the openai python client must be installed with pip install openai>=1.13.0.

The vectorizer supports both synchronous and asynchronous operations,
allowing for batch processing of texts and flexibility in handling
preprocessing tasks.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the AzureOpenAI vectorizer.

* **Parameters:**
  * **model** (*str*) – Deployment to use for embedding. Must be the
    ‘Deployment name’ not the ‘Model name’. Defaults to
    ‘text-embedding-ada-002’.
  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the
    API key, API version, Azure endpoint, and any other API options.
    Defaults to None.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ImportError** – If the openai library is not installed.
  * **ValueError** – If the AzureOpenAI API key, version, or endpoint are not provided.
  * **ValueError** – If an invalid dtype is provided.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: VertexAITextVectorizer

title: `class VertexAITextVectorizer(model='textembedding-gecko', api_config=None, dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The VertexAITextVectorizer uses Google’s VertexAI Palm 2 embedding model
API to create text embeddings.

This vectorizer is tailored for use in
environments where integration with Google Cloud Platform (GCP) services is
a key requirement.

Utilizing this vectorizer requires an active GCP project and location
(region), along with appropriate application credentials. These can be
provided through the api_config dictionary or set the GOOGLE_APPLICATION_CREDENTIALS
env var. Additionally, the vertexai python client must be
installed with pip install google-cloud-aiplatform>=1.26.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the VertexAI vectorizer.

* **Parameters:**
  * **model** (*str*) – Model to use for embedding. Defaults to
    ‘textembedding-gecko’.
  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the
    API config details. Defaults to None.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ImportError** – If the google-cloud-aiplatform library is not installed.
  * **ValueError** – If the API key is not provided.
  * **ValueError** – If an invalid dtype is provided.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: CohereTextVectorizer

title: `class CohereTextVectorizer(model='embed-english-v3.0', api_config=None, dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The CohereTextVectorizer class utilizes Cohere’s API to generate
embeddings for text data.

This vectorizer is designed to interact with Cohere’s /embed API,
requiring an API key for authentication. The key can be provided
directly in the api_config dictionary or through the COHERE_API_KEY
environment variable. User must obtain an API key from Cohere’s website
([https://dashboard.cohere.com/](https://dashboard.cohere.com/)). Additionally, the cohere python
client must be installed with pip install cohere.

The vectorizer supports only synchronous operations, allows for batch
processing of texts and flexibility in handling preprocessing tasks.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the Cohere vectorizer.

Visit [https://cohere.ai/embed](https://cohere.ai/embed) to learn about embeddings.

* **Parameters:**
  * **model** (*str*) – Model to use for embedding. Defaults to ‘embed-english-v3.0’.
  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the API key.
    Defaults to None.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    ‘float32’ will use Cohere’s float embeddings, ‘int8’ and ‘uint8’ will map
    to Cohere’s corresponding embedding types. Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ImportError** – If the cohere library is not installed.
  * **ValueError** – If the API key is not provided.
  * **ValueError** – If an invalid dtype is provided.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: BedrockTextVectorizer

title: `class BedrockTextVectorizer(model='amazon.titan-embed-text-v2:0', api_config=None, dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The AmazonBedrockTextVectorizer class utilizes Amazon Bedrock’s API to generate
embeddings for text data.

This vectorizer is designed to interact with Amazon Bedrock API,
requiring AWS credentials for authentication. The credentials can be provided
directly in the api_config dictionary or through environment variables:
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_REGION (defaults to us-east-1)

The vectorizer supports synchronous operations with batch processing and
preprocessing capabilities.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the AWS Bedrock Vectorizer.

* **Parameters:**
  * **model** (*str*) – The Bedrock model ID to use. Defaults to amazon.titan-embed-text-v2:0
  * **api_config** (*Optional* *[* *Dict* *[* *str* *,* *str* *]* *]*) – AWS credentials and config.
    Can include: aws_access_key_id, aws_secret_access_key, aws_region
    If not provided, will use environment variables.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ValueError** – If credentials are not provided in config or environment.
  * **ImportError** – If boto3 is not installed.
  * **ValueError** – If an invalid dtype is provided.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: CustomTextVectorizer

title: `class CustomTextVectorizer(embed, embed_many=None, aembed=None, aembed_many=None, dtype='float32', cache=None)`

text:
Bases: `BaseVectorizer`

The CustomTextVectorizer class wraps user-defined embedding methods to create
embeddings for text data.

This vectorizer is designed to accept a provided callable text vectorizer and
provides a class definition to allow for compatibility with RedisVL.
The vectorizer may support both synchronous and asynchronous operations which
allows for batch processing of texts, but at a minimum only synchronous embedding
is required to satisfy the ‘embed()’ method.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the Custom vectorizer.

* **Parameters:**
  * **embed** (*Callable*) – a Callable function that accepts a string object and returns a list of floats.
  * **embed_many** (*Optional* *[* *Callable* *]*) – a Callable function that accepts a list of string objects and returns a list containing lists of floats. Defaults to None.
  * **aembed** (*Optional* *[* *Callable* *]*) – an asynchronous Callable function that accepts a string object and returns a lists of floats. Defaults to None.
  * **aembed_many** (*Optional* *[* *Callable* *]*) – an asynchronous Callable function that accepts a list of string objects and returns a list containing lists of floats. Defaults to None.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
* **Raises:**
  **ValueError** – if embedding validation fails.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

title: VoyageAITextVectorizer

title: `class VoyageAITextVectorizer(model='voyage-large-2', api_config=None, dtype='float32', cache=None, *, dims=None)`

text:
Bases: `BaseVectorizer`

The VoyageAITextVectorizer class utilizes VoyageAI’s API to generate
embeddings for text data.

This vectorizer is designed to interact with VoyageAI’s /embed API,
requiring an API key for authentication. The key can be provided
directly in the api_config dictionary or through the VOYAGE_API_KEY
environment variable. User must obtain an API key from VoyageAI’s website
([https://dash.voyageai.com/](https://dash.voyageai.com/)). Additionally, the voyageai python
client must be installed with pip install voyageai.

The vectorizer supports both synchronous and asynchronous operations, allows for batch
processing of texts and flexibility in handling preprocessing tasks.

You can optionally enable caching to improve performance when generating
embeddings for repeated text inputs.

[code example]

Initialize the VoyageAI vectorizer.

Visit [https://docs.voyageai.com/docs/embeddings](https://docs.voyageai.com/docs/embeddings) to learn about embeddings and check the available models.

* **Parameters:**
  * **model** (*str*) – Model to use for embedding. Defaults to "voyage-large-2".
  * **api_config** (*Optional* *[* *Dict* *]* *,* *optional*) – Dictionary containing the API key.
    Defaults to None.
  * **dtype** (*str*) – the default datatype to use when embedding text as byte arrays.
    Used when setting as_buffer=True in calls to embed() and embed_many().
    Defaults to ‘float32’.
  * **cache** (*Optional* *[*[*EmbeddingsCache*]() *]*) – Optional EmbeddingsCache instance to cache embeddings for
    better performance with repeated texts. Defaults to None.
  * **dims** (*Annotated* *[* *int* *|* *None* *,* *FieldInfo* *(* *annotation=NoneType* *,* *required=True* *,* *metadata=* *[* *Strict* *(* *strict=True* *)* *,* *Gt* *(* *gt=0* *)* *]* *)* *]*)
* **Raises:**
  * **ImportError** – If the voyageai library is not installed.
  * **ValueError** – If the API key is not provided.

#### `model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}`

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

#### `property type: str`

Return the type of vectorizer.

code:
# Basic usage
vectorizer = HFTextVectorizer(model="sentence-transformers/all-mpnet-base-v2")
embedding = vectorizer.embed("Hello, world!")

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="my_embeddings_cache")

vectorizer = HFTextVectorizer(
    model="sentence-transformers/all-mpnet-base-v2",
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed("Hello, world!")

# Second call will retrieve from cache
embedding2 = vectorizer.embed("Hello, world!")

# Batch processing
embeddings = vectorizer.embed_many(
    ["Hello, world!", "How are you?"],
    batch_size=2
)

code:
# Basic usage with OpenAI embeddings
vectorizer = OpenAITextVectorizer(
    model="text-embedding-ada-002",
    api_config={"api_key": "your_api_key"} # OR set OPENAI_API_KEY in your env
)
embedding = vectorizer.embed("Hello, world!")

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="openai_embeddings_cache")

vectorizer = OpenAITextVectorizer(
    model="text-embedding-ada-002",
    api_config={"api_key": "your_api_key"},
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed("Hello, world!")

# Second call will retrieve from cache
embedding2 = vectorizer.embed("Hello, world!")

# Asynchronous batch embedding of multiple texts
embeddings = await vectorizer.aembed_many(
    ["Hello, world!", "How are you?"],
    batch_size=2
)

code:
# Basic usage
vectorizer = AzureOpenAITextVectorizer(
    model="text-embedding-ada-002",
    api_config={
        "api_key": "your_api_key", # OR set AZURE_OPENAI_API_KEY in your env
        "api_version": "your_api_version", # OR set OPENAI_API_VERSION in your env
        "azure_endpoint": "your_azure_endpoint", # OR set AZURE_OPENAI_ENDPOINT in your env
    }
)
embedding = vectorizer.embed("Hello, world!")

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="azureopenai_embeddings_cache")

vectorizer = AzureOpenAITextVectorizer(
    model="text-embedding-ada-002",
    api_config={
        "api_key": "your_api_key",
        "api_version": "your_api_version",
        "azure_endpoint": "your_azure_endpoint",
    },
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed("Hello, world!")

# Second call will retrieve from cache
embedding2 = vectorizer.embed("Hello, world!")

# Asynchronous batch embedding of multiple texts
embeddings = await vectorizer.aembed_many(
    ["Hello, world!", "How are you?"],
    batch_size=2
)

code:
# Basic usage
vectorizer = VertexAITextVectorizer(
    model="textembedding-gecko",
    api_config={
        "project_id": "your_gcp_project_id", # OR set GCP_PROJECT_ID
        "location": "your_gcp_location",     # OR set GCP_LOCATION
    })
embedding = vectorizer.embed("Hello, world!")

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="vertexai_embeddings_cache")

vectorizer = VertexAITextVectorizer(
    model="textembedding-gecko",
    api_config={
        "project_id": "your_gcp_project_id",
        "location": "your_gcp_location",
    },
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed("Hello, world!")

# Second call will retrieve from cache
embedding2 = vectorizer.embed("Hello, world!")

# Batch embedding of multiple texts
embeddings = vectorizer.embed_many(
    ["Hello, world!", "Goodbye, world!"],
    batch_size=2
)

code:
from redisvl.utils.vectorize import CohereTextVectorizer

# Basic usage
vectorizer = CohereTextVectorizer(
    model="embed-english-v3.0",
    api_config={"api_key": "your-cohere-api-key"} # OR set COHERE_API_KEY in your env
)
query_embedding = vectorizer.embed(
    text="your input query text here",
    input_type="search_query"
)
doc_embeddings = vectorizer.embed_many(
    texts=["your document text", "more document text"],
    input_type="search_document"
)

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="cohere_embeddings_cache")

vectorizer = CohereTextVectorizer(
    model="embed-english-v3.0",
    api_config={"api_key": "your-cohere-api-key"},
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed(
    text="your input query text here",
    input_type="search_query"
)

# Second call will retrieve from cache
embedding2 = vectorizer.embed(
    text="your input query text here",
    input_type="search_query"
)

code:
# Basic usage with explicit credentials
vectorizer = AmazonBedrockTextVectorizer(
    model="amazon.titan-embed-text-v2:0",
    api_config={
        "aws_access_key_id": "your_access_key",
        "aws_secret_access_key": "your_secret_key",
        "aws_region": "us-east-1"
    }
)

# With environment variables and caching
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="bedrock_embeddings_cache")

vectorizer = AmazonBedrockTextVectorizer(
    model="amazon.titan-embed-text-v2:0",
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed("Hello, world!")

# Second call will retrieve from cache
embedding2 = vectorizer.embed("Hello, world!")

# Generate batch embeddings
embeddings = vectorizer.embed_many(["Hello", "World"], batch_size=2)

code:
# Basic usage with a custom embedding function
vectorizer = CustomTextVectorizer(
    embed = my_vectorizer.generate_embedding
)
embedding = vectorizer.embed("Hello, world!")

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="my_embeddings_cache")

vectorizer = CustomTextVectorizer(
    embed=my_vectorizer.generate_embedding,
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed("Hello, world!")

# Second call will retrieve from cache
embedding2 = vectorizer.embed("Hello, world!")

# Asynchronous batch embedding of multiple texts
embeddings = await vectorizer.aembed_many(
    ["Hello, world!", "How are you?"],
    batch_size=2
)

code:
from redisvl.utils.vectorize import VoyageAITextVectorizer

# Basic usage
vectorizer = VoyageAITextVectorizer(
    model="voyage-large-2",
    api_config={"api_key": "your-voyageai-api-key"} # OR set VOYAGE_API_KEY in your env
)
query_embedding = vectorizer.embed(
    text="your input query text here",
    input_type="query"
)
doc_embeddings = vectorizer.embed_many(
    texts=["your document text", "more document text"],
    input_type="document"
)

# With caching enabled
from redisvl.extensions.cache.embeddings import EmbeddingsCache
cache = EmbeddingsCache(name="voyageai_embeddings_cache")

vectorizer = VoyageAITextVectorizer(
    model="voyage-large-2",
    api_config={"api_key": "your-voyageai-api-key"},
    cache=cache
)

# First call will compute and cache the embedding
embedding1 = vectorizer.embed(
    text="your input query text here",
    input_type="query"
)

# Second call will retrieve from cache
embedding2 = vectorizer.embed(
    text="your input query text here",
    input_type="query"
)


title: RedisVL

summary: This is the Redis vector library (RedisVL).

title: Install RedisVL

title: Overview

title: User Guides

title: RedisVL API

page_type: content

title: Content

role: content

text:
RedisVL is a powerful, dedicated Python client library for Redis that enables seamless integration and management of high-dimensional vector data.
Built to support machine learning and artificial intelligence workflows, RedisVL simplifies the process of storing, searching, and analyzing vector embeddings, which are commonly used for tasks like recommendation systems, semantic search, and anomaly detection.

Key features of RedisVL include:

- Vector Similarity Search: Efficiently find nearest neighbors in high-dimensional spaces using algorithms like HNSW (Hierarchical Navigable Small World).
- Integration with AI Frameworks: RedisVL works seamlessly with popular frameworks such as TensorFlow, PyTorch, and Hugging Face, making it easy to deploy AI models.
- Scalable and Fast: Leveraging Redis's in-memory architecture, RedisVL provides low-latency access to vector data, even at scale.
- By bridging the gap between data storage and AI model deployment, RedisVL empowers developers to build intelligent, real-time applications with minimal infrastructure complexity.


title: Install RedisVL

title: Installation

role: setup

text:
Install the `redisvl` package into your Python (>=3.8) environment using the `pip` command:

[code example]

Then make sure to have a Redis instance with the Redis Query Engine features enabled on Redis Cloud or locally in docker with Redis Stack:

[code example]

After running the previous command, the Redis Insight GUI will be available at http://localhost:8001.

code: pip install redisvl

section_id: installation

code: docker run -d --name redis -p 6379:6379 -p 8001:8001 redis/redis-stack:latest


title: The RedisVL CLI

title: Overview

role: overview

text:
RedisVL is a Python library with a dedicated CLI to help load and create vector search indices within Redis.

This notebook will walk through how to use the Redis Vector Library CLI (``rvl``).

Before running this notebook, be sure to
1. Have installed ``redisvl`` and have that environment active for this notebook.
2. Have a running Redis instance with the Search and Query capability

[code example]

    11:20:38 [RedisVL] INFO   RedisVL version 0.8.2

title: Commands

role: syntax

text:
Here's a table of all the rvl commands and options. We'll go into each one in detail below.

| Command       | Options                  | Description |
|---------------|--------------------------|-------------|
| `rvl version` |                          | display the redisvl library version|
| `rvl index`   | `create --schema` or `-s <schema.yaml>`| create a redis index from the specified schema file|
| `rvl index`   | `listall`                | list all the existing search indices|
| `rvl index`   | `info --index` or ` -i <index_name>`   | display the index definition in tabular format|
| `rvl index`   | `delete --index` or `-i <index_name>` | remove the specified index, leaving the data still in Redis|
| `rvl index`   | `destroy --index` or `-i <index_name>`| remove the specified index, as well as the associated data|
| `rvl stats`   | `--index` or `-i <index_name>`        | display the index statistics, including number of docs, average bytes per record, indexing time, etc|
| `rvl stats`   | `--schema` or `-s <schema.yaml>`        | display the index statistics of a schema defined in <schema.yaml>. The index must have already been created within Redis|

title: Index

text:
The ``rvl index`` command can be used for a number of tasks related to creating and managing indices. Whether you are working in Python or another language, this cli tool can still be useful for managing and inspecting your indices.

First, we will create an index from a yaml schema that looks like the following:

[code example]

    Overwriting schema.yaml

[code example]

    12:42:45 [RedisVL] INFO   Index created successfully

[code example]

    12:42:47 [RedisVL] INFO   Indices:
    12:42:47 [RedisVL] INFO   1. vectorizers

[code example]

    Index Information:
    ╭───────────────┬───────────────┬───────────────┬───────────────┬───────────────╮
    │ Index Name    │ Storage Type  │ Prefixes      │ Index Options │ Indexing      │
    ├───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤
    | vectorizers   | HASH          | ['doc']       | []            | 0             |
    ╰───────────────┴───────────────┴───────────────┴───────────────┴───────────────╯
    Index Fields:
    ╭─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬─────────────────╮
    │ Name            │ Attribute       │ Type            │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │ Field Option    │ Option Value    │
    ├─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼─────────────────┤
    │ sentence        │ sentence        │ TEXT            │ WEIGHT          │ 1               │                 │                 │                 │                 │                 │                 │
    │ embedding       │ embedding       │ VECTOR          │ algorithm       │ FLAT            │ data_type       │ FLOAT32         │ dim             │ 768             │ distance_metric │ COSINE          │
    ╰─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────┴─────────────────╯

[code example]

    12:42:54 [RedisVL] INFO   Index deleted successfully

[code example]

    12:42:56 [RedisVL] INFO   Indices:

title: Stats

text:
The ``rvl stats`` command will return some basic information about the index. This is useful for checking the status of an index, or for getting information about the index to use in other commands.

[code example]

    12:42:59 [RedisVL] INFO   Index created successfully

[code example]

    12:43:01 [RedisVL] INFO   Indices:
    12:43:01 [RedisVL] INFO   1. vectorizers

[code example]

    Statistics:
    ╭─────────────────────────────┬────────────╮
    │ Stat Key                    │ Value      │
    ├─────────────────────────────┼────────────┤
    │ num_docs                    │ 0          │
    │ num_terms                   │ 0          │
    │ max_doc_id                  │ 0          │
    │ num_records                 │ 0          │
    │ percent_indexed             │ 1          │
    │ hash_indexing_failures      │ 0          │
    │ number_of_uses              │ 1          │
    │ bytes_per_record_avg        │ nan        │
    │ doc_table_size_mb           │ 0          │
    │ inverted_sz_mb              │ 0          │
    │ key_table_size_mb           │ 0          │
    │ offset_bits_per_record_avg  │ nan        │
    │ offset_vectors_sz_mb        │ 0          │
    │ offsets_per_term_avg        │ nan        │
    │ records_per_doc_avg         │ nan        │
    │ sortable_values_size_mb     │ 0          │
    │ total_indexing_time         │ 0          │
    │ total_inverted_index_blocks │ 0          │
    │ vector_index_sz_mb          │ 0.00818634 │
    ╰─────────────────────────────┴────────────╯

title: Optional arguments

role: parameters

text:
You can modify these commands with the below optional arguments

| Argument       | Description | Default |
|----------------|-------------|---------|
| `-u --url`     | The full Redis URL to connect to | `redis://localhost:6379` |
| `--host`       | Redis host to connect to | `localhost` |
| `-p --port`    | Redis port to connect to. Must be an integer | `6379` |
| `--user`       | Redis username, if one is required   | `default` |
| `--ssl`        | Boolean flag indicating if ssl is required. If set the Redis base url changes to `rediss://` | None |
| `-a --password`| Redis password, if one is required| `""` |

title: Choosing your Redis instance

text:
By default rvl first checks if you have `REDIS_URL` environment variable defined and tries to connect to that. If not, it then falls back to `localhost:6379`, unless you pass the `--host` or `--port` arguments

[code example]

    12:43:06 [RedisVL] INFO   Indices:
    12:43:06 [RedisVL] INFO   1. vectorizers

title: Using SSL encryption

text:
If your Redis instance is configured to use SSL encryption then set the `--ssl` flag.
You can similarly specify the username and password to construct the full Redis URL

[code example]

[code example]

    12:43:09 [RedisVL] INFO   Index deleted successfully

code:
# First, see if the rvl tool is installed
!rvl version

section_id: overview

code:
%%writefile schema.yaml

version: '0.1.0'

index:
    name: vectorizers
    prefix: doc
    storage_type: hash

fields:
    - name: sentence
      type: text
    - name: embedding
      type: vector
      attrs:
        dims: 768
        algorithm: flat
        distance_metric: cosine

section_id: index

code:
# Create an index from a yaml schema
!rvl index create -s schema.yaml

code:
# list the indices that are available
!rvl index listall

code:
# inspect the index fields
!rvl index info -i vectorizers

code:
# delete an index without deleting the data within it
!rvl index delete -i vectorizers

code:
# see the indices that still exist
!rvl index listall

code:
# create a new index with the same schema
# recreating the index will reindex the documents
!rvl index create -s schema.yaml

section_id: stats

code:
# see all the stats for the index
!rvl stats -i vectorizers

code:
# specify your Redis instance to connect to
!rvl index listall --host localhost --port 6379

section_id: choosing-your-redis-instance

code:
# connect to rediss://jane_doe:password123@localhost:6379
!rvl index listall --user jane_doe -a password123 --ssl

section_id: using-ssl-encryption

code: !rvl index destroy -i vectorizers


title: Overview

title: Install RedisVL

title: The RedisVL CLI

title: Content

text:
* [Install RedisVL](installation/)
  * [Install RedisVL with Pip](installation/#install-redisvl-with-pip)
  * [Install RedisVL from Source](installation/#install-redisvl-from-source)
  * [Installing Redis](installation/#installing-redis)
* [The RedisVL CLI](cli/)
  * [Commands](cli/#commands)
  * [Index](cli/#index)
  * [Stats](cli/#stats)
  * [Optional arguments](cli/#optional-arguments)


title: Install RedisVL

title: Overview

text: There are a few ways to install RedisVL. The easiest way is to use pip.

title: Install RedisVL with Pip

text:
Install `redisvl` into your Python (>=3.8) environment using `pip`:

[code example]

RedisVL comes with a few dependencies that are automatically installed, however, a few dependencies
are optional and can be installed separately if needed:

[code example]

If you use ZSH, remember to escape the brackets:

[code example]

This library supports the use of hiredis, so you can also install by running:

[code example]

title: Install RedisVL from Source

text:
To install RedisVL from source, clone the repository and install the package using `pip`:

[code example]

title: Installing Redis

text:
RedisVL requires a distribution of Redis that supports the [Search and Query](https://redis.com/modules/redis-search/) capability of which there are 3:

offering

1. [Redis Cloud](https://redis.io/cloud), a fully managed cloud offering
2. [Redis Stack](https://redis.io/docs/getting-started/install-stack/docker/), a local docker image for testing and development
3. [Redis Software](https://redis.com/redis-enterprise/), a commercial self-hosted

title: Redis Cloud

text:
Redis Cloud is the easiest way to get started with RedisVL. You can sign up for a free account [here](https://redis.io/cloud). Make sure to have the `Search and Query`
capability enabled when creating your database.

title: Redis Stack (local development)

text:
For local development and testing, Redis-Stack can be used. We recommend running Redis
in a docker container. To do so, run the following command:

[code example]

This will also spin up the [Redis Insight GUI](https://redis.io/insight/) at `http://localhost:8001`.

title: Redis Software (self-hosted)

text:
Redis Software is a commercial offering that can be self-hosted. You can download the latest version [here](https://redis.io/downloads/).

If you are considering a self-hosted Redis Software deployment on Kubernetes, there is the [Redis Software Operator](https://docs.redis.com/latest/kubernetes/) for Kubernetes. This will allow you to easily deploy and manage a Redis Software cluster on Kubernetes.

title: Redis Sentinel

text:
For high availability deployments, RedisVL supports connecting to Redis through Sentinel. Use the `redis+sentinel://` URL scheme to connect:

[code example]

The Sentinel URL format supports:

- Multiple sentinel hosts (comma-separated)
- Optional authentication (username:password)
- Service name (required - the name of the Redis master)
- Optional database number (defaults to 0)

code: $ pip install -U redisvl

section_id: install-redisvl-with-pip

code:
$ pip install redisvl[all]  # install vectorizer dependencies
$ pip install redisvl[dev]  # install dev dependencies

code: $ pip install redisvl\[all\]

code: pip install redisvl[hiredis]

code:
$ git clone https://github.com/redis/redis-vl-python.git && cd redisvl
$ pip install .

# or for an editable installation (for developers of RedisVL)
$ pip install -e .

section_id: install-redisvl-from-source

code: docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

section_id: redis-stack-local-development

code:
from redisvl.index import SearchIndex

# Connect via Sentinel
# Format: redis+sentinel://[username:password@]host1:port1,host2:port2/service_name[/db]
index = SearchIndex.from_yaml(
    "schema.yaml",
    redis_url="redis+sentinel://sentinel1:26379,sentinel2:26379/mymaster"
)

# With authentication
index = SearchIndex.from_yaml(
    "schema.yaml",
    redis_url="redis+sentinel://user:pass@sentinel1:26379,sentinel2:26379/mymaster/0"
)

section_id: redis-sentinel


title: Advanced Query Types

title: Overview

text:
In this notebook, we will explore advanced query types available in RedisVL:

1. **`TextQuery`**: Full text search with advanced scoring
2. **`AggregateHybridQuery`**: Combines text and vector search for hybrid retrieval
3. **`MultiVectorQuery`**: Search over multiple vector fields simultaneously

These query types are powerful tools for building sophisticated search applications that go beyond simple vector similarity search.

Prerequisites:
- Ensure `redisvl` is installed in your Python environment.
- Have a running instance of [Redis Stack](https://redis.io/docs/install/install-stack/) or [Redis Cloud](https://redis.io/cloud).

title: Setup and Data Preparation

text:
First, let's create a schema and prepare sample data that includes text fields, numeric fields, and vector fields.

[code example]

title: Define the Schema

text:
Our schema includes:
- **Tag fields**: `product_id`, `category`
- **Text fields**: `brief_description` and `full_description` for full-text search
- **Numeric fields**: `price`, `rating`
- **Vector fields**: `text_embedding` (3 dimensions) and `image_embedding` (2 dimensions) for semantic search

[code example]

title: Create Index and Load Data

text:
[code example]

    16:27:50 redisvl.index.index INFO   Index already exists, overwriting.
    Loaded 6 products into the index

title: 1. TextQuery: Full Text Search

text: The `TextQuery` class enables full text search with advanced scoring algorithms. It's ideal for keyword-based search with relevance ranking.

title: Basic Text Search

text:
Let's search for products related to "running shoes":

[code example]

score	product_id	brief_description	category	price

4.080705480646511	prod_1	comfortable running shoes for athletes	footwear	89.99

1.4504838715161907	prod_5	basketball shoes with excellent ankle support	footwear	139.99

1.431980178975859	prod_2	lightweight running jacket with water resistance	outerwear	129.99

title: Text Search with Different Scoring Algorithms

text:
RedisVL supports multiple text scoring algorithms. Let's compare `BM25STD` and `TFIDF`:

[code example]

    Results with BM25 scoring:

score	product_id	brief_description	price

4.165936382048982	prod_1	comfortable running shoes for athletes	89.99

1.769051138581863	prod_4	yoga mat with extra cushioning for comfort	39.99

1.2306902673750557	prod_5	basketball shoes with excellent ankle support	139.99

[code example]

    Results with TFIDF scoring:

score	product_id	brief_description	price

1.3333333333333333	prod_1	comfortable running shoes for athletes	89.99

1.3333333333333333	prod_1	comfortable running shoes for athletes	89.99

1.0	prod_5	basketball shoes with excellent ankle support	139.99

title: Text Search with Filters

text:
Combine text search with filters to narrow results:

[code example]

score	product_id	brief_description	category	price

2.385806908729779	prod_1	comfortable running shoes for athletes	footwear	89.99

2.385806908729779	prod_1	comfortable running shoes for athletes	footwear	89.99

1.9340948871093797	prod_5	basketball shoes with excellent ankle support	footwear	139.99

[code example]

score	product_id	brief_description	price

2.2775029612659465	prod_1	comfortable running shoes for athletes	89.99

1.1387514806329733	prod_1	comfortable running shoes for athletes	89.99

1.1190633543347508	prod_4	yoga mat with extra cushioning for comfort	39.99

1.1190633543347508	prod_4	yoga mat with extra cushioning for comfort	39.99

title: Text Search with Multiple Fields and Weights

text:
You can search across multiple text fields with different weights to prioritize certain fields.
Here we'll prioritize the `brief_description` field and make text similarity in that field twice as important as text similarity in `full_description`:

[code example]

score	product_id	brief_description

3.040323653363804	prod_1	comfortable running shoes for athletes

3.040323653363804	prod_1	comfortable running shoes for athletes

1.289396591406253	prod_5	basketball shoes with excellent ankle support

title: Text Search with Custom Stopwords

text:
Stopwords are common words that are filtered out before processing the query. You can specify which language's default stopwords should be filtered out, like `english`, `french`, or `german`. You can also define your own list of stopwords:

[code example]

score	product_id	brief_description

4.1444591833267275	prod_1	comfortable running shoes for athletes

4.1444591833267275	prod_1	comfortable running shoes for athletes

1.4875097606385526	prod_5	basketball shoes with excellent ankle support

[code example]

score	product_id	brief_description

2.5107799078325	prod_1	comfortable running shoes for athletes

2.5107799078325	prod_1	comfortable running shoes for athletes

2.482820220115406	prod_3	professional tennis racket for competitive players

[code example]

score	product_id	brief_description

3.69730364515632	prod_1	comfortable running shoes for athletes

3.69730364515632	prod_1	comfortable running shoes for athletes

1.5329921800414583	prod_5	basketball shoes with excellent ankle support

title: 2. AggregateHybridQuery: Combining Text and Vector Search

text:
The `AggregateHybridQuery` combines text search and vector similarity to provide the best of both worlds:
- **Text search**: Finds exact keyword matches
- **Vector search**: Captures semantic similarity

Results are scored using a weighted combination:

[code example]

Where `alpha` controls the balance between vector and text search (default: 0.7).

title: Index-Level Stopwords Configuration

text:
The previous example showed **query-time stopwords** using `TextQuery.stopwords`, which filters words from the query before searching. RedisVL also supports **index-level stopwords** configuration, which determines which words are indexed in the first place.

**Key Difference:**
- **Query-time stopwords** (`TextQuery.stopwords`): Filters words from your search query (client-side)
- **Index-level stopwords** (`IndexInfo.stopwords`): Controls which words get indexed in Redis (server-side)

**Three Configuration Modes:**

1. **`None` (default)**: Use Redis's default stopwords list
2. **`[]` (empty list)**: Disable stopwords completely (`STOPWORDS 0` in FT.CREATE)
3. **`["the", "a", "an"]`**: Use a custom stopwords list

**When to use `STOPWORDS 0`:**
- When you need to search for common words like "of", "at", "the"
- For entity names containing stopwords (e.g., "Bank of Glasberliner", "University of Glasberliner")
- When working with structured data where every word matters

[code example]

    Index created with STOPWORDS 0: <redisvl.index.index.SearchIndex object at 0x109ce3c50>

[code example]

    ✓ Loaded 5 companies

[code example]

    Found 1 results for 'Bank of Glasberliner':
      - Bank of Glasberliner: Major financial institution

**Comparison: With vs Without Stopwords**

If we had used the default stopwords (not specifying `stopwords` in the schema), the word "of" would be filtered out during indexing. This means:

- ❌ Searching for `"Bank of Glasberliner"` might not find exact matches
- ❌ The phrase would be indexed as `"Bank Berlin"` (without "of")
- ✅ With `STOPWORDS 0`, all words including "of" are indexed

**Custom Stopwords Example:**

You can also provide a custom list of stopwords:

[code example]

    Custom stopwords: ['inc', 'llc', 'corp']

**YAML Format:**

You can also define stopwords in YAML schema files:

[code example]

Or with custom stopwords:

[code example]

[code example]

    ✓ Cleaned up company_index

title: Basic Aggregate Hybrid Query

text:
Let's search for "running" with both text and semantic search:

[code example]

vector_distance	product_id	brief_description	category	price	vector_similarity	text_score	hybrid_score

5.96046447754e-08	prod_1	comfortable running shoes for athletes	footwear	89.99	0.999999970198	4.82977442609	2.14893230697

5.96046447754e-08	prod_1	comfortable running shoes for athletes	footwear	89.99	0.999999970198	4.82977442609	2.14893230697

5.96046447754e-08	prod_1	comfortable running shoes for athletes	footwear	89.99	0.999999970198	4.82977442609	2.14893230697

0.0038834810257	prod_4	yoga mat with extra cushioning for comfort	accessories	39.99	0.998058259487	0	0.698640781641

0.0038834810257	prod_4	yoga mat with extra cushioning for comfort	accessories	39.99	0.998058259487	0	0.698640781641

title: Adjusting the Alpha Parameter

text:
The `alpha` parameter controls the weight between vector and text search:
- `alpha=1.0`: Pure vector search
- `alpha=0.0`: Pure text search
- `alpha=0.7` (default): 70% vector, 30% text

[code example]

    Results with alpha=0.9 (vector-heavy):

vector_distance	product_id	brief_description	vector_similarity	text_score	hybrid_score

-1.19209289551e-07	prod_4	yoga mat with extra cushioning for comfort	1.0000000596	1.53838070541	1.05383812419

-1.19209289551e-07	prod_4	yoga mat with extra cushioning for comfort	1.0000000596	1.53838070541	1.05383812419

-1.19209289551e-07	prod_4	yoga mat with extra cushioning for comfort	1.0000000596	1.53838070541	1.05383812419

title: Aggregate Hybrid Query with Filters

text:
You can also combine hybrid search with filters:

[code example]

vector_distance	product_id	brief_description	category	price	vector_similarity	text_score	hybrid_score

-1.19209289551e-07	prod_3	professional tennis racket for competitive players	equipment	199.99	1.0000000596	1.54723705506	1.16417115824

-1.19209289551e-07	prod_3	professional tennis racket for competitive players	equipment	199.99	1.0000000596	1.54723705506	1.16417115824

-1.19209289551e-07	prod_3	professional tennis racket for competitive players	equipment	199.99	1.0000000596	1.54723705506	1.16417115824

0.411657452583	prod_2	lightweight running jacket with water resistance	outerwear	129.99	0.794171273708	0	0.555919891596

0.411657452583	prod_2	lightweight running jacket with water resistance	outerwear	129.99	0.794171273708	0	0.555919891596

title: Using Different Text Scorers

text:
AggregateHybridQuery supports the same text scoring algorithms as TextQuery:

[code example]

vector_distance	product_id	brief_description	vector_similarity	text_score	hybrid_score

0	prod_5	basketball shoes with excellent ankle support	1	3	1.6

0	prod_2	lightweight running jacket with water resistance	1	0	0.7

0	prod_2	lightweight running jacket with water resistance	1	0	0.7

title: Runtime Parameters for Vector Search Tuning

text:
**Important:** `AggregateHybridQuery` uses FT.AGGREGATE commands which do NOT support runtime parameters.

Runtime parameters (such as `ef_runtime` for HNSW indexes or `search_window_size` for SVS-VAMANA indexes) are only supported with FT.SEARCH commands.

**For runtime parameter support, use `VectorQuery` or `VectorRangeQuery` instead:**

- `VectorQuery`: Supports all runtime parameters (HNSW and SVS-VAMANA)
- `VectorRangeQuery`: Supports all runtime parameters (HNSW and SVS-VAMANA)
- `AggregateHybridQuery`: Does NOT support runtime parameters (uses FT.AGGREGATE)

See the **Runtime Parameters** section earlier in this notebook for examples of using runtime parameters with `VectorQuery`.

title: 3. MultiVectorQuery: Multi-Vector Search

text:
The `MultiVectorQuery` allows you to search over multiple vector fields simultaneously. This is useful when you have different types of embeddings (e.g., text and image embeddings) and want to find results that match across multiple modalities.

The final score is calculated as a weighted combination:

[code example]

title: Basic Multi-Vector Query

text:
First, we need to import the `Vector` class to define our query vectors:

[code example]

title: Adjusting Vector Weights

text:
You can adjust the weights to prioritize different vector fields:

[code example]

title: Multi-Vector Query with Filters

text:
Combine multi-vector search with filters to narrow results:

[code example]

title: Comparing Query Types

text:
Let's compare the three query types side by side:

[code example]

[code example]

[code example]

title: Best Practices

title: When to Use Each Query Type:

text:
1. **`TextQuery`**:
   - When you need precise keyword matching
   - For traditional search engine functionality
   - When text relevance scoring is important
   - Example: Product search, document retrieval

2. **`AggregateHybridQuery`**:
   - When you want to combine keyword and semantic search
   - For improved search quality over pure text or vector search
   - When you have both text and vector representations of your data
   - Example: E-commerce search, content recommendation

3. **`MultiVectorQuery`**:
   - When you have multiple types of embeddings (text, image, audio, etc.)
   - For multi-modal search applications
   - When you want to balance multiple semantic signals
   - Example: Image-text search, cross-modal retrieval

[code example]

code:
import numpy as np
from jupyterutils import result_print

# Sample data with text descriptions, categories, and vectors
data = [
    {
        'product_id': 'prod_1',
        'brief_description': 'comfortable running shoes for athletes',
        'full_description': 'Engineered with a dual-layer EVA foam midsole and FlexWeave breathable mesh upper, these running shoes deliver responsive cushioning for long-distance runs. The anatomical footbed adapts to your stride while the carbon rubber outsole provides superior traction on varied terrain.',
        'category': 'footwear',
        'price': 89.99,
        'rating': 4.5,
        'text_embedding': np.array([0.1, 0.2, 0.1], dtype=np.float32).tobytes(),
        'image_embedding': np.array([0.8, 0.1], dtype=np.float32).tobytes(),
    },
    {
        'product_id': 'prod_2',
        'brief_description': 'lightweight running jacket with water resistance',
        'full_description': 'Stay protected with this ultralight 2.5-layer DWR-coated shell featuring laser-cut ventilation zones and reflective piping for low-light visibility. Packs into its own chest pocket and weighs just 4.2 oz, making it ideal for unpredictable weather conditions.',
        'category': 'outerwear',
        'price': 129.99,
        'rating': 4.8,
        'text_embedding': np.array([0.2, 0.3, 0.2], dtype=np.float32).tobytes(),
        'image_embedding': np.array([0.7, 0.2], dtype=np.float32).tobytes(),
    },
    {
        'product_id': 'prod_3',
        'brief_description': 'professional tennis racket for competitive players',
        'full_description': 'Competition-grade racket featuring a 98 sq in head size, 16x19 string pattern, and aerospace-grade graphite frame that delivers explosive power with pinpoint control. Tournament-approved specs include 315g weight and 68 RA stiffness rating for advanced baseline play.',
        'category': 'equipment',
        'price': 199.99,
        'rating': 4.9,
        'text_embedding': np.array([0.9, 0.1, 0.05], dtype=np.float32).tobytes(),
        'image_embedding': np.array([0.1, 0.9], dtype=np.float32).tobytes(),
    },
    {
        'product_id': 'prod_4',
        'brief_description': 'yoga mat with extra cushioning for comfort',
        'full_description': 'Premium 8mm thick TPE yoga mat with dual-texture surface - smooth side for hot yoga flow and textured side for maximum grip during balancing poses. Closed-cell technology prevents moisture absorption while alignment markers guide proper positioning in asanas.',
        'category': 'accessories',
        'price': 39.99,
        'rating': 4.3,
        'text_embedding': np.array([0.15, 0.25, 0.15], dtype=np.float32).tobytes(),
        'image_embedding': np.array([0.5, 0.5], dtype=np.float32).tobytes(),
    },
    {
        'product_id': 'prod_5',
        'brief_description': 'basketball shoes with excellent ankle support',
        'full_description': 'High-top basketball sneakers with Zoom Air units in forefoot and heel, reinforced lateral sidewalls for explosive cuts, and herringbone traction pattern optimized for hardwood courts. The internal bootie construction and extended ankle collar provide lockdown support during aggressive drives.',
        'category': 'footwear',
        'price': 139.99,
        'rating': 4.7,
        'text_embedding': np.array([0.12, 0.18, 0.12], dtype=np.float32).tobytes(),
        'image_embedding': np.array([0.75, 0.15], dtype=np.float32).tobytes(),
    },
    {
        'product_id': 'prod_6',
        'brief_description': 'swimming goggles with anti-fog coating',
        'full_description': 'Low-profile competition goggles with curved polycarbonate lenses offering 180-degree peripheral vision and UV protection. Hydrophobic anti-fog coating lasts 10x longer than standard treatments, while the split silicone strap and interchangeable nose bridges ensure a watertight, custom fit.',
        'category': 'accessories',
        'price': 24.99,
        'rating': 4.4,
        'text_embedding': np.array([0.3, 0.1, 0.2], dtype=np.float32).tobytes(),
        'image_embedding': np.array([0.2, 0.8], dtype=np.float32).tobytes(),
    },
]

section_id: setup-and-data-preparation

code:
schema = {
    "index": {
        "name": "advanced_queries",
        "prefix": "products",
        "storage_type": "hash",
    },
    "fields": [
        {"name": "product_id", "type": "tag"},
        {"name": "category", "type": "tag"},
        {"name": "brief_description", "type": "text"},
        {"name": "full_description", "type": "text"},
        {"name": "price", "type": "numeric"},
        {"name": "rating", "type": "numeric"},
        {
            "name": "text_embedding",
            "type": "vector",
            "attrs": {
                "dims": 3,
                "distance_metric": "cosine",
                "algorithm": "flat",
                "datatype": "float32"
            }
        },
        {
            "name": "image_embedding",
            "type": "vector",
            "attrs": {
                "dims": 2,
                "distance_metric": "cosine",
                "algorithm": "flat",
                "datatype": "float32"
            }
        }
    ],
}

section_id: define-the-schema

code:
from redisvl.index import SearchIndex

# Create the search index
index = SearchIndex.from_dict(schema, redis_url="redis://localhost:6379")

# Create the index and load data
index.create(overwrite=True)
keys = index.load(data)

print(f"Loaded {len(keys)} products into the index")

section_id: create-index-and-load-data

code:
from redisvl.query import TextQuery

# Create a text query
text_query = TextQuery(
    text="running shoes",
    text_field_name="brief_description",
    return_fields=["product_id", "brief_description", "category", "price"],
    num_results=5
)

results = index.query(text_query)
result_print(results)

section_id: basic-text-search

code:
# BM25 standard scoring (default)
bm25_query = TextQuery(
    text="comfortable shoes",
    text_field_name="brief_description",
    text_scorer="BM25STD",
    return_fields=["product_id", "brief_description", "price"],
    num_results=3
)

print("Results with BM25 scoring:")
results = index.query(bm25_query)
result_print(results)

code:
# TFIDF scoring
tfidf_query = TextQuery(
    text="comfortable shoes",
    text_field_name="brief_description",
    text_scorer="TFIDF",
    return_fields=["product_id", "brief_description", "price"],
    num_results=3
)

print("Results with TFIDF scoring:")
results = index.query(tfidf_query)
result_print(results)

code:
from redisvl.query.filter import Tag, Num

# Search for "shoes" only in the footwear category
filtered_text_query = TextQuery(
    text="shoes",
    text_field_name="brief_description",
    filter_expression=Tag("category") == "footwear",
    return_fields=["product_id", "brief_description", "category", "price"],
    num_results=5
)

results = index.query(filtered_text_query)
result_print(results)

section_id: text-search-with-filters

code:
# Search for products under $100
price_filtered_query = TextQuery(
    text="comfortable",
    text_field_name="brief_description",
    filter_expression=Num("price") < 100,
    return_fields=["product_id", "brief_description", "price"],
    num_results=5
)

results = index.query(price_filtered_query)
result_print(results)

code:
weighted_query = TextQuery(
    text="shoes",
    text_field_name={"brief_description": 1.0, "full_description": 0.5},
    return_fields=["product_id", "brief_description"],
    num_results=3
)

results = index.query(weighted_query)
result_print(results)

code:
# Use English stopwords (default)
query_with_stopwords = TextQuery(
    text="the best shoes for running",
    text_field_name="brief_description",
    stopwords="english",  # Common words like "the", "for" will be removed
    return_fields=["product_id", "brief_description"],
    num_results=3
)

results = index.query(query_with_stopwords)
result_print(results)

section_id: text-search-with-custom-stopwords

code:
# Use custom stopwords
custom_stopwords_query = TextQuery(
    text="professional equipment for athletes",
    text_field_name="brief_description",
    stopwords=["for", "with"],  # Only these words will be filtered
    return_fields=["product_id", "brief_description"],
    num_results=3
)

results = index.query(custom_stopwords_query)
result_print(results)

code:
# No stopwords
no_stopwords_query = TextQuery(
    text="the best shoes for running",
    text_field_name="brief_description",
    stopwords=None,  # All words will be included
    return_fields=["product_id", "brief_description"],
    num_results=3
)

results = index.query(no_stopwords_query)
result_print(results)

code: hybrid_score = (alpha) * vector_score + (1 - alpha) * text_score

code:
# Create a schema with index-level stopwords disabled
from redisvl.index import SearchIndex

stopwords_schema = {
    "index": {
        "name": "company_index",
        "prefix": "company:",
        "storage_type": "hash",
        "stopwords": []  # STOPWORDS 0 - disable stopwords completely
    },
    "fields": [
        {"name": "company_name", "type": "text"},
        {"name": "description", "type": "text"}
    ]
}

# Create index using from_dict (handles schema creation internally)
company_index = SearchIndex.from_dict(stopwords_schema, redis_url="redis://localhost:6379")
company_index.create(overwrite=True, drop=True)

print(f"Index created with STOPWORDS 0: {company_index}")

section_id: index-level-stopwords-configuration

code:
# Load sample data with company names containing common stopwords
companies = [
    {"company_name": "Bank of Glasberliner", "description": "Major financial institution"},
    {"company_name": "University of Glasberliner", "description": "Public university system"},
    {"company_name": "Department of Glasberliner Affairs", "description": "A government agency"},
    {"company_name": "Glasberliner FC", "description": "Football Club"},
    {"company_name": "The Home Market", "description": "Home improvement retailer"},
]

for i, company in enumerate(companies):
    company_index.load([company], keys=[f"company:{i}"])

print(f"✓ Loaded {len(companies)} companies")

code:
# Search for "Bank of Glasberliner" - with STOPWORDS 0, "of" is indexed and searchable
from redisvl.query import FilterQuery

query = FilterQuery(
    filter_expression='@company_name:(Bank of Glasberliner)',
    return_fields=["company_name", "description"],
)

results = company_index.search(query.query, query_params=query.params)

print(f"Found {len(results.docs)} results for 'Bank of Glasberliner':")
for doc in results.docs:
    print(f"  - {doc.company_name}: {doc.description}")

code:
# Example: Create index with custom stopwords
custom_stopwords_schema = {
    "index": {
        "name": "custom_stopwords_index",
        "prefix": "custom:",
        "stopwords": ["inc", "llc", "corp"]  # Filter out legal entity suffixes
    },
    "fields": [
        {"name": "name", "type": "text"}
    ]
}

# This would create an index where "inc", "llc", "corp" are not indexed
print("Custom stopwords:", custom_stopwords_schema["index"]["stopwords"])

code:
version: '0.1.0'

index:
  name: company_index
  prefix: company:
  storage_type: hash
  stopwords: []  # Disable stopwords (STOPWORDS 0)

fields:
  - name: company_name
    type: text
  - name: description
    type: text

code:
index:
  stopwords:
    - the
    - a
    - an

code:
# Cleanup
company_index.delete(drop=True)
print("✓ Cleaned up company_index")

code:
from redisvl.query import AggregateHybridQuery

# Create a hybrid query
hybrid_query = AggregateHybridQuery(
    text="running shoes",
    text_field_name="brief_description",
    vector=[0.1, 0.2, 0.1],  # Query vector
    vector_field_name="text_embedding",
    return_fields=["product_id", "brief_description", "category", "price"],
    num_results=5
)

results = index.query(hybrid_query)
result_print(results)

section_id: basic-aggregate-hybrid-query

code:
# More emphasis on vector search (alpha=0.9)
vector_heavy_query = AggregateHybridQuery(
    text="comfortable",
    text_field_name="brief_description",
    vector=[0.15, 0.25, 0.15],
    vector_field_name="text_embedding",
    alpha=0.9,  # 90% vector, 10% text
    return_fields=["product_id", "brief_description"],
    num_results=3
)

print("Results with alpha=0.9 (vector-heavy):")
results = index.query(vector_heavy_query)
result_print(results)

section_id: adjusting-the-alpha-parameter

code:
# Hybrid search with a price filter
filtered_hybrid_query = AggregateHybridQuery(
    text="professional equipment",
    text_field_name="brief_description",
    vector=[0.9, 0.1, 0.05],
    vector_field_name="text_embedding",
    filter_expression=Num("price") > 100,
    return_fields=["product_id", "brief_description", "category", "price"],
    num_results=5
)

results = index.query(filtered_hybrid_query)
result_print(results)

section_id: aggregate-hybrid-query-with-filters

code:
# Aggregate Hybrid query with TFIDF scorer
hybrid_tfidf = AggregateHybridQuery(
    text="shoes support",
    text_field_name="brief_description",
    vector=[0.12, 0.18, 0.12],
    vector_field_name="text_embedding",
    text_scorer="TFIDF",
    return_fields=["product_id", "brief_description"],
    num_results=3
)

results = index.query(hybrid_tfidf)
result_print(results)

section_id: using-different-text-scorers

code: combined_score = w_1 * score_1 + w_2 * score_2 + w_3 * score_3 + ...

code:
from redisvl.query import MultiVectorQuery, Vector

# Define multiple vectors for the query
text_vector = Vector(
    vector=[0.1, 0.2, 0.1],
    field_name="text_embedding",
    dtype="float32",
    weight=0.7  # 70% weight for text embedding
)

image_vector = Vector(
    vector=[0.8, 0.1],
    field_name="image_embedding",
    dtype="float32",
    weight=0.3  # 30% weight for image embedding
)

# Create a multi-vector query
multi_vector_query = MultiVectorQuery(
    vectors=[text_vector, image_vector],
    return_fields=["product_id", "brief_description", "category"],
    num_results=5
)

results = index.query(multi_vector_query)
result_print(results)

section_id: basic-multi-vector-query

code:
# More emphasis on image similarity
text_vec = Vector(
    vector=[0.9, 0.1, 0.05],
    field_name="text_embedding",
    dtype="float32",
    weight=0.2  # 20% weight
)

image_vec = Vector(
    vector=[0.1, 0.9],
    field_name="image_embedding",
    dtype="float32",
    weight=0.8  # 80% weight
)

image_heavy_query = MultiVectorQuery(
    vectors=[text_vec, image_vec],
    return_fields=["product_id", "brief_description", "category"],
    num_results=3
)

print("Results with emphasis on image similarity:")
results = index.query(image_heavy_query)
result_print(results)

section_id: adjusting-vector-weights

code:
# Multi-vector search with category filter
text_vec = Vector(
    vector=[0.1, 0.2, 0.1],
    field_name="text_embedding",
    dtype="float32",
    weight=0.6
)

image_vec = Vector(
    vector=[0.8, 0.1],
    field_name="image_embedding",
    dtype="float32",
    weight=0.4
)

filtered_multi_query = MultiVectorQuery(
    vectors=[text_vec, image_vec],
    filter_expression=Tag("category") == "footwear",
    return_fields=["product_id", "brief_description", "category", "price"],
    num_results=5
)

results = index.query(filtered_multi_query)
result_print(results)

section_id: multi-vector-query-with-filters

code:
# TextQuery - keyword-based search
text_q = TextQuery(
    text="shoes",
    text_field_name="brief_description",
    return_fields=["product_id", "brief_description"],
    num_results=3
)

print("TextQuery Results (keyword-based):")
result_print(index.query(text_q))
print()

section_id: comparing-query-types

code:
# AggregateHybridQuery - combines text and vector search
hybrid_q = AggregateHybridQuery(
    text="shoes",
    text_field_name="brief_description",
    vector=[0.1, 0.2, 0.1],
    vector_field_name="text_embedding",
    return_fields=["product_id", "brief_description"],
    num_results=3
)

print("AggregateHybridQuery Results (text + vector):")
result_print(index.query(hybrid_q))
print()

code:
# MultiVectorQuery - searches multiple vector fields
mv_text = Vector(
    vector=[0.1, 0.2, 0.1],
    field_name="text_embedding",
    dtype="float32",
    weight=0.5
)

mv_image = Vector(
    vector=[0.8, 0.1],
    field_name="image_embedding",
    dtype="float32",
    weight=0.5
)

multi_q = MultiVectorQuery(
    vectors=[mv_text, mv_image],
    return_fields=["product_id", "brief_description"],
    num_results=3
)

print("MultiVectorQuery Results (multiple vectors):")
result_print(index.query(multi_q))

code:
# Cleanup
index.delete()

section_id: when-to-use-each-query-type


title: Caching Embeddings

title: Overview

text:
RedisVL provides an `EmbeddingsCache` that makes it easy to store and retrieve embedding vectors with their associated text and metadata. This cache is particularly useful for applications that frequently compute the same embeddings, enabling you to:

- Reduce computational costs by reusing previously computed embeddings
- Decrease latency in applications that rely on embeddings
- Store additional metadata alongside embeddings for richer applications

This notebook will show you how to use the `EmbeddingsCache` effectively in your applications.

title: Setup

text:
First, let's import the necessary libraries. We'll use a text embedding model from HuggingFace to generate our embeddings.

[code example]

Let's create a vectorizer to generate embeddings for our texts:

[code example]

    /Users/tyler.hutcherson/Documents/AppliedAI/redis-vl-python/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
      from .autonotebook import tqdm as notebook_tqdm

    13:06:09 sentence_transformers.SentenceTransformer INFO   Use pytorch device_name: mps
    13:06:09 sentence_transformers.SentenceTransformer INFO   Load pretrained SentenceTransformer: redis/langcache-embed-v1
    13:06:09 sentence_transformers.SentenceTransformer WARNING   You try to use a model that was created with version 4.1.0, however, your version is 3.4.1. This might cause unexpected behavior or errors. In that case, try to update to the latest version.

    Batches: 100%|██████████| 1/1 [00:00<00:00,  4.09it/s]

title: Initializing the EmbeddingsCache

text:
Now let's initialize our `EmbeddingsCache`. The cache requires a Redis connection to store the embeddings and their associated data.

[code example]

title: Basic Usage

title: Storing Embeddings

text:
Let's store some text with its embedding in the cache. The `set` method takes the following parameters:
- `text`: The input text that was embedded
- `model_name`: The name of the embedding model used
- `embedding`: The embedding vector
- `metadata`: Optional metadata associated with the embedding
- `ttl`: Optional time-to-live override for this specific entry

[code example]

    Batches: 100%|██████████| 1/1 [00:00<00:00,  3.18it/s]

    Stored with key: embedcache:909f...

title: Retrieving Embeddings

text:
To retrieve an embedding from the cache, use the `get` method with the original text and model name:

[code example]

    Found in cache: What is machine learning?
    Model: redis/langcache-embed-v1
    Metadata: {'category': 'ai', 'source': 'user_query'}
    Embedding shape: (768,)

title: Checking Existence

text:
You can check if an embedding exists in the cache without retrieving it using the `exists` method:

[code example]

    First query exists in cache: True
    New query exists in cache: False

title: Removing Entries

text:
To remove an entry from the cache, use the `drop` method:

[code example]

    After dropping: False

title: Advanced Usage

title: Key-Based Operations

text:
The `EmbeddingsCache` also provides methods that work directly with Redis keys, which can be useful for advanced use cases:

[code example]

    Stored with key: embedcache:909f...
    Exists by key: True
    Retrieved by key: What is machine learning?

title: Batch Operations

text:
When working with multiple embeddings, batch operations can significantly improve performance by reducing network roundtrips. The `EmbeddingsCache` provides methods prefixed with `m` (for "multi") that handle batches efficiently.

[code example]

    Batches: 100%|██████████| 1/1 [00:00<00:00, 21.37it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00,  9.04it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 20.84it/s]

    Stored 3 embeddings with batch operation
    All embeddings exist: True
    Retrieved 3 embeddings in one operation

Batch operations are particularly beneficial when working with large numbers of embeddings. They provide the same functionality as individual operations but with better performance by reducing network roundtrips.

For asynchronous applications, async versions of all batch methods are also available with the `am` prefix (e.g., `amset`, `amget`, `amexists`, `amdrop`).

title: Working with TTL (Time-To-Live)

text:
You can set a global TTL when initializing the cache, or specify TTL for individual entries:

[code example]

    Immediately after setting: True
    After waiting: False

You can also override the default TTL for individual entries:

[code example]

    Entry with custom TTL after 2 seconds: False
    Entry with default TTL after 2 seconds: True

title: Async Support

text:
The `EmbeddingsCache` provides async versions of all methods for use in async applications. The async methods are prefixed with `a` (e.g., `aset`, `aget`, `aexists`, `adrop`).

[code example]

    Async set successful? True
    Async get successful? True

title: Real-World Example

text:
Let's build a simple embeddings caching system for a text classification task. We'll check the cache before computing new embeddings to save computation time.

[code example]

    13:06:20 sentence_transformers.SentenceTransformer INFO   Use pytorch device_name: mps
    13:06:20 sentence_transformers.SentenceTransformer INFO   Load pretrained SentenceTransformer: redis/langcache-embed-v1
    13:06:20 sentence_transformers.SentenceTransformer WARNING   You try to use a model that was created with version 4.1.0, however, your version is 3.4.1. This might cause unexpected behavior or errors. In that case, try to update to the latest version.

    Batches: 100%|██████████| 1/1 [00:00<00:00, 21.84it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 22.04it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 22.62it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 22.71it/s]

    Statistics:
    Total queries: 5
    Cache hits: 2
    Cache misses: 3
    Cache hit rate: 40.0%

title: Performance Benchmark

role: performance

text:
Let's run benchmarks to compare the performance of embedding with and without caching, as well as batch versus individual operations.

[code example]

    Benchmarking without caching:

    Batches: 100%|██████████| 1/1 [00:00<00:00, 21.51it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.21it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.96it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.28it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 22.69it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 22.98it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.17it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 24.12it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.37it/s]
    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.24it/s]

    Time taken without caching: 0.4549 seconds
    Average time per embedding: 0.0455 seconds

    Benchmarking with caching:

    Batches: 100%|██████████| 1/1 [00:00<00:00, 23.69it/s]

    Time taken with caching: 0.0664 seconds
    Average time per embedding: 0.0066 seconds

    Performance comparison:
    Speedup with caching: 6.86x faster
    Time saved: 0.3885 seconds (85.4%)
    Latency reduction: 0.0389 seconds per query

title: Common Use Cases for Embedding Caching

text:
Embedding caching is particularly useful in the following scenarios:

1. **Search applications**: Cache embeddings for frequently searched queries to reduce latency
2. **Content recommendation systems**: Cache embeddings for content items to speed up similarity calculations
3. **API services**: Reduce costs and improve response times when generating embeddings through paid APIs
4. **Batch processing**: Speed up processing of datasets that contain duplicate texts
5. **Chatbots and virtual assistants**: Cache embeddings for common user queries to provide faster responses
6. **Development** workflows

title: Cleanup

text:
Let's clean up our caches to avoid leaving data in Redis:

[code example]

title: Summary

text:
The `EmbeddingsCache` provides an efficient way to store and retrieve embeddings with their associated text and metadata. Key features include:

- Simple API for storing and retrieving individual embeddings (`set`/`get`)
- Batch operations for working with multiple embeddings efficiently (`mset`/`mget`/`mexists`/`mdrop`)
- Support for metadata storage alongside embeddings
- Configurable time-to-live (TTL) for cache entries
- Key-based operations for advanced use cases
- Async support for use in asynchronous applications
- Significant performance improvements (15-20x faster with batch operations)

By using the `EmbeddingsCache`, you can reduce computational costs and improve the performance of applications that rely on embeddings.

code:
import os
import time
import numpy as np

# Disable tokenizers parallelism to avoid deadlocks
os.environ["TOKENIZERS_PARALLELISM"] = "False"

# Import the EmbeddingsCache
from redisvl.extensions.cache.embeddings import EmbeddingsCache
from redisvl.utils.vectorize import HFTextVectorizer

section_id: setup

code:
# Initialize the vectorizer
vectorizer = HFTextVectorizer(
    model="redis/langcache-embed-v1",
    cache_folder=os.getenv("SENTENCE_TRANSFORMERS_HOME")
)

code:
# Initialize the embeddings cache
cache = EmbeddingsCache(
    name="embedcache",                  # name prefix for Redis keys
    redis_url="redis://localhost:6379",  # Redis connection URL
    ttl=None                            # Optional TTL in seconds (None means no expiration)
)

section_id: initializing-the-embeddingscache

code:
# Text to embed
text = "What is machine learning?"
model_name = "redis/langcache-embed-v1"

# Generate the embedding
embedding = vectorizer.embed(text)

# Optional metadata
metadata = {"category": "ai", "source": "user_query"}

# Store in cache
key = cache.set(
    text=text,
    model_name=model_name,
    embedding=embedding,
    metadata=metadata
)

print(f"Stored with key: {key[:15]}...")

section_id: storing-embeddings

code:
# Retrieve from cache

if result := cache.get(text=text, model_name=model_name):
    print(f"Found in cache: {result['text']}")
    print(f"Model: {result['model_name']}")
    print(f"Metadata: {result['metadata']}")
    print(f"Embedding shape: {np.array(result['embedding']).shape}")
else:
    print("Not found in cache.")

section_id: retrieving-embeddings

code:
# Check if existing text is in cache
exists = cache.exists(text=text, model_name=model_name)
print(f"First query exists in cache: {exists}")

# Check if a new text is in cache
new_text = "What is deep learning?"
exists = cache.exists(text=new_text, model_name=model_name)
print(f"New query exists in cache: {exists}")

section_id: checking-existence

code:
# Remove from cache
cache.drop(text=text, model_name=model_name)

# Verify it's gone
exists = cache.exists(text=text, model_name=model_name)
print(f"After dropping: {exists}")

section_id: removing-entries

code:
# Store an entry again
key = cache.set(
    text=text,
    model_name=model_name,
    embedding=embedding,
    metadata=metadata
)
print(f"Stored with key: {key[:15]}...")

# Check existence by key
exists_by_key = cache.exists_by_key(key)
print(f"Exists by key: {exists_by_key}")

# Retrieve by key
result_by_key = cache.get_by_key(key)
print(f"Retrieved by key: {result_by_key['text']}")

# Drop by key
cache.drop_by_key(key)

section_id: key-based-operations

code:
# Create multiple embeddings
texts = [
    "What is machine learning?",
    "How do neural networks work?",
    "What is deep learning?"
]
embeddings = [vectorizer.embed(t) for t in texts]

# Prepare batch items as dictionaries
batch_items = [
    {
        "text": texts[0],
        "model_name": model_name,
        "embedding": embeddings[0],
        "metadata": {"category": "ai", "type": "question"}
    },
    {
        "text": texts[1],
        "model_name": model_name,
        "embedding": embeddings[1],
        "metadata": {"category": "ai", "type": "question"}
    },
    {
        "text": texts[2],
        "model_name": model_name,
        "embedding": embeddings[2],
        "metadata": {"category": "ai", "type": "question"}
    }
]

# Store multiple embeddings in one operation
keys = cache.mset(batch_items)
print(f"Stored {len(keys)} embeddings with batch operation")

# Check if multiple embeddings exist in one operation
exist_results = cache.mexists(texts, model_name)
print(f"All embeddings exist: {all(exist_results)}")

# Retrieve multiple embeddings in one operation
results = cache.mget(texts, model_name)
print(f"Retrieved {len(results)} embeddings in one operation")

# Delete multiple embeddings in one operation
cache.mdrop(texts, model_name)

# Alternative: key-based batch operations
# cache.mget_by_keys(keys)     # Retrieve by keys
# cache.mexists_by_keys(keys)  # Check existence by keys
# cache.mdrop_by_keys(keys)    # Delete by keys

section_id: batch-operations

code:
# Create a cache with a default 5-second TTL
ttl_cache = EmbeddingsCache(
    name="ttl_cache",
    redis_url="redis://localhost:6379",
    ttl=5  # 5 second TTL
)

# Store an entry
key = ttl_cache.set(
    text=text,
    model_name=model_name,
    embedding=embedding
)

# Check if it exists
exists = ttl_cache.exists_by_key(key)
print(f"Immediately after setting: {exists}")

# Wait for it to expire
time.sleep(6)

# Check again
exists = ttl_cache.exists_by_key(key)
print(f"After waiting: {exists}")

section_id: working-with-ttl-time-to-live

code:
# Store an entry with a custom 1-second TTL
key1 = ttl_cache.set(
    text="Short-lived entry",
    model_name=model_name,
    embedding=embedding,
    ttl=1  # Override with 1 second TTL
)

# Store another entry with the default TTL (5 seconds)
key2 = ttl_cache.set(
    text="Default TTL entry",
    model_name=model_name,
    embedding=embedding
    # No TTL specified = uses the default 5 seconds
)

# Wait for 2 seconds
time.sleep(2)

# Check both entries
exists1 = ttl_cache.exists_by_key(key1)
exists2 = ttl_cache.exists_by_key(key2)

print(f"Entry with custom TTL after 2 seconds: {exists1}")
print(f"Entry with default TTL after 2 seconds: {exists2}")

# Cleanup
ttl_cache.drop_by_key(key2)

code:
async def async_cache_demo():
    # Store an entry asynchronously
    key = await cache.aset(
        text="Async embedding",
        model_name=model_name,
        embedding=embedding,
        metadata={"async": True}
    )

    # Check if it exists
    exists = await cache.aexists_by_key(key)
    print(f"Async set successful? {exists}")

    # Retrieve it
    result = await cache.aget_by_key(key)
    success = result is not None and result["text"] == "Async embedding"
    print(f"Async get successful? {success}")

    # Remove it
    await cache.adrop_by_key(key)

# Run the async demo
await async_cache_demo()

section_id: async-support

code:
# Create a fresh cache for this example
example_cache = EmbeddingsCache(
    name="example_cache",
    redis_url="redis://localhost:6379",
    ttl=3600  # 1 hour TTL
)

vectorizer = HFTextVectorizer(
    model=model_name,
    cache=example_cache,
    cache_folder=os.getenv("SENTENCE_TRANSFORMERS_HOME")
)

# Simulate processing a stream of queries
queries = [
    "What is artificial intelligence?",
    "How does machine learning work?",
    "What is artificial intelligence?",  # Repeated query
    "What are neural networks?",
    "How does machine learning work?"   # Repeated query
]

# Process the queries and track statistics
total_queries = 0
cache_hits = 0

for query in queries:
    total_queries += 1

    # Check cache before computing
    before = example_cache.exists(text=query, model_name=model_name)
    if before:
        cache_hits += 1

    # Get embedding (will compute or use cache)
    embedding = vectorizer.embed(query)

# Report statistics
cache_misses = total_queries - cache_hits
hit_rate = (cache_hits / total_queries) * 100

print("\nStatistics:")
print(f"Total queries: {total_queries}")
print(f"Cache hits: {cache_hits}")
print(f"Cache misses: {cache_misses}")
print(f"Cache hit rate: {hit_rate:.1f}%")

# Cleanup
for query in set(queries):  # Use set to get unique queries
    example_cache.drop(text=query, model_name=model_name)

section_id: real-world-example

code:
# Text to use for benchmarking
benchmark_text = "This is a benchmark text to measure the performance of embedding caching."

# Create a fresh cache for benchmarking
benchmark_cache = EmbeddingsCache(
    name="benchmark_cache",
    redis_url="redis://localhost:6379",
    ttl=3600  # 1 hour TTL
)
vectorizer.cache = benchmark_cache

# Number of iterations for the benchmark
n_iterations = 10

# Benchmark without caching
print("Benchmarking without caching:")
start_time = time.time()
for _ in range(n_iterations):
    embedding = vectorizer.embed(text, skip_cache=True)
no_cache_time = time.time() - start_time
print(f"Time taken without caching: {no_cache_time:.4f} seconds")
print(f"Average time per embedding: {no_cache_time/n_iterations:.4f} seconds")

# Benchmark with caching
print("\nBenchmarking with caching:")
start_time = time.time()
for _ in range(n_iterations):
    embedding = vectorizer.embed(text)
cache_time = time.time() - start_time
print(f"Time taken with caching: {cache_time:.4f} seconds")
print(f"Average time per embedding: {cache_time/n_iterations:.4f} seconds")

# Compare performance
speedup = no_cache_time / cache_time
latency_reduction = (no_cache_time/n_iterations) - (cache_time/n_iterations)
print(f"\nPerformance comparison:")
print(f"Speedup with caching: {speedup:.2f}x faster")
print(f"Time saved: {no_cache_time - cache_time:.4f} seconds ({(1 - cache_time/no_cache_time) * 100:.1f}%)")
print(f"Latency reduction: {latency_reduction:.4f} seconds per query")

section_id: performance-benchmark

code:
# Clean up all caches
cache.clear()
ttl_cache.clear()
example_cache.clear()
benchmark_cache.clear()

section_id: cleanup