Skip to content

Commit 8e48e5d

Browse files
authored
Merge pull request #36 from RediSearch/ft.sug.refactor
support for FT.GET, FT.MGET, FT.SUGLEN, FT.SUGDEL, FT.ALIASADD, FT.ALIASUPDATE, FT.ALIASDEL, FT.DICTADD, FT.DICTDEL, FT.DICTDUMP
2 parents 522e62a + 82d2f33 commit 8e48e5d

15 files changed

+1144
-412
lines changed

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,29 +80,29 @@ func ExampleClient() {
8080
| [FT.ADD](https://oss.redislabs.com/redisearch/Commands.html#ftadd) | [IndexOptions](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.IndexOptions) |
8181
| [FT.ADDHASH](https://oss.redislabs.com/redisearch/Commands.html#ftaddhash) | N/A |
8282
| [FT.ALTER](https://oss.redislabs.com/redisearch/Commands.html#ftalter) | N/A |
83-
| [FT.ALIASADD](https://oss.redislabs.com/redisearch/Commands.html#ftaliasadd) | N/A |
84-
| [FT.ALIASUPDATE](https://oss.redislabs.com/redisearch/Commands.html#ftaliasupdate) | N/A |
85-
| [FT.ALIASDEL](https://oss.redislabs.com/redisearch/Commands.html#ftaliasdel) | N/A |
83+
| [FT.ALIASADD](https://oss.redislabs.com/redisearch/Commands.html#ftaliasadd) | [AliasAdd](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.AliasAdd) |
84+
| [FT.ALIASUPDATE](https://oss.redislabs.com/redisearch/Commands.html#ftaliasupdate) | [AliasUpdate](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.AliasUpdate) |
85+
| [FT.ALIASDEL](https://oss.redislabs.com/redisearch/Commands.html#ftaliasdel) | [AliasDel](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.AliasDel) |
8686
| [FT.INFO](https://oss.redislabs.com/redisearch/Commands.html#ftinfo) | [Info](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Info) |
8787
| [FT.SEARCH](https://oss.redislabs.com/redisearch/Commands.html#ftsearch) | [Search](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Search) |
8888
| [FT.AGGREGATE](https://oss.redislabs.com/redisearch/Commands.html#ftaggregate) | [Aggregate](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Aggregate) |
8989
| [FT.CURSOR](https://oss.redislabs.com/redisearch/Aggregations.html#cursor_api) | [Aggregate](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Aggregate) + (*WithCursor option set to True) |
9090
| [FT.EXPLAIN](https://oss.redislabs.com/redisearch/Commands.html#ftexplain) | [Explain](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Explain) |
9191
| [FT.DEL](https://oss.redislabs.com/redisearch/Commands.html#ftdel) | [Delete](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Delete) |
92-
| [FT.GET](https://oss.redislabs.com/redisearch/Commands.html#ftget) | N/A |
93-
| [FT.MGET](https://oss.redislabs.com/redisearch/Commands.html#ftmget) | N/A |
92+
| [FT.GET](https://oss.redislabs.com/redisearch/Commands.html#ftget) | [Get](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Get) |
93+
| [FT.MGET](https://oss.redislabs.com/redisearch/Commands.html#ftmget) | [MultiGet](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Multi) |
9494
| [FT.DROP](https://oss.redislabs.com/redisearch/Commands.html#ftdrop) | [Drop](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Drop) |
9595
| [FT.TAGVALS](https://oss.redislabs.com/redisearch/Commands.html#fttagvals) | N/A |
96-
| [FT.SUGADD](https://oss.redislabs.com/redisearch/Commands.html#ftsugadd) | N/A |
97-
| [FT.SUGGET](https://oss.redislabs.com/redisearch/Commands.html#ftsugget) | N/A |
98-
| [FT.SUGDEL](https://oss.redislabs.com/redisearch/Commands.html#ftsugdel) | N/A |
99-
| [FT.SUGLEN](https://oss.redislabs.com/redisearch/Commands.html#ftsuglen) | N/A |
96+
| [FT.SUGADD](https://oss.redislabs.com/redisearch/Commands.html#ftsugadd) | [AddTerms](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.AddTerms) |
97+
| [FT.SUGGET](https://oss.redislabs.com/redisearch/Commands.html#ftsugget) | [SuggestOpts](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.SuggestOpts) |
98+
| [FT.SUGDEL](https://oss.redislabs.com/redisearch/Commands.html#ftsugdel) | [DeleteTerms](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.DeleteTerms) |
99+
| [FT.SUGLEN](https://oss.redislabs.com/redisearch/Commands.html#ftsuglen) | [Autocompleter.Length](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.Length) |
100100
| [FT.SYNADD](https://oss.redislabs.com/redisearch/Commands.html#ftsynadd) | N/A |
101101
| [FT.SYNUPDATE](https://oss.redislabs.com/redisearch/Commands.html#ftsynupdate) | N/A |
102102
| [FT.SYNDUMP](https://oss.redislabs.com/redisearch/Commands.html#ftsyndump) | N/A |
103103
| [FT.SPELLCHECK](https://oss.redislabs.com/redisearch/Commands.html#ftspellcheck) | [SpellCheck](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.SpellCheck) |
104-
| [FT.DICTADD](https://oss.redislabs.com/redisearch/Commands.html#ftdictadd) | N/A |
105-
| [FT.DICTDEL](https://oss.redislabs.com/redisearch/Commands.html#ftdictdel) | N/A |
106-
| [FT.DICTDUMP](https://oss.redislabs.com/redisearch/Commands.html#ftdictdump) | N/A |
104+
| [FT.DICTADD](https://oss.redislabs.com/redisearch/Commands.html#ftdictadd) | [DictAdd](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.DictAdd) |
105+
| [FT.DICTDEL](https://oss.redislabs.com/redisearch/Commands.html#ftdictdel) | [DictDel](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.DictDel) |
106+
| [FT.DICTDUMP](https://oss.redislabs.com/redisearch/Commands.html#ftdictdump) | [DictDump](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.DictDump) |
107107
| [FT.CONFIG](https://oss.redislabs.com/redisearch/Commands.html#ftconfig) | N/A |
108108

redisearch/aggregate_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func AddValues(c *Client) {
7979
}
8080

8181
}
82-
func init() {
82+
func Init() {
8383
/* load test data */
8484
c := createClient("docs-games-idx1")
8585

@@ -96,7 +96,7 @@ func init() {
9696
AddValues(c)
9797
}
9898
func TestAggregateGroupBy(t *testing.T) {
99-
99+
Init()
100100
c := createClient("docs-games-idx1")
101101

102102
q1 := NewAggregateQuery().
@@ -111,7 +111,7 @@ func TestAggregateGroupBy(t *testing.T) {
111111
}
112112

113113
func TestAggregateMinMax(t *testing.T) {
114-
114+
Init()
115115
c := createClient("docs-games-idx1")
116116

117117
q1 := NewAggregateQuery().SetQuery(NewQuery("sony")).
@@ -143,7 +143,7 @@ func TestAggregateMinMax(t *testing.T) {
143143
}
144144

145145
func TestAggregateCountDistinct(t *testing.T) {
146-
146+
Init()
147147
c := createClient("docs-games-idx1")
148148

149149
q1 := NewAggregateQuery().
@@ -158,7 +158,7 @@ func TestAggregateCountDistinct(t *testing.T) {
158158
}
159159

160160
func TestAggregateFilter(t *testing.T) {
161-
161+
Init()
162162
c := createClient("docs-games-idx1")
163163

164164
q1 := NewAggregateQuery().
@@ -246,7 +246,7 @@ func TestProjection_Serialize(t *testing.T) {
246246
Alias: tt.fields.Alias,
247247
}
248248
if got := p.Serialize(); !reflect.DeepEqual(got, tt.want) {
249-
t.Errorf("Serialize() = %v, want %v", got, tt.want)
249+
t.Errorf("serialize() = %v, want %v", got, tt.want)
250250
}
251251
})
252252
}
@@ -275,7 +275,7 @@ func TestCursor_Serialize(t *testing.T) {
275275
MaxIdle: tt.fields.MaxIdle,
276276
}
277277
if got := c.Serialize(); !reflect.DeepEqual(got, tt.want) {
278-
t.Errorf("Serialize() = %v, want %v", got, tt.want)
278+
t.Errorf("serialize() = %v, want %v", got, tt.want)
279279
}
280280
})
281281
}

redisearch/autocomplete.go

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ import (
77

88
// Autocompleter implements a redisearch auto-completer API
99
type Autocompleter struct {
10-
pool *redis.Pool
1110
name string
11+
pool *redis.Pool
12+
}
13+
14+
// NewAutocompleter creates a new Autocompleter with the given pool and key name
15+
func NewAutocompleterFromPool(pool *redis.Pool, name string) *Autocompleter {
16+
return &Autocompleter{name: name, pool: pool}
1217
}
1318

1419
// NewAutocompleter creates a new Autocompleter with the given host and key name
@@ -23,7 +28,6 @@ func NewAutocompleter(addr, name string) *Autocompleter {
2328

2429
// Delete deletes the Autocompleter key for this AC
2530
func (a *Autocompleter) Delete() error {
26-
2731
conn := a.pool.Get()
2832
defer conn.Close()
2933

@@ -33,7 +37,6 @@ func (a *Autocompleter) Delete() error {
3337

3438
// AddTerms pushes new term suggestions to the index
3539
func (a *Autocompleter) AddTerms(terms ...Suggestion) error {
36-
3740
conn := a.pool.Get()
3841
defer conn.Close()
3942

@@ -62,49 +65,85 @@ func (a *Autocompleter) AddTerms(terms ...Suggestion) error {
6265
return nil
6366
}
6467

68+
// AddTerms pushes new term suggestions to the index
69+
func (a *Autocompleter) DeleteTerms(terms ...Suggestion) error {
70+
conn := a.pool.Get()
71+
defer conn.Close()
72+
73+
i := 0
74+
for _, term := range terms {
75+
76+
args := redis.Args{a.name, term.Term}
77+
if err := conn.Send("FT.SUGDEL", args...); err != nil {
78+
return err
79+
}
80+
i++
81+
}
82+
if err := conn.Flush(); err != nil {
83+
return err
84+
}
85+
for i > 0 {
86+
if _, err := conn.Receive(); err != nil {
87+
return err
88+
}
89+
i--
90+
}
91+
return nil
92+
}
93+
94+
// AddTerms pushes new term suggestions to the index
95+
func (a *Autocompleter) Length() (len int64, err error) {
96+
conn := a.pool.Get()
97+
defer conn.Close()
98+
len, err = redis.Int64(conn.Do("FT.SUGLEN", a.name))
99+
return
100+
}
101+
65102
// Suggest gets completion suggestions from the Autocompleter dictionary to the given prefix.
66103
// If fuzzy is set, we also complete for prefixes that are in 1 Levenshten distance from the
67104
// given prefix
68105
//
69106
// Deprecated: Please use SuggestOpts() instead
70-
func (a *Autocompleter) Suggest(prefix string, num int, fuzzy bool) ([]Suggestion, error) {
107+
func (a *Autocompleter) Suggest(prefix string, num int, fuzzy bool) (ret []Suggestion, err error) {
71108
conn := a.pool.Get()
72109
defer conn.Close()
73110

74-
args := redis.Args{a.name, prefix, "MAX", num, "WITHSCORES"}
75-
if fuzzy {
76-
args = append(args, "FUZZY")
77-
}
111+
seropts := DefaultSuggestOptions
112+
seropts.Num = num
113+
seropts.Fuzzy = fuzzy
114+
args, inc := a.Serialize(prefix, seropts)
115+
78116
vals, err := redis.Strings(conn.Do("FT.SUGGET", args...))
79117
if err != nil {
80118
return nil, err
81119
}
82120

83-
ret := make([]Suggestion, 0, len(vals)/2)
84-
for i := 0; i < len(vals); i += 2 {
85-
86-
score, err := strconv.ParseFloat(vals[i+1], 64)
87-
if err != nil {
88-
continue
89-
}
90-
ret = append(ret, Suggestion{Term: vals[i], Score: score})
91-
92-
}
93-
94-
return ret, nil
121+
ret = ProcessSugGetVals(vals, inc, true, false)
95122

123+
return
96124
}
97125

98126
// SuggestOpts gets completion suggestions from the Autocompleter dictionary to the given prefix.
99127
// SuggestOptions are passed allowing you specify if the returned values contain a payload, and scores.
100-
// If SuggestOptions.Fuzzy is set, we also complete for prefixes that are in 1 Levenshten distance from the
128+
// If SuggestOptions.Fuzzy is set, we also complete for prefixes that are in 1 Levenshtein distance from the
101129
// given prefix
102-
func (a *Autocompleter) SuggestOpts(prefix string, opts SuggestOptions) ([]Suggestion, error) {
130+
func (a *Autocompleter) SuggestOpts(prefix string, opts SuggestOptions) (ret []Suggestion, err error) {
103131
conn := a.pool.Get()
104132
defer conn.Close()
105133

106-
inc := 1
134+
args, inc := a.Serialize(prefix, opts)
135+
vals, err := redis.Strings(conn.Do("FT.SUGGET", args...))
136+
if err != nil {
137+
return nil, err
138+
}
139+
140+
ret = ProcessSugGetVals(vals, inc, opts.WithScores, opts.WithPayloads)
107141

142+
return
143+
}
144+
145+
func (a *Autocompleter) Serialize(prefix string, opts SuggestOptions) (redis.Args, int) {
146+
inc := 1
108147
args := redis.Args{a.name, prefix, "MAX", opts.Num}
109148
if opts.Fuzzy {
110149
args = append(args, "FUZZY")
@@ -117,29 +156,26 @@ func (a *Autocompleter) SuggestOpts(prefix string, opts SuggestOptions) ([]Sugge
117156
args = append(args, "WITHPAYLOADS")
118157
inc++
119158
}
120-
vals, err := redis.Strings(conn.Do("FT.SUGGET", args...))
121-
if err != nil {
122-
return nil, err
123-
}
159+
return args, inc
160+
}
124161

125-
ret := make([]Suggestion, 0, len(vals)/inc)
162+
func ProcessSugGetVals(vals []string, inc int, WithScores, WithPayloads bool) (ret []Suggestion) {
163+
ret = make([]Suggestion, 0, len(vals)/inc)
126164
for i := 0; i < len(vals); i += inc {
127165

128166
suggestion := Suggestion{Term: vals[i]}
129-
if opts.WithScores {
167+
if WithScores {
130168
score, err := strconv.ParseFloat(vals[i+1], 64)
131169
if err != nil {
132170
continue
133171
}
134172
suggestion.Score = score
135173
}
136-
if opts.WithPayloads {
174+
if WithPayloads {
137175
suggestion.Payload = vals[i+(inc-1)]
138176
}
139177
ret = append(ret, suggestion)
140178

141179
}
142-
143-
return ret, nil
144-
180+
return
145181
}

redisearch/autocomplete_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package redisearch_test
2+
3+
import (
4+
"fmt"
5+
"github.com/RediSearch/redisearch-go/redisearch"
6+
"github.com/gomodule/redigo/redis"
7+
"github.com/stretchr/testify/assert"
8+
"os"
9+
"reflect"
10+
"testing"
11+
)
12+
13+
func createAutocompleter(dictName string) *redisearch.Autocompleter {
14+
value, exists := os.LookupEnv("REDISEARCH_TEST_HOST")
15+
host := "localhost:6379"
16+
if exists && value != "" {
17+
host = value
18+
}
19+
return redisearch.NewAutocompleter(host, dictName)
20+
}
21+
22+
func TestAutocompleter_Serialize(t *testing.T) {
23+
fuzzy := redisearch.DefaultSuggestOptions
24+
fuzzy.Fuzzy = true
25+
withscores := redisearch.DefaultSuggestOptions
26+
withscores.WithScores = true
27+
withpayloads := redisearch.DefaultSuggestOptions
28+
withpayloads.WithPayloads = true
29+
all := redisearch.DefaultSuggestOptions
30+
all.Fuzzy = true
31+
all.WithScores = true
32+
all.WithPayloads = true
33+
34+
type fields struct {
35+
name string
36+
}
37+
type args struct {
38+
prefix string
39+
opts redisearch.SuggestOptions
40+
}
41+
tests := []struct {
42+
name string
43+
fields fields
44+
args args
45+
want redis.Args
46+
want1 int
47+
}{
48+
{"default options", fields{"key1"}, args{"ab", redisearch.DefaultSuggestOptions,}, redis.Args{"key1", "ab", "MAX", 5}, 1},
49+
{"FUZZY", fields{"key1"}, args{"ab", fuzzy,}, redis.Args{"key1", "ab", "MAX", 5, "FUZZY"}, 1},
50+
{"WITHSCORES", fields{"key1"}, args{"ab", withscores,}, redis.Args{"key1", "ab", "MAX", 5, "WITHSCORES"}, 2},
51+
{"WITHPAYLOADS", fields{"key1"}, args{"ab", withpayloads,}, redis.Args{"key1", "ab", "MAX", 5, "WITHPAYLOADS"}, 2},
52+
{"all", fields{"key1"}, args{"ab", all,}, redis.Args{"key1", "ab", "MAX", 5, "FUZZY", "WITHSCORES", "WITHPAYLOADS"}, 3},
53+
}
54+
for _, tt := range tests {
55+
t.Run(tt.name, func(t *testing.T) {
56+
a := redisearch.NewAutocompleterFromPool(nil, tt.fields.name)
57+
got, got1 := a.Serialize(tt.args.prefix, tt.args.opts)
58+
if !reflect.DeepEqual(got, tt.want) {
59+
t.Errorf("serialize() got = %v, want %v", got, tt.want)
60+
}
61+
if got1 != tt.want1 {
62+
t.Errorf("serialize() got1 = %v, want %v", got1, tt.want1)
63+
}
64+
})
65+
}
66+
}
67+
68+
func TestSuggest(t *testing.T) {
69+
a := createAutocompleter("testing")
70+
71+
// Add Terms to the Autocompleter
72+
terms := make([]redisearch.Suggestion, 10)
73+
for i := 0; i < 10; i++ {
74+
terms[i] = redisearch.Suggestion{Term: fmt.Sprintf("foo %d", i),
75+
Score: 1.0, Payload: fmt.Sprintf("bar %d", i)}
76+
}
77+
err := a.AddTerms(terms...)
78+
assert.Nil(t, err)
79+
suglen, err := a.Length()
80+
assert.Nil(t, err)
81+
assert.Equal(t, int64(10), suglen)
82+
// Retrieve Terms From Autocompleter - Without Payloads / Scores
83+
suggestions, err := a.SuggestOpts("f", redisearch.SuggestOptions{Num: 10})
84+
assert.Nil(t, err)
85+
assert.Equal(t, 10, len(suggestions))
86+
for _, suggestion := range suggestions {
87+
assert.Contains(t, suggestion.Term, "foo")
88+
assert.Equal(t, suggestion.Payload, "")
89+
assert.Zero(t, suggestion.Score)
90+
}
91+
92+
// Retrieve Terms From Autocompleter - With Payloads & Scores
93+
suggestions, err = a.SuggestOpts("f", redisearch.SuggestOptions{Num: 10, WithScores: true, WithPayloads: true})
94+
assert.Nil(t, err)
95+
assert.Equal(t, 10, len(suggestions))
96+
for _, suggestion := range suggestions {
97+
assert.Contains(t, suggestion.Term, "foo")
98+
assert.Contains(t, suggestion.Payload, "bar")
99+
assert.NotZero(t, suggestion.Score)
100+
}
101+
}

0 commit comments

Comments
 (0)