Skip to content

Commit b5ba960

Browse files
authored
Implement interface for unified hybrid search in Redis 8.4.0+ (#447)
This PR adds an interface for using the new [`FT.HYBRID`](https://redis.io/docs/latest/commands/ft.hybrid) search operation introduced in Redis Open Source 8.4.0. ## Changes ### `redisvl.query.hybrid` This new module defines query constructors for working with the new hybrid search operation. Attempting to import this module without having `redis-py>=7.1.0` will raise an `ImportError`. It defines a `HybridQuery` object which can be used to define a hybrid search operation in a format largely similar to what `AggregateHybridQuery` provides. Under the hood, `redis-py` expects three separate configuration objects to run hybrid search - one for the text+vector searches, one for the score combination, and one for post-processing of the results (e.g. aggregations, loading, limiting, etc.). The `HybridQuery` class manages the definition of all three. ```python # old from redisvl.query.aggregate import AggregateHybridQuery query = AggregateHybridQuery( text="medical professional", text_field_name="description", vector=[0.1, 0.1, 0.5, ...], vector_field_name="user_embedding", alpha=0.7, # LINEAR only return_fields=["user", "job"] ) results = index.query(query) # new from redisvl.query.hybrid import HybridQuery query = HybridQuery( text="medical professional", text_field_name="description", vector=[0.1, 0.1, 0.5, ...], vector_field_name="user_embedding", combination_method="LINEAR", linear_alpha=0.3, # NOTE: The linear combination is defined inconsistently between the two approaches return_fields=["user", "job"] ) results = index.hybrid_search(query) results = await async_index.hybrid_search(query) ``` ### SearchIndex method For `redis-py>=7.1.0`, the `SearchIndex` and `AsyncSearchIndex` classes define a `hybrid_search` method that takes a `HybridQuery` instance as an argument. For `redis-py<7.1.0` the method raises a `NotImplementedError`. The method returns a list of retrieved dictionaries. ### Additional Changes - Fixed a typo in a test for `AggregateHybridQuery` - `redis-py 7.0.0` introduced a change to the type of `Query._fields`, changing it from a tuple to a list - a test had to be updated to differentiate the expectation based on the redis-py version.
1 parent 8747a3a commit b5ba960

File tree

16 files changed

+2821
-385
lines changed

16 files changed

+2821
-385
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ jobs:
9696
matrix:
9797
# 3.11 tests are run in the service-tests job
9898
python-version: ["3.9", "3.10", "3.12", "3.13"]
99-
redis-py-version: ["5.x", "6.x"]
100-
redis-version: ["6.2.6-v9", "latest", "8.0.2"]
99+
redis-py-version: ["5.x", "6.x", "7.x"]
100+
redis-version: ["6.2.6-v9", "latest", "8.4.0"]
101101
steps:
102102
- name: Check out repository
103103
uses: actions/checkout@v4
@@ -130,13 +130,15 @@ jobs:
130130
# Install right redis version based on redis py
131131
if [[ "${{ matrix.redis-py-version }}" == "5.x" ]]; then
132132
uv pip install "redis>=5,<6"
133-
else
133+
elif [[ "${{ matrix.redis-py-version }}" == "6.x" ]]; then
134134
uv pip install "redis>=6,<7"
135+
else
136+
uv pip install "redis>=7,<8"
135137
fi
136138
137139
- name: Set Redis image name
138140
run: |
139-
if [[ "${{ matrix.redis-version }}" == "8.0.2" ]]; then
141+
if [[ "${{ matrix.redis-version }}" == "8.4.0" ]]; then
140142
echo "REDIS_IMAGE=redis:${{ matrix.redis-version }}" >> $GITHUB_ENV
141143
else
142144
echo "REDIS_IMAGE=redis/redis-stack-server:${{ matrix.redis-version }}" >> $GITHUB_ENV

docs/api/query.rst

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,47 +105,72 @@ VectorRangeQuery
105105
use_search_history='AUTO' # SVS-VAMANA only
106106
)
107107
108-
HybridQuery
108+
AggregateHybridQuery
109109
================
110110

111111

112112
.. currentmodule:: redisvl.query
113113

114114

115-
.. autoclass:: HybridQuery
115+
.. autoclass:: AggregateHybridQuery
116116
:members:
117117
:inherited-members:
118118
:show-inheritance:
119119
:exclude-members: add_filter,get_args,highlight,return_field,summarize
120120

121121
.. note::
122-
The ``stopwords`` parameter in :class:`HybridQuery` (and :class:`AggregateHybridQuery`) controls query-time stopword filtering (client-side).
122+
The ``stopwords`` parameter in :class:`AggregateHybridQuery` (and :class:`HybridQuery`) controls query-time stopword filtering (client-side).
123123
For index-level stopwords configuration (server-side), see :class:`redisvl.schema.IndexInfo.stopwords`.
124124
Using query-time stopwords with index-level ``STOPWORDS 0`` is counterproductive.
125125

126+
.. note::
127+
:class:`HybridQuery` and :class:`AggregateHybridQuery` apply linear combination inconsistently. :class:`HybridQuery` uses ``linear_alpha`` to weight the text score, while :class:`AggregateHybridQuery` uses ``alpha`` to weight the vector score. When switching between the two classes, take care to revise your ``alpha`` setting.
128+
126129
.. note::
127130
**Runtime Parameters for Hybrid Queries**
128131

129132
**Important:** AggregateHybridQuery uses FT.AGGREGATE commands which do NOT support runtime parameters.
130133
Runtime parameters (``ef_runtime``, ``search_window_size``, ``use_search_history``, ``search_buffer_capacity``)
131134
are only supported with FT.SEARCH commands.
132135

133-
For runtime parameter support, use :class:`VectorQuery` or :class:`VectorRangeQuery` instead of AggregateHybridQuery.
136+
For runtime parameter support, use :class:`HybridQuery`, :class:`VectorQuery`, or :class:`VectorRangeQuery` instead of AggregateHybridQuery.
134137

135-
Example with VectorQuery (supports runtime parameters):
138+
Example with HybridQuery (supports runtime parameters):
136139

137140
.. code-block:: python
138141
139-
from redisvl.query import VectorQuery
142+
from redisvl.query import HybridQuery
140143
141-
query = VectorQuery(
144+
query = HybridQuery(
145+
text="query string",
146+
text_field_name="description",
142147
vector=[0.1, 0.2, 0.3],
143148
vector_field_name="embedding",
149+
vector_search_method="KNN",
150+
knn_ef_runtime=150, # Runtime parameters work with HybridQuery
144151
return_fields=["description"],
145152
num_results=10,
146-
ef_runtime=150 # Runtime parameters work with VectorQuery
147153
)
148154
155+
HybridQuery
156+
================
157+
158+
159+
.. currentmodule:: redisvl.query.hybrid
160+
161+
162+
.. autoclass:: HybridQuery
163+
:members:
164+
:inherited-members:
165+
:show-inheritance:
166+
167+
.. note::
168+
The ``stopwords`` parameter in :class:`HybridQuery` (and :class:`AggregateHybridQuery`) controls query-time stopword filtering (client-side).
169+
For index-level stopwords configuration (server-side), see :class:`redisvl.schema.IndexInfo.stopwords`.
170+
Using query-time stopwords with index-level ``STOPWORDS 0`` is counterproductive.
171+
172+
.. note::
173+
:class:`HybridQuery` and :class:`AggregateHybridQuery` apply linear combination inconsistently. :class:`HybridQuery` uses ``linear_alpha`` to weight the text score, while :class:`AggregateHybridQuery` uses ``alpha`` to weight the vector score. When switching between the two classes, take care to revise your ``alpha`` setting.
149174

150175
TextQuery
151176
================

docs/overview/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ There are a few ways to install RedisVL. The easiest way is to use pip.
1111

1212
## Install RedisVL with Pip
1313

14-
Install `redisvl` into your Python (>=3.8) environment using `pip`:
14+
Install `redisvl` into your Python (>=3.9) environment using `pip`:
1515

1616
```bash
1717
$ pip install -U redisvl

0 commit comments

Comments
 (0)