Skip to content

Commit b024e87

Browse files
DOC-4835 updated text and examples for redis-py
1 parent 4b786ed commit b024e87

File tree

3 files changed

+143
-132
lines changed

3 files changed

+143
-132
lines changed

content/develop/clients/redis-py/queryjson.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ categories:
1212
description: Learn how to use the Redis query engine with JSON
1313
linkTitle: Index and query JSON
1414
title: Example - Index and query JSON documents
15-
weight: 2
15+
weight: 3
1616
---
1717

1818
This example shows how to create a

content/develop/clients/redis-py/transpipe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ categories:
1212
description: Learn how to use Redis pipelines and transactions
1313
linkTitle: Pipelines/transactions
1414
title: Pipelines and transactions
15-
weight: 2
15+
weight: 5
1616
---
1717

1818
Redis lets you send a sequence of commands to the server together in a batch.

content/develop/clients/redis-py/vecsearch.md

Lines changed: 141 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ categories:
99
- oss
1010
- kubernetes
1111
- clients
12-
description: Store and query vector embeddings with Redis
13-
linkTitle: Vector embeddings
14-
title: Vector embeddings
15-
weight: 5
12+
description: Learn how to index and query vector embeddings with Redis
13+
linkTitle: Index and query vectors
14+
title: Index and query vectors
15+
weight: 4
1616
---
1717

1818
[Redis Query Engine]({{< relref "/develop/interact/search-and-query" >}})
@@ -28,196 +28,207 @@ similarity of an embedding generated from some query text with embeddings stored
2828
or JSON fields, Redis can retrieve documents that closely match the query in terms
2929
of their meaning.
3030

31-
The [RedisVL]({{< relref "/develop/clients/redis-vl" >}}) library provides a
32-
high-level Python API to help you use vector embeddings with Redis Query
33-
Engine. The example below shows how RedisVL can retrieve data
34-
that has a similar meaning to the query text you supply.
31+
In the example below, we use the
32+
[`sentence-transformers`](https://pypi.org/project/sentence-transformers/)
33+
library to generate vector embeddings to store and index with
34+
Redis Query Engine.
3535

3636
## Initialize
3737

38-
Start by importing the required classes:
38+
Install [`redis-py`]({{< relref "/develop/clients/redis-py" >}}) if you
39+
have not already done so. Also, install `sentence-transformers` with the
40+
following command:
41+
42+
```bash
43+
pip install sentence-transformers
44+
```
45+
46+
In a new Python source file, start by importing the required classes:
3947

4048
```python
41-
from redisvl.utils.vectorize.text.huggingface import (
42-
HFTextVectorizer
43-
)
44-
from redisvl.index import SearchIndex
45-
from redisvl.query import VectorQuery
49+
from sentence_transformers import SentenceTransformer
50+
from redis.commands.search.query import Query
51+
from redis.commands.search.field import TextField, TagField, VectorField
52+
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
53+
54+
import numpy as np
55+
import redis
4656
```
4757

4858
The first of these imports is the
49-
[`HFTextVectorizer`](https://docs.redisvl.com/en/stable/api/vectorizer.html#hftextvectorizer)
50-
class, which generates
51-
an embedding from a section sample of text. Here, we create an instance of `HFTextVectorizer`
52-
that uses the
59+
`SentenceTransformer` class, which generates an embedding from a section of text.
60+
Here, we create an instance of `SentenceTransformer` that uses the
5361
[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)
5462
model for the embeddings. This model generates vectors with 384 dimensions, regardless
5563
of the length of the input text, but note that the input is truncated to 256
5664
tokens (see
5765
[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)
58-
at the [Hugging Face](https://huggingface.co/) docs to learn more about how tokens
66+
at the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens
5967
are related to the original text).
6068

6169
```python
62-
hf = HFTextVectorizer(model="sentence-transformers/all-MiniLM-L6-v2")
70+
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
6371
```
6472

6573
## Create the index
6674

67-
RedisVL's
68-
[`SearchIndex`](https://docs.redisvl.com/en/stable/api/searchindex.html#searchindex-api)
69-
class provides the
70-
[`from_dict()`](https://docs.redisvl.com/en/stable/api/searchindex.html#redisvl.index.SearchIndex.from_dict)
71-
method, which lets you specify your index schema with a Python dictionary, as shown
72-
below. Another option is
73-
[`from_yaml()`](https://docs.redisvl.com/en/stable/api/searchindex.html#redisvl.index.SearchIndex.from_yaml),
74-
which loads the index schema from a [YAML](https://en.wikipedia.org/wiki/YAML) file.
75+
Connect to Redis and delete any index previously created with the
76+
name `vector_idx`. (The `dropindex()` call throws an exception if
77+
the index doesn't already exist, which is why you need the
78+
`try: except:` block.)
79+
80+
```python
81+
r = redis.Redis(decode_responses=True)
7582

83+
try:
84+
r.ft("vector_idx").dropindex(True)
85+
except redis.exceptions.ResponseError:
86+
pass
87+
```
88+
89+
Next, we create the index.
7690
The schema in the example below specifies hash objects for storage and includes
7791
three fields: the text content to index, a
7892
[tag]({{< relref "/develop/interact/search-and-query/advanced-concepts/tags" >}})
7993
field to represent the "genre" of the text, and the embedding vector generated from
80-
the original text content. The attributes of the vector field (specified in the
81-
`attrs` object) specify [HNSW]({{< relref "/develop/interact/search-and-query/advanced-concepts/vectors#hnsw-index" >}}) indexing, the
94+
the original text content. The `embedding` field specifies
95+
[HNSW]({{< relref "/develop/interact/search-and-query/advanced-concepts/vectors#hnsw-index" >}})
96+
indexing, the
8297
[L2]({{< relref "/develop/interact/search-and-query/advanced-concepts/vectors#distance-metrics" >}})
8398
vector distance metric, `Float32` values to represent the vector's components,
8499
and 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.
85100

86101
```python
87-
index = SearchIndex.from_dict({
88-
"index": {
89-
"name": "vector_idx",
90-
"prefix": "doc",
91-
"storage_type": "hash",
92-
},
93-
"fields": [
94-
{"name": "content", "type": "text"},
95-
{"name": "genre", "type": "tag"},
96-
{
97-
"name": "embedding",
98-
"type": "vector",
99-
"attrs": {
100-
"algorithm": "HNSW",
101-
"dims": 384,
102-
"distance_metric": "L2",
103-
"datatype": "FLOAT32",
104-
},
105-
},
106-
],
107-
})
108-
```
109-
110-
When you have created the `SearchIndex` object, you must connect to the Redis
111-
server using the
112-
[`connect()`](https://docs.redisvl.com/en/stable/api/searchindex.html#redisvl.index.SearchIndex.connect)
113-
method and then use the
114-
[`create()`](https://docs.redisvl.com/en/stable/api/searchindex.html#redisvl.index.SearchIndex.create)
115-
method to create the index in the database. The `overwrite` parameter for `create()`
116-
ensures that the index you create replaces any existing index with
117-
the same name. The `drop` parameter deletes any objects that were
118-
indexed by the index you are replacing.
102+
schema = (
103+
TextField("content"),
104+
TagField("genre"),
105+
VectorField("embedding", "HNSW", {
106+
"TYPE": "FLOAT32",
107+
"DIM": 384,
108+
"DISTANCE_METRIC":"L2"
109+
})
110+
)
119111

120-
```python
121-
index.connect("redis://localhost:6379")
122-
index.create(overwrite=True, drop=True)
112+
r.ft("vector_idx").create_index(
113+
schema,
114+
definition=IndexDefinition(
115+
prefix=["doc:"], index_type=IndexType.HASH
116+
)
117+
)
123118
```
124119

125120
## Add data
126121

127122
You can now supply the data objects, which will be indexed automatically
128-
when you add them using the
129-
[`load()`](https://docs.redisvl.com/en/stable/api/searchindex.html#redisvl.index.SearchIndex.load)
130-
method. Use the
131-
[`embed()`](https://docs.redisvl.com/en/stable/api/vectorizer.html#redisvl.utils.vectorize.text.huggingface.HFTextVectorizer.embed)
132-
method of the `HFTextVectorizer` instance to create the embedding that represents the `content` field. By default, `embed()` returns a list of `float` values to represent the embedding
133-
vector, but you can also supply the `as_buffer` parameter to encode this list as a
134-
binary string. Use the string representation when you are indexing hash objects
123+
when you add them with [`hset()`]({{< relref "/commands/hset" >}}), as long as
124+
you use the `doc:` prefix specified in the index definition.
125+
126+
Use the `model.encode()` method of `SentenceTransformer`
127+
as shown below to create the embedding that represents the `content` field.
128+
The `astype()` option that follows the `model.encode()` call specifies that
129+
we want a vector of `float32` values. The `tobytes()` option encodes the
130+
vector components together as a single binary string rather than the
131+
default Python list of `float` values.
132+
Use the binary string representation when you are indexing hash objects
135133
(as we are here), but use the default list of `float` for JSON objects.
136134

137135
```python
138-
hf = HFTextVectorizer(model="sentence-transformers/all-MiniLM-L6-v2")
139-
140-
data = [
141-
{
142-
"content": "That is a very happy person",
143-
"genre": "persons",
144-
"embedding": hf.embed("That is a very happy person", as_buffer=True)
145-
},
146-
{
147-
"content": "That is a happy dog",
148-
"genre": "pets",
149-
"embedding": hf.embed("That is a happy dog", as_buffer=True)
150-
},
151-
{
152-
"content": "Today is a sunny day",
153-
"genre": "weather",
154-
"embedding": hf.embed("Today is a sunny day", as_buffer=True)
155-
}
156-
]
136+
content = "That is a very happy person"
137+
138+
r.hset("doc:0", mapping={
139+
"content": content,
140+
"genre": "persons",
141+
"embedding": model.encode(content).astype(np.float32).tobytes(),
142+
})
143+
144+
content = "That is a happy dog"
145+
146+
r.hset("doc:1", mapping={
147+
"content": content,
148+
"genre": "pets",
149+
"embedding": model.encode(content).astype(np.float32).tobytes(),
150+
})
151+
152+
content = "Today is a sunny day"
157153

158-
index.load(data)
154+
r.hset("doc:2", mapping={
155+
"content": content,
156+
"genre": "weather",
157+
"embedding": model.encode(content).astype(np.float32).tobytes(),
158+
})
159159
```
160160

161161
## Run a query
162162

163-
After you have created the index, you are ready to run a query against it.
163+
After you have created the index and added the data, you are ready to run a query.
164164
To do this, you must create another embedding vector from your chosen query
165165
text. Redis calculates the similarity between the query vector and each
166166
embedding vector in the index as it runs the query. It then ranks the
167167
results in order of this numeric similarity value.
168168

169-
Create a
170-
[`VectorQuery`](https://docs.redisvl.com/en/stable/api/query.html#vectorquery)
171-
instance by supplying the embedding vector for your
172-
query text, along with the hash or JSON field to match against and the number
173-
of results you want, as shown in the example below. Then, call the
174-
[`query()`](https://docs.redisvl.com/en/stable/api/searchindex.html#redisvl.index.SearchIndex.query)
175-
method of `SearchIndex` with your `VectorQuery` instance to run the query.
169+
The code below creates the query embedding using `model.encode()`, as with
170+
the indexing, and passes it as a parameter when the query executes
171+
(see
172+
[Vector search]({{< relref "/develop/interact/search-and-query/query/vector-search" >}})
173+
for more information about using query parameters with embeddings).
176174

177175
```python
178-
query = VectorQuery(
179-
vector=hf.embed("That is a happy person"),
180-
vector_field_name="embedding",
181-
return_fields=["content"],
182-
num_results=3,
176+
q = Query(
177+
"*=>[KNN 3 @embedding $vec AS vector_distance]"
178+
).return_field("score").dialect(2)
179+
180+
query_text = "That is a happy person"
181+
182+
res = r.ft("vector_idx").search(
183+
q, query_params={
184+
"vec": model.encode(query_text).astype(np.float32).tobytes()
185+
}
183186
)
184187

185-
results = index.query(query)
186-
print(results)
188+
print(res)
187189
```
188190

189191
The code is now ready to run, but note that it may take a while to complete when
190192
you run it for the first time (which happens because RedisVL must download the
191193
`all-MiniLM-L6-v2` model data before it can
192-
generate the embeddings). When you run the code, it outputs the following results
193-
as a list of dict objects:
194+
generate the embeddings). When you run the code, it outputs the following result
195+
object (slightly formatted here for clarity):
194196

195197
```Python
196-
[
197-
{
198-
'id': 'doc:cf3c28e7fdd44753af3080a9a7f8d8e9',
199-
'vector_distance': '0.114169985056',
200-
'content': 'That is a very happy person'
201-
},
202-
{
203-
'id': 'doc:614508f297b644d0be47dde5373bb059',
204-
'vector_distance': '0.610845386982',
205-
'content': 'That is a happy dog'
206-
},
207-
{
208-
'id': 'doc:930a7dfca0d74808baee490d747c9534',
209-
'vector_distance': '1.48624813557',
210-
'content': 'Today is a sunny day'
211-
}
212-
]
198+
Result{
199+
3 total,
200+
docs: [
201+
Document {
202+
'id': 'doc:0',
203+
'payload': None,
204+
'vector_distance': '0.114169985056',
205+
'content': 'That is a very happy person'
206+
},
207+
Document {
208+
'id': 'doc:1',
209+
'payload': None,
210+
'vector_distance': '0.610845386982',
211+
'content': 'That is a happy dog'
212+
},
213+
Document {
214+
'id': 'doc:2',
215+
'payload': None,
216+
'vector_distance': '1.48624813557',
217+
'content': 'Today is a sunny day'
218+
}
219+
]
220+
}
213221
```
214222

215223
Note that the results are ordered according to the value of the `vector_distance`
216224
field, with the lowest distance indicating the greatest similarity to the query.
217-
As you would expect, the text *"That is a very happy person"* is the result that is
218-
most similar in meaning to the query text *"That is a happy person"*.
225+
As you would expect, the result for `doc:0` with the content text *"That is a very happy person"*
226+
is the result that is most similar in meaning to the query text
227+
*"That is a happy person"*.
219228

220229
## Learn more
221230

222-
See the [RedisVL documentation](https://docs.redisvl.com/en/stable/index.html)
223-
for more details about its features and examples of how to use them.
231+
See
232+
[Vector search]({{< relref "/develop/interact/search-and-query/query/vector-search" >}})
233+
for more information about the indexing options, distance metrics, and query format
234+
for vectors.

0 commit comments

Comments
 (0)