1 package redis_test
2
3 import (
4 "context"
5 "fmt"
6
7 . "github.com/bsm/ginkgo/v2"
8 . "github.com/bsm/gomega"
9 "github.com/redis/go-redis/v9"
10 )
11
12 var _ = Describe("RediSearch Builders", Label("search", "builders"), func() {
13 ctx := context.Background()
14 var client *redis.Client
15
16 BeforeEach(func() {
17 client = redis.NewClient(&redis.Options{Addr: ":6379", Protocol: 2})
18 Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
19 })
20
21 AfterEach(func() {
22 expectCloseErr := client.Close()
23 Expect(expectCloseErr).NotTo(HaveOccurred())
24 })
25
26 It("should create index and search with scores using builders", Label("search", "ftcreate", "ftsearch"), func() {
27 createVal, err := client.NewCreateIndexBuilder(ctx, "idx1").
28 OnHash().
29 Schema(&redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}).
30 Run()
31 Expect(err).NotTo(HaveOccurred())
32 Expect(createVal).To(Equal("OK"))
33
34 WaitForIndexing(client, "idx1")
35
36 client.HSet(ctx, "doc1", "foo", "hello world")
37 client.HSet(ctx, "doc2", "foo", "hello redis")
38
39 res, err := client.NewSearchBuilder(ctx, "idx1", "hello").WithScores().Run()
40 Expect(err).NotTo(HaveOccurred())
41 Expect(res.Total).To(Equal(2))
42 for _, doc := range res.Docs {
43 Expect(*doc.Score).To(BeNumerically(">", 0))
44 }
45 })
46
47 It("should aggregate using builders", Label("search", "ftaggregate"), func() {
48 _, err := client.NewCreateIndexBuilder(ctx, "idx2").
49 OnHash().
50 Schema(&redis.FieldSchema{FieldName: "n", FieldType: redis.SearchFieldTypeNumeric}).
51 Run()
52 Expect(err).NotTo(HaveOccurred())
53 WaitForIndexing(client, "idx2")
54
55 client.HSet(ctx, "d1", "n", 1)
56 client.HSet(ctx, "d2", "n", 2)
57
58 agg, err := client.NewAggregateBuilder(ctx, "idx2", "*").
59 GroupBy("@n").
60 ReduceAs(redis.SearchCount, "count").
61 Run()
62 Expect(err).NotTo(HaveOccurred())
63 Expect(len(agg.Rows)).To(Equal(2))
64 })
65
66 It("should drop index using builder", Label("search", "ftdropindex"), func() {
67 Expect(client.NewCreateIndexBuilder(ctx, "idx3").
68 OnHash().
69 Schema(&redis.FieldSchema{FieldName: "x", FieldType: redis.SearchFieldTypeText}).
70 Run()).To(Equal("OK"))
71 WaitForIndexing(client, "idx3")
72
73 dropVal, err := client.NewDropIndexBuilder(ctx, "idx3").Run()
74 Expect(err).NotTo(HaveOccurred())
75 Expect(dropVal).To(Equal("OK"))
76 })
77
78 It("should manage aliases using builder", Label("search", "ftalias"), func() {
79 Expect(client.NewCreateIndexBuilder(ctx, "idx4").
80 OnHash().
81 Schema(&redis.FieldSchema{FieldName: "t", FieldType: redis.SearchFieldTypeText}).
82 Run()).To(Equal("OK"))
83 WaitForIndexing(client, "idx4")
84
85 addVal, err := client.NewAliasBuilder(ctx, "alias1").Add("idx4").Run()
86 Expect(err).NotTo(HaveOccurred())
87 Expect(addVal).To(Equal("OK"))
88
89 _, err = client.NewSearchBuilder(ctx, "alias1", "*").Run()
90 Expect(err).NotTo(HaveOccurred())
91
92 delVal, err := client.NewAliasBuilder(ctx, "alias1").Del().Run()
93 Expect(err).NotTo(HaveOccurred())
94 Expect(delVal).To(Equal("OK"))
95 })
96
97 It("should explain query using ExplainBuilder", Label("search", "builders", "ftexplain"), func() {
98 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_explain").
99 OnHash().
100 Schema(&redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}).
101 Run()
102 Expect(err).NotTo(HaveOccurred())
103 Expect(createVal).To(Equal("OK"))
104 WaitForIndexing(client, "idx_explain")
105
106 expl, err := client.NewExplainBuilder(ctx, "idx_explain", "foo").Run()
107 Expect(err).NotTo(HaveOccurred())
108 Expect(expl).To(ContainSubstring("UNION"))
109 })
110
111 It("should retrieve info using SearchInfo builder", Label("search", "builders", "ftinfo"), func() {
112 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_info").
113 OnHash().
114 Schema(&redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}).
115 Run()
116 Expect(err).NotTo(HaveOccurred())
117 Expect(createVal).To(Equal("OK"))
118 WaitForIndexing(client, "idx_info")
119
120 i, err := client.NewSearchInfoBuilder(ctx, "idx_info").Run()
121 Expect(err).NotTo(HaveOccurred())
122 Expect(i.IndexName).To(Equal("idx_info"))
123 })
124
125 It("should spellcheck using builder", Label("search", "builders", "ftspellcheck"), func() {
126 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_spell").
127 OnHash().
128 Schema(&redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}).
129 Run()
130 Expect(err).NotTo(HaveOccurred())
131 Expect(createVal).To(Equal("OK"))
132 WaitForIndexing(client, "idx_spell")
133
134 client.HSet(ctx, "doc1", "foo", "bar")
135
136 _, err = client.NewSpellCheckBuilder(ctx, "idx_spell", "ba").Distance(1).Run()
137 Expect(err).NotTo(HaveOccurred())
138 })
139
140 It("should manage dictionary using DictBuilder", Label("search", "ftdict"), func() {
141 addCount, err := client.NewDictBuilder(ctx, "dict1").Add("a", "b").Run()
142 Expect(err).NotTo(HaveOccurred())
143 Expect(addCount).To(Equal(int64(2)))
144
145 dump, err := client.NewDictBuilder(ctx, "dict1").Dump().Run()
146 Expect(err).NotTo(HaveOccurred())
147 Expect(dump).To(ContainElements("a", "b"))
148
149 delCount, err := client.NewDictBuilder(ctx, "dict1").Del("a").Run()
150 Expect(err).NotTo(HaveOccurred())
151 Expect(delCount).To(Equal(int64(1)))
152 })
153
154 It("should tag values using TagValsBuilder", Label("search", "builders", "fttagvals"), func() {
155 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_tag").
156 OnHash().
157 Schema(&redis.FieldSchema{FieldName: "tags", FieldType: redis.SearchFieldTypeTag}).
158 Run()
159 Expect(err).NotTo(HaveOccurred())
160 Expect(createVal).To(Equal("OK"))
161 WaitForIndexing(client, "idx_tag")
162
163 client.HSet(ctx, "doc1", "tags", "red,blue")
164 client.HSet(ctx, "doc2", "tags", "green,blue")
165
166 vals, err := client.NewTagValsBuilder(ctx, "idx_tag", "tags").Run()
167 Expect(err).NotTo(HaveOccurred())
168 Expect(vals).To(BeAssignableToTypeOf([]string{}))
169 })
170
171 It("should cursor read and delete using CursorBuilder", Label("search", "builders", "ftcursor"), func() {
172 Expect(client.NewCreateIndexBuilder(ctx, "idx5").
173 OnHash().
174 Schema(&redis.FieldSchema{FieldName: "f", FieldType: redis.SearchFieldTypeText}).
175 Run()).To(Equal("OK"))
176 WaitForIndexing(client, "idx5")
177 client.HSet(ctx, "doc1", "f", "hello")
178 client.HSet(ctx, "doc2", "f", "world")
179
180 cursorBuilder := client.NewCursorBuilder(ctx, "idx5", 1)
181 Expect(cursorBuilder).NotTo(BeNil())
182
183 cursorBuilder = cursorBuilder.Count(10)
184 Expect(cursorBuilder).NotTo(BeNil())
185
186 delBuilder := client.NewCursorBuilder(ctx, "idx5", 1)
187 Expect(delBuilder).NotTo(BeNil())
188 })
189
190 It("should update synonyms using SynUpdateBuilder", Label("search", "builders", "ftsynupdate"), func() {
191 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_syn").
192 OnHash().
193 Schema(&redis.FieldSchema{FieldName: "foo", FieldType: redis.SearchFieldTypeText}).
194 Run()
195 Expect(err).NotTo(HaveOccurred())
196 Expect(createVal).To(Equal("OK"))
197 WaitForIndexing(client, "idx_syn")
198
199 syn, err := client.NewSynUpdateBuilder(ctx, "idx_syn", "grp1").Terms("a", "b").Run()
200 Expect(err).NotTo(HaveOccurred())
201 Expect(syn).To(Equal("OK"))
202 })
203
204 It("should test SearchBuilder with NoContent and Verbatim", Label("search", "ftsearch", "builders"), func() {
205 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_nocontent").
206 OnHash().
207 Schema(&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Weight: 5}).
208 Schema(&redis.FieldSchema{FieldName: "body", FieldType: redis.SearchFieldTypeText}).
209 Run()
210 Expect(err).NotTo(HaveOccurred())
211 Expect(createVal).To(Equal("OK"))
212 WaitForIndexing(client, "idx_nocontent")
213
214 client.HSet(ctx, "doc1", "title", "RediSearch", "body", "Redisearch implements a search engine on top of redis")
215
216 res, err := client.NewSearchBuilder(ctx, "idx_nocontent", "search engine").
217 NoContent().
218 Verbatim().
219 Limit(0, 5).
220 Run()
221 Expect(err).NotTo(HaveOccurred())
222 Expect(res.Total).To(Equal(1))
223 Expect(res.Docs[0].ID).To(Equal("doc1"))
224
225 Expect(res.Docs[0].Fields).To(BeEmpty())
226 })
227
228 It("should test SearchBuilder with NoStopWords", Label("search", "ftsearch", "builders"), func() {
229 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_nostop").
230 OnHash().
231 Schema(&redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).
232 Run()
233 Expect(err).NotTo(HaveOccurred())
234 Expect(createVal).To(Equal("OK"))
235 WaitForIndexing(client, "idx_nostop")
236
237 client.HSet(ctx, "doc1", "txt", "hello world")
238 client.HSet(ctx, "doc2", "txt", "test document")
239
240
241 res, err := client.NewSearchBuilder(ctx, "idx_nostop", "hello").NoContent().NoStopWords().Run()
242 Expect(err).NotTo(HaveOccurred())
243 Expect(res.Total).To(Equal(1))
244 })
245
246 It("should test SearchBuilder with filters", Label("search", "ftsearch", "builders"), func() {
247 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_filters").
248 OnHash().
249 Schema(&redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).
250 Schema(&redis.FieldSchema{FieldName: "num", FieldType: redis.SearchFieldTypeNumeric}).
251 Schema(&redis.FieldSchema{FieldName: "loc", FieldType: redis.SearchFieldTypeGeo}).
252 Run()
253 Expect(err).NotTo(HaveOccurred())
254 Expect(createVal).To(Equal("OK"))
255 WaitForIndexing(client, "idx_filters")
256
257 client.HSet(ctx, "doc1", "txt", "foo bar", "num", 3.141, "loc", "-0.441,51.458")
258 client.HSet(ctx, "doc2", "txt", "foo baz", "num", 2, "loc", "-0.1,51.2")
259
260
261 res1, err := client.NewSearchBuilder(ctx, "idx_filters", "foo").
262 Filter("num", 2, 4).
263 NoContent().
264 Run()
265 Expect(err).NotTo(HaveOccurred())
266 Expect(res1.Total).To(Equal(2))
267
268
269 res2, err := client.NewSearchBuilder(ctx, "idx_filters", "foo").
270 GeoFilter("loc", -0.44, 51.45, 10, "km").
271 NoContent().
272 Run()
273 Expect(err).NotTo(HaveOccurred())
274 Expect(res2.Total).To(Equal(1))
275 })
276
277 It("should test SearchBuilder with sorting", Label("search", "ftsearch", "builders"), func() {
278 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_sort").
279 OnHash().
280 Schema(&redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).
281 Schema(&redis.FieldSchema{FieldName: "num", FieldType: redis.SearchFieldTypeNumeric, Sortable: true}).
282 Run()
283 Expect(err).NotTo(HaveOccurred())
284 Expect(createVal).To(Equal("OK"))
285 WaitForIndexing(client, "idx_sort")
286
287 client.HSet(ctx, "doc1", "txt", "foo bar", "num", 1)
288 client.HSet(ctx, "doc2", "txt", "foo baz", "num", 2)
289 client.HSet(ctx, "doc3", "txt", "foo qux", "num", 3)
290
291
292 res1, err := client.NewSearchBuilder(ctx, "idx_sort", "foo").
293 SortBy("num", true).
294 NoContent().
295 Run()
296 Expect(err).NotTo(HaveOccurred())
297 Expect(res1.Total).To(Equal(3))
298 Expect(res1.Docs[0].ID).To(Equal("doc1"))
299 Expect(res1.Docs[1].ID).To(Equal("doc2"))
300 Expect(res1.Docs[2].ID).To(Equal("doc3"))
301
302
303 res2, err := client.NewSearchBuilder(ctx, "idx_sort", "foo").
304 SortBy("num", false).
305 NoContent().
306 Run()
307 Expect(err).NotTo(HaveOccurred())
308 Expect(res2.Total).To(Equal(3))
309 Expect(res2.Docs[0].ID).To(Equal("doc3"))
310 Expect(res2.Docs[1].ID).To(Equal("doc2"))
311 Expect(res2.Docs[2].ID).To(Equal("doc1"))
312 })
313
314 It("should test SearchBuilder with InKeys and InFields", Label("search", "ftsearch", "builders"), func() {
315 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_in").
316 OnHash().
317 Schema(&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText}).
318 Schema(&redis.FieldSchema{FieldName: "body", FieldType: redis.SearchFieldTypeText}).
319 Run()
320 Expect(err).NotTo(HaveOccurred())
321 Expect(createVal).To(Equal("OK"))
322 WaitForIndexing(client, "idx_in")
323
324 client.HSet(ctx, "doc1", "title", "hello world", "body", "lorem ipsum")
325 client.HSet(ctx, "doc2", "title", "foo bar", "body", "hello world")
326 client.HSet(ctx, "doc3", "title", "baz qux", "body", "dolor sit")
327
328
329 res1, err := client.NewSearchBuilder(ctx, "idx_in", "hello").
330 InKeys("doc1", "doc2").
331 NoContent().
332 Run()
333 Expect(err).NotTo(HaveOccurred())
334 Expect(res1.Total).To(Equal(2))
335
336
337 res2, err := client.NewSearchBuilder(ctx, "idx_in", "hello").
338 InFields("title").
339 NoContent().
340 Run()
341 Expect(err).NotTo(HaveOccurred())
342 Expect(res2.Total).To(Equal(1))
343 Expect(res2.Docs[0].ID).To(Equal("doc1"))
344 })
345
346 It("should test SearchBuilder with Return fields", Label("search", "ftsearch", "builders"), func() {
347 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_return").
348 OnHash().
349 Schema(&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText}).
350 Schema(&redis.FieldSchema{FieldName: "body", FieldType: redis.SearchFieldTypeText}).
351 Schema(&redis.FieldSchema{FieldName: "num", FieldType: redis.SearchFieldTypeNumeric}).
352 Run()
353 Expect(err).NotTo(HaveOccurred())
354 Expect(createVal).To(Equal("OK"))
355 WaitForIndexing(client, "idx_return")
356
357 client.HSet(ctx, "doc1", "title", "hello", "body", "world", "num", 42)
358
359
360 res1, err := client.NewSearchBuilder(ctx, "idx_return", "hello").
361 ReturnFields("title", "num").
362 Run()
363 Expect(err).NotTo(HaveOccurred())
364 Expect(res1.Total).To(Equal(1))
365 Expect(res1.Docs[0].Fields).To(HaveKey("title"))
366 Expect(res1.Docs[0].Fields).To(HaveKey("num"))
367 Expect(res1.Docs[0].Fields).NotTo(HaveKey("body"))
368
369
370 res2, err := client.NewSearchBuilder(ctx, "idx_return", "hello").
371 ReturnAs("title", "doc_title").
372 Run()
373 Expect(err).NotTo(HaveOccurred())
374 Expect(res2.Total).To(Equal(1))
375 Expect(res2.Docs[0].Fields).To(HaveKey("doc_title"))
376 Expect(res2.Docs[0].Fields).NotTo(HaveKey("title"))
377 })
378
379 It("should test SearchBuilder with advanced options", Label("search", "ftsearch", "builders"), func() {
380 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_advanced").
381 OnHash().
382 Schema(&redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText}).
383 Run()
384 Expect(err).NotTo(HaveOccurred())
385 Expect(createVal).To(Equal("OK"))
386 WaitForIndexing(client, "idx_advanced")
387
388 client.HSet(ctx, "doc1", "description", "The quick brown fox jumps over the lazy dog")
389 client.HSet(ctx, "doc2", "description", "Quick alice was beginning to get very tired of sitting by her quick sister on the bank")
390
391
392 res1, err := client.NewSearchBuilder(ctx, "idx_advanced", "quick").
393 WithScores().
394 Scorer("TFIDF").
395 Run()
396 Expect(err).NotTo(HaveOccurred())
397 Expect(res1.Total).To(Equal(2))
398 for _, doc := range res1.Docs {
399 Expect(*doc.Score).To(BeNumerically(">", 0))
400 }
401
402 res2, err := client.NewSearchBuilder(ctx, "idx_advanced", "quick").
403 WithScores().
404 Payload("test_payload").
405 NoContent().
406 Run()
407 Expect(err).NotTo(HaveOccurred())
408 Expect(res2.Total).To(Equal(2))
409
410
411 res3, err := client.NewSearchBuilder(ctx, "idx_advanced", "quick brown").
412 Slop(1).
413 InOrder().
414 NoContent().
415 Run()
416 Expect(err).NotTo(HaveOccurred())
417 Expect(res3.Total).To(Equal(1))
418
419
420 res4, err := client.NewSearchBuilder(ctx, "idx_advanced", "quick").
421 Language("english").
422 Expander("SYNONYM").
423 NoContent().
424 Run()
425 Expect(err).NotTo(HaveOccurred())
426 Expect(res4.Total).To(BeNumerically(">=", 0))
427
428
429 res5, err := client.NewSearchBuilder(ctx, "idx_advanced", "quick").
430 Timeout(1000).
431 NoContent().
432 Run()
433 Expect(err).NotTo(HaveOccurred())
434 Expect(res5.Total).To(Equal(2))
435 })
436
437 It("should test SearchBuilder with Params and Dialect", Label("search", "ftsearch", "builders"), func() {
438 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_params").
439 OnHash().
440 Schema(&redis.FieldSchema{FieldName: "name", FieldType: redis.SearchFieldTypeText}).
441 Run()
442 Expect(err).NotTo(HaveOccurred())
443 Expect(createVal).To(Equal("OK"))
444 WaitForIndexing(client, "idx_params")
445
446 client.HSet(ctx, "doc1", "name", "Alice")
447 client.HSet(ctx, "doc2", "name", "Bob")
448 client.HSet(ctx, "doc3", "name", "Carol")
449
450
451 res1, err := client.NewSearchBuilder(ctx, "idx_params", "@name:$name").
452 Param("name", "Alice").
453 NoContent().
454 Run()
455 Expect(err).NotTo(HaveOccurred())
456 Expect(res1.Total).To(Equal(1))
457 Expect(res1.Docs[0].ID).To(Equal("doc1"))
458
459
460 params := map[string]interface{}{
461 "name1": "Bob",
462 "name2": "Carol",
463 }
464 res2, err := client.NewSearchBuilder(ctx, "idx_params", "@name:($name1|$name2)").
465 ParamsMap(params).
466 Dialect(2).
467 NoContent().
468 Run()
469 Expect(err).NotTo(HaveOccurred())
470 Expect(res2.Total).To(Equal(2))
471 })
472
473 It("should test SearchBuilder with Limit and CountOnly", Label("search", "ftsearch", "builders"), func() {
474 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_limit").
475 OnHash().
476 Schema(&redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).
477 Run()
478 Expect(err).NotTo(HaveOccurred())
479 Expect(createVal).To(Equal("OK"))
480 WaitForIndexing(client, "idx_limit")
481
482 for i := 1; i <= 10; i++ {
483 client.HSet(ctx, fmt.Sprintf("doc%d", i), "txt", "test document")
484 }
485
486
487 res1, err := client.NewSearchBuilder(ctx, "idx_limit", "test").
488 Limit(2, 3).
489 NoContent().
490 Run()
491 Expect(err).NotTo(HaveOccurred())
492 Expect(res1.Total).To(Equal(10))
493 Expect(len(res1.Docs)).To(Equal(3))
494
495
496 res2, err := client.NewSearchBuilder(ctx, "idx_limit", "test").
497 CountOnly().
498 Run()
499 Expect(err).NotTo(HaveOccurred())
500 Expect(res2.Total).To(Equal(10))
501 Expect(len(res2.Docs)).To(Equal(0))
502 })
503
504 It("should test SearchBuilder with WithSortByCount and SortBy", Label("search", "ftsearch", "builders"), func() {
505 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_payloads").
506 OnHash().
507 Schema(&redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).
508 Schema(&redis.FieldSchema{FieldName: "num", FieldType: redis.SearchFieldTypeNumeric, Sortable: true}).
509 Run()
510 Expect(err).NotTo(HaveOccurred())
511 Expect(createVal).To(Equal("OK"))
512 WaitForIndexing(client, "idx_payloads")
513
514 client.HSet(ctx, "doc1", "txt", "hello", "num", 1)
515 client.HSet(ctx, "doc2", "txt", "world", "num", 2)
516
517
518 res, err := client.NewSearchBuilder(ctx, "idx_payloads", "*").
519 SortBy("num", true).
520 WithSortByCount().
521 NoContent().
522 Run()
523 Expect(err).NotTo(HaveOccurred())
524 Expect(res.Total).To(Equal(2))
525 })
526
527 It("should test SearchBuilder with JSON", Label("search", "ftsearch", "builders", "json"), func() {
528 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_json").
529 OnJSON().
530 Prefix("king:").
531 Schema(&redis.FieldSchema{FieldName: "$.name", FieldType: redis.SearchFieldTypeText}).
532 Run()
533 Expect(err).NotTo(HaveOccurred())
534 Expect(createVal).To(Equal("OK"))
535 WaitForIndexing(client, "idx_json")
536
537 client.JSONSet(ctx, "king:1", "$", `{"name": "henry"}`)
538 client.JSONSet(ctx, "king:2", "$", `{"name": "james"}`)
539
540 res, err := client.NewSearchBuilder(ctx, "idx_json", "henry").Run()
541 Expect(err).NotTo(HaveOccurred())
542 Expect(res.Total).To(Equal(1))
543 Expect(res.Docs[0].ID).To(Equal("king:1"))
544 Expect(res.Docs[0].Fields["$"]).To(Equal(`{"name":"henry"}`))
545 })
546
547 It("should test SearchBuilder with vector search", Label("search", "ftsearch", "builders", "vector"), func() {
548 hnswOptions := &redis.FTHNSWOptions{Type: "FLOAT32", Dim: 2, DistanceMetric: "L2"}
549 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_vector").
550 OnHash().
551 Schema(&redis.FieldSchema{FieldName: "v", FieldType: redis.SearchFieldTypeVector, VectorArgs: &redis.FTVectorArgs{HNSWOptions: hnswOptions}}).
552 Run()
553 Expect(err).NotTo(HaveOccurred())
554 Expect(createVal).To(Equal("OK"))
555 WaitForIndexing(client, "idx_vector")
556
557 client.HSet(ctx, "a", "v", "aaaaaaaa")
558 client.HSet(ctx, "b", "v", "aaaabaaa")
559 client.HSet(ctx, "c", "v", "aaaaabaa")
560
561 res, err := client.NewSearchBuilder(ctx, "idx_vector", "*=>[KNN 2 @v $vec]").
562 ReturnFields("__v_score").
563 SortBy("__v_score", true).
564 Dialect(2).
565 Param("vec", "aaaaaaaa").
566 Run()
567 Expect(err).NotTo(HaveOccurred())
568 Expect(res.Docs[0].ID).To(Equal("a"))
569 Expect(res.Docs[0].Fields["__v_score"]).To(Equal("0"))
570 })
571
572 It("should test SearchBuilder with complex filtering and aggregation", Label("search", "ftsearch", "builders"), func() {
573 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_complex").
574 OnHash().
575 Schema(&redis.FieldSchema{FieldName: "category", FieldType: redis.SearchFieldTypeTag}).
576 Schema(&redis.FieldSchema{FieldName: "price", FieldType: redis.SearchFieldTypeNumeric, Sortable: true}).
577 Schema(&redis.FieldSchema{FieldName: "location", FieldType: redis.SearchFieldTypeGeo}).
578 Schema(&redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText}).
579 Run()
580 Expect(err).NotTo(HaveOccurred())
581 Expect(createVal).To(Equal("OK"))
582 WaitForIndexing(client, "idx_complex")
583
584 client.HSet(ctx, "product1", "category", "electronics", "price", 100, "location", "-0.1,51.5", "description", "smartphone device")
585 client.HSet(ctx, "product2", "category", "electronics", "price", 200, "location", "-0.2,51.6", "description", "laptop computer")
586 client.HSet(ctx, "product3", "category", "books", "price", 20, "location", "-0.3,51.7", "description", "programming guide")
587
588 res, err := client.NewSearchBuilder(ctx, "idx_complex", "@category:{electronics} @description:(device|computer)").
589 Filter("price", 50, 250).
590 GeoFilter("location", -0.15, 51.55, 50, "km").
591 SortBy("price", true).
592 ReturnFields("category", "price", "description").
593 Limit(0, 10).
594 Run()
595 Expect(err).NotTo(HaveOccurred())
596 Expect(res.Total).To(BeNumerically(">=", 1))
597
598 res2, err := client.NewSearchBuilder(ctx, "idx_complex", "@category:{$cat} @price:[$min $max]").
599 ParamsMap(map[string]interface{}{
600 "cat": "electronics",
601 "min": 150,
602 "max": 300,
603 }).
604 Dialect(2).
605 WithScores().
606 Run()
607 Expect(err).NotTo(HaveOccurred())
608 Expect(res2.Total).To(Equal(1))
609 Expect(res2.Docs[0].ID).To(Equal("product2"))
610 })
611
612 It("should test SearchBuilder error handling and edge cases", Label("search", "ftsearch", "builders", "edge-cases"), func() {
613 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_edge").
614 OnHash().
615 Schema(&redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).
616 Run()
617 Expect(err).NotTo(HaveOccurred())
618 Expect(createVal).To(Equal("OK"))
619 WaitForIndexing(client, "idx_edge")
620
621 client.HSet(ctx, "doc1", "txt", "hello world")
622
623
624 res1, err := client.NewSearchBuilder(ctx, "idx_edge", "*").NoContent().Run()
625 Expect(err).NotTo(HaveOccurred())
626 Expect(res1.Total).To(Equal(1))
627
628
629 res2, err := client.NewSearchBuilder(ctx, "idx_edge", "nonexistent").NoContent().Run()
630 Expect(err).NotTo(HaveOccurred())
631 Expect(res2.Total).To(Equal(0))
632
633
634 res3, err := client.NewSearchBuilder(ctx, "idx_edge", "hello").
635 WithScores().
636 NoContent().
637 Verbatim().
638 InOrder().
639 Slop(0).
640 Timeout(5000).
641 Language("english").
642 Dialect(2).
643 Run()
644 Expect(err).NotTo(HaveOccurred())
645 Expect(res3.Total).To(Equal(1))
646 })
647
648 It("should test SearchBuilder method chaining", Label("search", "ftsearch", "builders", "fluent"), func() {
649 createVal, err := client.NewCreateIndexBuilder(ctx, "idx_fluent").
650 OnHash().
651 Schema(&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText}).
652 Schema(&redis.FieldSchema{FieldName: "tags", FieldType: redis.SearchFieldTypeTag}).
653 Schema(&redis.FieldSchema{FieldName: "score", FieldType: redis.SearchFieldTypeNumeric, Sortable: true}).
654 Run()
655 Expect(err).NotTo(HaveOccurred())
656 Expect(createVal).To(Equal("OK"))
657 WaitForIndexing(client, "idx_fluent")
658
659 client.HSet(ctx, "doc1", "title", "Redis Search Tutorial", "tags", "redis,search,tutorial", "score", 95)
660 client.HSet(ctx, "doc2", "title", "Advanced Redis", "tags", "redis,advanced", "score", 88)
661
662 builder := client.NewSearchBuilder(ctx, "idx_fluent", "@title:(redis) @tags:{search}")
663 result := builder.
664 WithScores().
665 Filter("score", 90, 100).
666 SortBy("score", false).
667 ReturnFields("title", "score").
668 Limit(0, 5).
669 Dialect(2).
670 Timeout(1000).
671 Language("english")
672
673 res, err := result.Run()
674 Expect(err).NotTo(HaveOccurred())
675 Expect(res.Total).To(Equal(1))
676 Expect(res.Docs[0].ID).To(Equal("doc1"))
677 Expect(res.Docs[0].Fields["title"]).To(Equal("Redis Search Tutorial"))
678 Expect(*res.Docs[0].Score).To(BeNumerically(">", 0))
679 })
680 })
681
View as plain text