Skip to content

Commit d6e4711

Browse files
DOC-5552 update Jedis vector search example with
1 parent 2d22804 commit d6e4711

File tree

2 files changed

+189
-130
lines changed

2 files changed

+189
-130
lines changed

content/develop/clients/jedis/vecsearch.md

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ 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-
In the example below, we use the [HuggingFace](https://huggingface.co/) model
32-
[`all-mpnet-base-v2`](https://huggingface.co/sentence-transformers/all-mpnet-base-v2)
31+
The example below uses the [HuggingFace](https://huggingface.co/) model
32+
[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)
3333
to generate the vector embeddings to store and index with Redis Query Engine.
3434
The code is first demonstrated for hash documents with a
3535
separate section to explain the
@@ -53,21 +53,33 @@ dependencies to your `pom.xml` file:
5353
<dependency>
5454
<groupId>redis.clients</groupId>
5555
<artifactId>jedis</artifactId>
56-
<version>5.2.0</version>
56+
<version>6.1.0</version>
5757
</dependency>
5858
<dependency>
5959
<groupId>ai.djl.huggingface</groupId>
6060
<artifactId>tokenizers</artifactId>
61-
<version>0.24.0</version>
61+
<version>0.33.0</version>
62+
</dependency>
63+
<dependency>
64+
<groupId>ai.djl.pytorch</groupId>
65+
<artifactId>pytorch-model-zoo</artifactId>
66+
<version>0.33.0</version>
67+
</dependency>
68+
<dependency>
69+
<groupId>ai.djl</groupId>
70+
<artifactId>api</artifactId>
71+
<version>0.33.0</version>
6272
</dependency>
6373
```
6474

6575
If you are using [Gradle](https://gradle.org/), add the following
6676
dependencies to your `build.gradle` file:
6777

6878
```bash
69-
implementation 'redis.clients:jedis:5.2.0'
70-
implementation 'ai.djl.huggingface:tokenizers:0.24.0'
79+
implementation 'redis.clients:jedis:6.1.0'
80+
implementation 'ai.djl.huggingface:tokenizers:0.33.0'
81+
implementation 'ai.djl.pytorch:pytorch-model-zoo:0.33.0'
82+
implementation 'ai.djl:api:0.33.0'
7183
```
7284

7385
## Import dependencies
@@ -79,24 +91,25 @@ Import the following classes in your source file:
7991

8092
## Define a helper method
8193

82-
Our embedding model represents the vectors as an array of `long` integer values,
83-
but Redis Query Engine expects the vector components to be `float` values.
94+
The embedding model represents the vectors as an array of `float` values,
95+
which is the format required by Redis Query Engine.
8496
Also, when you store vectors in a hash object, you must encode the vector
85-
array as a `byte` string. To simplify this situation, we declare a helper
86-
method `longsToFloatsByteString()` that takes the `long` array that the
87-
embedding model returns, converts it to an array of `float` values, and
88-
then encodes the `float` array as a `byte` string:
97+
array as a `byte` string. To simplify this situation, you can declare a helper
98+
method `floatsToByteString()` that takes the `float` array that the
99+
embedding model returns and encodes it as a `byte` string:
89100

90101
{{< clients-example set="HomeQueryVec" step="helper_method" lang_filter="Java-Sync" >}}
91102
{{< /clients-example >}}
92103

93104
## Create a tokenizer instance
94105

95-
We will use the
96-
[`all-mpnet-base-v2`](https://huggingface.co/sentence-transformers/all-mpnet-base-v2)
97-
tokenizer to generate the embeddings. The vectors that represent the
98-
embeddings have 768 components, regardless of the length of the input
99-
text.
106+
The next step is to generate the embeddings using the
107+
[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)
108+
model. The vectors that represent the
109+
embeddings have 384 components, regardless of the length of the input
110+
text, but note that the input is truncated to 256
111+
tokens (see
112+
[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)
100113

101114
{{< clients-example set="HomeQueryVec" step="tokenizer" lang_filter="Java-Sync" >}}
102115
{{< /clients-example >}}
@@ -111,7 +124,7 @@ the index doesn't already exist, which is why you need the
111124
{{< clients-example set="HomeQueryVec" step="connect" lang_filter="Java-Sync" >}}
112125
{{< /clients-example >}}
113126

114-
Next, we create the index.
127+
Next, create the index.
115128
The schema in the example below includes three fields: the text content to index, a
116129
[tag]({{< relref "/develop/ai/search-and-query/advanced-concepts/tags" >}})
117130
field to represent the "genre" of the text, and the embedding vector generated from
@@ -120,10 +133,10 @@ the original text content. The `embedding` field specifies
120133
indexing, the
121134
[L2]({{< relref "/develop/ai/search-and-query/vectors#distance-metrics" >}})
122135
vector distance metric, `Float32` values to represent the vector's components,
123-
and 768 dimensions, as required by the `all-mpnet-base-v2` embedding model.
136+
and 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.
124137

125138
The `FTCreateParams` object specifies hash objects for storage and a
126-
prefix `doc:` that identifies the hash objects we want to index.
139+
prefix `doc:` that identifies the hash objects to index.
127140

128141
{{< clients-example set="HomeQueryVec" step="create_index" lang_filter="Java-Sync" >}}
129142
{{< /clients-example >}}
@@ -134,16 +147,15 @@ You can now supply the data objects, which will be indexed automatically
134147
when you add them with [`hset()`]({{< relref "/commands/hset" >}}), as long as
135148
you use the `doc:` prefix specified in the index definition.
136149

137-
Use the `encode()` method of the `sentenceTokenizer` object
150+
Use the `predict()` method of the `Predictor` object
138151
as shown below to create the embedding that represents the `content` field.
139-
The `getIds()` method that follows `encode()` obtains the vector
140-
of `long` values which we then convert to a `float` array stored as a `byte`
141-
string using our helper method. Use the `byte` string representation when you are
142-
indexing hash objects (as we are here), but use an array of `float` for
152+
The `predict()` method returns a `float[]` array which is then converted to a `byte`
153+
string using the helper method. Use the `byte` string representation when you are
154+
indexing hash objects (as we are here), but use the array of `float` directly for
143155
JSON objects (see [Differences with JSON objects](#differences-with-json-documents)
144-
below). Note that when we set the `embedding` field, we must use an overload
156+
below). Note that when you set the `embedding` field, you must use an overload
145157
of `hset()` that requires `byte` arrays for each of the key, the field name, and
146-
the value, which is why we include the `getBytes()` calls on the strings.
158+
the value, which is why you must include the `getBytes()` calls on the strings.
147159

148160
{{< clients-example set="HomeQueryVec" step="add_data" lang_filter="Java-Sync" >}}
149161
{{< /clients-example >}}
@@ -153,10 +165,10 @@ the value, which is why we include the `getBytes()` calls on the strings.
153165
After you have created the index and added the data, you are ready to run a query.
154166
To do this, you must create another embedding vector from your chosen query
155167
text. Redis calculates the vector distance between the query vector and each
156-
embedding vector in the index as it runs the query. We can request the results to be
168+
embedding vector in the index as it runs the query. You can request the results to be
157169
sorted to rank them in order of ascending distance.
158170

159-
The code below creates the query embedding using the `encode()` method, as with
171+
The code below creates the query embedding using the `predict()` method, as with
160172
the indexing, and passes it as a parameter when the query executes (see
161173
[Vector search]({{< relref "/develop/ai/search-and-query/query/vector-search" >}})
162174
for more information about using query parameters with embeddings).
@@ -170,19 +182,19 @@ search that sorts the results in order of vector distance from the query vector.
170182
Assuming you have added the code from the steps above to your source file,
171183
it is now ready to run, but note that it may take a while to complete when
172184
you run it for the first time (which happens because the tokenizer must download the
173-
`all-mpnet-base-v2` model data before it can
185+
`all-MiniLM-L6-v2` model data before it can
174186
generate the embeddings). When you run the code, it outputs the following result text:
175187

176188
```
177189
Results:
178-
ID: doc:2, Distance: 1411344, Content: That is a happy dog
179-
ID: doc:1, Distance: 9301635, Content: That is a very happy person
180-
ID: doc:3, Distance: 67178800, Content: Today is a sunny day
190+
ID: doc:1, Distance: 0.114169836044, Content: That is a very happy person
191+
ID: doc:2, Distance: 0.610845506191, Content: That is a happy dog
192+
ID: doc:3, Distance: 1.48624765873, Content: Today is a sunny day
181193
```
182194

183195
Note that the results are ordered according to the value of the `distance`
184196
field, with the lowest distance indicating the greatest similarity to the query.
185-
For this model, the text *"That is a happy dog"*
197+
As expected, the text *"That is a very happy person"*
186198
is the result judged to be most similar in meaning to the query text
187199
*"That is a happy person"*.
188200

@@ -202,12 +214,8 @@ the one created previously for hashes:
202214
{{< /clients-example >}}
203215

204216
An important difference with JSON indexing is that the vectors are
205-
specified using arrays of `float` instead of binary strings. This requires
206-
a modified version of the `longsToFloatsByteString()` method
207-
used previously:
208-
209-
{{< clients-example set="HomeQueryVec" step="json_helper_method" lang_filter="Java-Sync" >}}
210-
{{< /clients-example >}}
217+
specified using arrays of `float` instead of binary strings, so you don't need
218+
a helper method to convert the array to a binary string.
211219

212220
Use [`jsonSet()`]({{< relref "/commands/json.set" >}}) to add the data
213221
instead of [`hset()`]({{< relref "/commands/hset" >}}). Use instances
@@ -221,7 +229,7 @@ The query is almost identical to the one for the hash documents. This
221229
demonstrates how the right choice of aliases for the JSON paths can
222230
save you having to write complex queries. An important thing to notice
223231
is that the vector parameter for the query is still specified as a
224-
binary string (using the `longsToFloatsByteString()` method), even though
232+
binary string (created using the `floatsToByteString()` method), even though
225233
the data for the `embedding` field of the JSON was specified as an array.
226234

227235
{{< clients-example set="HomeQueryVec" step="json_query" lang_filter="Java-Sync" >}}
@@ -232,9 +240,9 @@ query is the same as for hash:
232240

233241
```
234242
Results:
235-
ID: jdoc:2, Distance: 1411344, Content: That is a happy dog
236-
ID: jdoc:1, Distance: 9301635, Content: That is a very happy person
237-
ID: jdoc:3, Distance: 67178800, Content: Today is a sunny day
243+
ID: jdoc:1, Distance: 0.114169836044, Content: That is a very happy person
244+
ID: jdoc:2, Distance: 0.610845506191, Content: That is a happy dog
245+
ID: jdoc:3, Distance: 1.48624765873, Content: Today is a sunny day
238246
```
239247

240248
## Learn more

0 commit comments

Comments
 (0)