Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b96444b
[add] support for FT.SUGLEN, FT.SUGDEL. Included default options for …
filipecosta90 Dec 17, 2019
fcc6707
[merge] merged with master
filipecosta90 Dec 17, 2019
db9fc01
[add] improved spellcheck coverage
filipecosta90 Dec 17, 2019
6df0c36
[add] no breaking changes on autocompleter
filipecosta90 Mar 30, 2020
dc05944
[add] fixed conflicts
filipecosta90 Mar 30, 2020
e9ef654
[add] fixed merge conflicts and prunned code
filipecosta90 Mar 30, 2020
c07c79e
[add] prunning changes
filipecosta90 Mar 30, 2020
f8833ae
[add] prunning changes
filipecosta90 Mar 30, 2020
84903f5
[add] Updated Readme with new supported commands
filipecosta90 Mar 30, 2020
dc65c2d
[add] support for FT.GET and FT.MGET
filipecosta90 Mar 30, 2020
78cab8d
[fix] fixed TestClient_Get
filipecosta90 Mar 30, 2020
a198e9c
[add] add support for FT.DICTADD FT.DICTDEL and FT.DICTDUMP
filipecosta90 Mar 30, 2020
e97368c
[add] added support for FT.ALIASADD, FT.ALIASUPDATE, FT.ALIASDEL
filipecosta90 Mar 31, 2020
31029bc
[add] updated Readme with Alias supported commands
filipecosta90 Mar 31, 2020
b7769b1
Merge branch 'master' into ft.sug.refactor
filipecosta90 Apr 2, 2020
9e484b4
[add] added missing inline docs
filipecosta90 Apr 2, 2020
09c668e
Merge branch 'master' into ft.sug.refactor
filipecosta90 Apr 2, 2020
c5e3c09
[fix] fixed conflicts on test files
filipecosta90 Apr 2, 2020
f5e7755
[add] added missing inline documents for Single Document Indexing Opt…
filipecosta90 Apr 2, 2020
82d2f33
[fix] moved IndexingOptions( represent the options for indexing a sin…
filipecosta90 Apr 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,29 +80,29 @@ func ExampleClient() {
| [FT.ADD](https://oss.redislabs.com/redisearch/Commands.html#ftadd) | [IndexOptions](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.IndexOptions) |
| [FT.ADDHASH](https://oss.redislabs.com/redisearch/Commands.html#ftaddhash) | N/A |
| [FT.ALTER](https://oss.redislabs.com/redisearch/Commands.html#ftalter) | N/A |
| [FT.ALIASADD](https://oss.redislabs.com/redisearch/Commands.html#ftaliasadd) | N/A |
| [FT.ALIASUPDATE](https://oss.redislabs.com/redisearch/Commands.html#ftaliasupdate) | N/A |
| [FT.ALIASDEL](https://oss.redislabs.com/redisearch/Commands.html#ftaliasdel) | N/A |
| [FT.ALIASADD](https://oss.redislabs.com/redisearch/Commands.html#ftaliasadd) | [AliasAdd](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.AliasAdd) |
| [FT.ALIASUPDATE](https://oss.redislabs.com/redisearch/Commands.html#ftaliasupdate) | [AliasUpdate](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.AliasUpdate) |
| [FT.ALIASDEL](https://oss.redislabs.com/redisearch/Commands.html#ftaliasdel) | [AliasDel](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.AliasDel) |
| [FT.INFO](https://oss.redislabs.com/redisearch/Commands.html#ftinfo) | [Info](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Info) |
| [FT.SEARCH](https://oss.redislabs.com/redisearch/Commands.html#ftsearch) | [Search](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Search) |
| [FT.AGGREGATE](https://oss.redislabs.com/redisearch/Commands.html#ftaggregate) | [Aggregate](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Aggregate) |
| [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) |
| [FT.EXPLAIN](https://oss.redislabs.com/redisearch/Commands.html#ftexplain) | [Explain](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Explain) |
| [FT.DEL](https://oss.redislabs.com/redisearch/Commands.html#ftdel) | [Delete](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Delete) |
| [FT.GET](https://oss.redislabs.com/redisearch/Commands.html#ftget) | N/A |
| [FT.MGET](https://oss.redislabs.com/redisearch/Commands.html#ftmget) | N/A |
| [FT.GET](https://oss.redislabs.com/redisearch/Commands.html#ftget) | [Get](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Get) |
| [FT.MGET](https://oss.redislabs.com/redisearch/Commands.html#ftmget) | [MultiGet](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Multi) |
| [FT.DROP](https://oss.redislabs.com/redisearch/Commands.html#ftdrop) | [Drop](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.Drop) |
| [FT.TAGVALS](https://oss.redislabs.com/redisearch/Commands.html#fttagvals) | N/A |
| [FT.SUGADD](https://oss.redislabs.com/redisearch/Commands.html#ftsugadd) | N/A |
| [FT.SUGGET](https://oss.redislabs.com/redisearch/Commands.html#ftsugget) | N/A |
| [FT.SUGDEL](https://oss.redislabs.com/redisearch/Commands.html#ftsugdel) | N/A |
| [FT.SUGLEN](https://oss.redislabs.com/redisearch/Commands.html#ftsuglen) | N/A |
| [FT.SUGADD](https://oss.redislabs.com/redisearch/Commands.html#ftsugadd) | [AddTerms](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.AddTerms) |
| [FT.SUGGET](https://oss.redislabs.com/redisearch/Commands.html#ftsugget) | [SuggestOpts](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.SuggestOpts) |
| [FT.SUGDEL](https://oss.redislabs.com/redisearch/Commands.html#ftsugdel) | [DeleteTerms](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.DeleteTerms) |
| [FT.SUGLEN](https://oss.redislabs.com/redisearch/Commands.html#ftsuglen) | [Autocompleter.Length](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Autocompleter.Length) |
| [FT.SYNADD](https://oss.redislabs.com/redisearch/Commands.html#ftsynadd) | N/A |
| [FT.SYNUPDATE](https://oss.redislabs.com/redisearch/Commands.html#ftsynupdate) | N/A |
| [FT.SYNDUMP](https://oss.redislabs.com/redisearch/Commands.html#ftsyndump) | N/A |
| [FT.SPELLCHECK](https://oss.redislabs.com/redisearch/Commands.html#ftspellcheck) | [SpellCheck](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.SpellCheck) |
| [FT.DICTADD](https://oss.redislabs.com/redisearch/Commands.html#ftdictadd) | N/A |
| [FT.DICTDEL](https://oss.redislabs.com/redisearch/Commands.html#ftdictdel) | N/A |
| [FT.DICTDUMP](https://oss.redislabs.com/redisearch/Commands.html#ftdictdump) | N/A |
| [FT.DICTADD](https://oss.redislabs.com/redisearch/Commands.html#ftdictadd) | [DictAdd](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.DictAdd) |
| [FT.DICTDEL](https://oss.redislabs.com/redisearch/Commands.html#ftdictdel) | [DictDel](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.DictDel) |
| [FT.DICTDUMP](https://oss.redislabs.com/redisearch/Commands.html#ftdictdump) | [DictDump](https://godoc.org/github.com/RediSearch/redisearch-go/redisearch#Client.DictDump) |
| [FT.CONFIG](https://oss.redislabs.com/redisearch/Commands.html#ftconfig) | N/A |

14 changes: 7 additions & 7 deletions redisearch/aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func AddValues(c *Client) {
}

}
func init() {
func Init() {
/* load test data */
c := createClient("docs-games-idx1")

Expand All @@ -96,7 +96,7 @@ func init() {
AddValues(c)
}
func TestAggregateGroupBy(t *testing.T) {

Init()
c := createClient("docs-games-idx1")

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

func TestAggregateMinMax(t *testing.T) {

Init()
c := createClient("docs-games-idx1")

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

func TestAggregateCountDistinct(t *testing.T) {

Init()
c := createClient("docs-games-idx1")

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

func TestAggregateFilter(t *testing.T) {

Init()
c := createClient("docs-games-idx1")

q1 := NewAggregateQuery().
Expand Down Expand Up @@ -246,7 +246,7 @@ func TestProjection_Serialize(t *testing.T) {
Alias: tt.fields.Alias,
}
if got := p.Serialize(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Serialize() = %v, want %v", got, tt.want)
t.Errorf("serialize() = %v, want %v", got, tt.want)
}
})
}
Expand Down Expand Up @@ -275,7 +275,7 @@ func TestCursor_Serialize(t *testing.T) {
MaxIdle: tt.fields.MaxIdle,
}
if got := c.Serialize(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Serialize() = %v, want %v", got, tt.want)
t.Errorf("serialize() = %v, want %v", got, tt.want)
}
})
}
Expand Down
102 changes: 69 additions & 33 deletions redisearch/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import (

// Autocompleter implements a redisearch auto-completer API
type Autocompleter struct {
pool *redis.Pool
name string
pool *redis.Pool
}

// NewAutocompleter creates a new Autocompleter with the given pool and key name
func NewAutocompleterFromPool(pool *redis.Pool, name string) *Autocompleter {
return &Autocompleter{name: name, pool: pool}
}

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

// Delete deletes the Autocompleter key for this AC
func (a *Autocompleter) Delete() error {

conn := a.pool.Get()
defer conn.Close()

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

// AddTerms pushes new term suggestions to the index
func (a *Autocompleter) AddTerms(terms ...Suggestion) error {

conn := a.pool.Get()
defer conn.Close()

Expand Down Expand Up @@ -62,49 +65,85 @@ func (a *Autocompleter) AddTerms(terms ...Suggestion) error {
return nil
}

// AddTerms pushes new term suggestions to the index
func (a *Autocompleter) DeleteTerms(terms ...Suggestion) error {
conn := a.pool.Get()
defer conn.Close()

i := 0
for _, term := range terms {

args := redis.Args{a.name, term.Term}
if err := conn.Send("FT.SUGDEL", args...); err != nil {
return err
}
i++
}
if err := conn.Flush(); err != nil {
return err
}
for i > 0 {
if _, err := conn.Receive(); err != nil {
return err
}
i--
}
return nil
}

// AddTerms pushes new term suggestions to the index
func (a *Autocompleter) Length() (len int64, err error) {
conn := a.pool.Get()
defer conn.Close()
len, err = redis.Int64(conn.Do("FT.SUGLEN", a.name))
return
}

// Suggest gets completion suggestions from the Autocompleter dictionary to the given prefix.
// If fuzzy is set, we also complete for prefixes that are in 1 Levenshten distance from the
// given prefix
//
// Deprecated: Please use SuggestOpts() instead
func (a *Autocompleter) Suggest(prefix string, num int, fuzzy bool) ([]Suggestion, error) {
func (a *Autocompleter) Suggest(prefix string, num int, fuzzy bool) (ret []Suggestion, err error) {
conn := a.pool.Get()
defer conn.Close()

args := redis.Args{a.name, prefix, "MAX", num, "WITHSCORES"}
if fuzzy {
args = append(args, "FUZZY")
}
seropts := DefaultSuggestOptions
seropts.Num = num
seropts.Fuzzy = fuzzy
args, inc := a.Serialize(prefix, seropts)

vals, err := redis.Strings(conn.Do("FT.SUGGET", args...))
if err != nil {
return nil, err
}

ret := make([]Suggestion, 0, len(vals)/2)
for i := 0; i < len(vals); i += 2 {

score, err := strconv.ParseFloat(vals[i+1], 64)
if err != nil {
continue
}
ret = append(ret, Suggestion{Term: vals[i], Score: score})

}

return ret, nil
ret = ProcessSugGetVals(vals, inc, true, false)

return
}

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

inc := 1
args, inc := a.Serialize(prefix, opts)
vals, err := redis.Strings(conn.Do("FT.SUGGET", args...))
if err != nil {
return nil, err
}

ret = ProcessSugGetVals(vals, inc, opts.WithScores, opts.WithPayloads)

return
}

func (a *Autocompleter) Serialize(prefix string, opts SuggestOptions) (redis.Args, int) {
inc := 1
args := redis.Args{a.name, prefix, "MAX", opts.Num}
if opts.Fuzzy {
args = append(args, "FUZZY")
Expand All @@ -117,29 +156,26 @@ func (a *Autocompleter) SuggestOpts(prefix string, opts SuggestOptions) ([]Sugge
args = append(args, "WITHPAYLOADS")
inc++
}
vals, err := redis.Strings(conn.Do("FT.SUGGET", args...))
if err != nil {
return nil, err
}
return args, inc
}

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

suggestion := Suggestion{Term: vals[i]}
if opts.WithScores {
if WithScores {
score, err := strconv.ParseFloat(vals[i+1], 64)
if err != nil {
continue
}
suggestion.Score = score
}
if opts.WithPayloads {
if WithPayloads {
suggestion.Payload = vals[i+(inc-1)]
}
ret = append(ret, suggestion)

}

return ret, nil

return
}
101 changes: 101 additions & 0 deletions redisearch/autocomplete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package redisearch_test

import (
"fmt"
"github.com/RediSearch/redisearch-go/redisearch"
"github.com/gomodule/redigo/redis"
"github.com/stretchr/testify/assert"
"os"
"reflect"
"testing"
)

func createAutocompleter(dictName string) *redisearch.Autocompleter {
value, exists := os.LookupEnv("REDISEARCH_TEST_HOST")
host := "localhost:6379"
if exists && value != "" {
host = value
}
return redisearch.NewAutocompleter(host, dictName)
}

func TestAutocompleter_Serialize(t *testing.T) {
fuzzy := redisearch.DefaultSuggestOptions
fuzzy.Fuzzy = true
withscores := redisearch.DefaultSuggestOptions
withscores.WithScores = true
withpayloads := redisearch.DefaultSuggestOptions
withpayloads.WithPayloads = true
all := redisearch.DefaultSuggestOptions
all.Fuzzy = true
all.WithScores = true
all.WithPayloads = true

type fields struct {
name string
}
type args struct {
prefix string
opts redisearch.SuggestOptions
}
tests := []struct {
name string
fields fields
args args
want redis.Args
want1 int
}{
{"default options", fields{"key1"}, args{"ab", redisearch.DefaultSuggestOptions,}, redis.Args{"key1", "ab", "MAX", 5}, 1},
{"FUZZY", fields{"key1"}, args{"ab", fuzzy,}, redis.Args{"key1", "ab", "MAX", 5, "FUZZY"}, 1},
{"WITHSCORES", fields{"key1"}, args{"ab", withscores,}, redis.Args{"key1", "ab", "MAX", 5, "WITHSCORES"}, 2},
{"WITHPAYLOADS", fields{"key1"}, args{"ab", withpayloads,}, redis.Args{"key1", "ab", "MAX", 5, "WITHPAYLOADS"}, 2},
{"all", fields{"key1"}, args{"ab", all,}, redis.Args{"key1", "ab", "MAX", 5, "FUZZY", "WITHSCORES", "WITHPAYLOADS"}, 3},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := redisearch.NewAutocompleterFromPool(nil, tt.fields.name)
got, got1 := a.Serialize(tt.args.prefix, tt.args.opts)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("serialize() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("serialize() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

func TestSuggest(t *testing.T) {
a := createAutocompleter("testing")

// Add Terms to the Autocompleter
terms := make([]redisearch.Suggestion, 10)
for i := 0; i < 10; i++ {
terms[i] = redisearch.Suggestion{Term: fmt.Sprintf("foo %d", i),
Score: 1.0, Payload: fmt.Sprintf("bar %d", i)}
}
err := a.AddTerms(terms...)
assert.Nil(t, err)
suglen, err := a.Length()
assert.Nil(t, err)
assert.Equal(t, int64(10), suglen)
// Retrieve Terms From Autocompleter - Without Payloads / Scores
suggestions, err := a.SuggestOpts("f", redisearch.SuggestOptions{Num: 10})
assert.Nil(t, err)
assert.Equal(t, 10, len(suggestions))
for _, suggestion := range suggestions {
assert.Contains(t, suggestion.Term, "foo")
assert.Equal(t, suggestion.Payload, "")
assert.Zero(t, suggestion.Score)
}

// Retrieve Terms From Autocompleter - With Payloads & Scores
suggestions, err = a.SuggestOpts("f", redisearch.SuggestOptions{Num: 10, WithScores: true, WithPayloads: true})
assert.Nil(t, err)
assert.Equal(t, 10, len(suggestions))
for _, suggestion := range suggestions {
assert.Contains(t, suggestion.Term, "foo")
assert.Contains(t, suggestion.Payload, "bar")
assert.NotZero(t, suggestion.Score)
}
}
Loading