From 55d65fa3b1631743f46bcea45e25a8a7cdad5214 Mon Sep 17 00:00:00 2001 From: filipecosta90 Date: Sun, 9 Aug 2020 00:09:10 +0100 Subject: [PATCH] [add] Added CreateIndexWithIndexDefinition --- Makefile | 4 +- redisearch/client.go | 22 ++++++-- redisearch/client_test.go | 43 ++++++++++++++ redisearch/filter.go | 2 +- redisearch/index.go | 115 +++++++++++++++++++++++++++++++++++++- redisearch/index_test.go | 39 +++++++++++++ 6 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 redisearch/index_test.go diff --git a/Makefile b/Makefile index 629d544..e1acfb5 100644 --- a/Makefile +++ b/Makefile @@ -41,8 +41,10 @@ examples: get --tls-ca-cert-file $(TLS_CACERT) \ --host $(REDISEARCH_TEST_HOST) -test: get +fmt: $(GOFMT) ./... + +test: get fmt $(GOTEST) -race -covermode=atomic ./... coverage: get test diff --git a/redisearch/client.go b/redisearch/client.go index 4ce011d..cda3274 100644 --- a/redisearch/client.go +++ b/redisearch/client.go @@ -46,15 +46,29 @@ func NewClientFromPool(pool *redis.Pool, name string) *Client { return ret } -// CreateIndex configues the index and creates it on redis -func (i *Client) CreateIndex(s *Schema) (err error) { +// CreateIndex configures the index and creates it on redis +func (i *Client) CreateIndex(schema *Schema) (err error) { + return i.indexWithDefinition(i.name, schema, nil, err) +} + +// CreateIndexWithIndexDefinition configures the index and creates it on redis +// IndexDefinition is used to define a index definition for automatic indexing on Hash update +func (i *Client) CreateIndexWithIndexDefinition(schema *Schema, definition *IndexDefinition) (err error) { + return i.indexWithDefinition(i.name, schema, definition, err) +} + +// internal method +func (i *Client) indexWithDefinition(indexName string, schema *Schema, definition *IndexDefinition, errIn error) (err error) { + err = errIn args := redis.Args{i.name} + if definition != nil { + args = definition.Serialize(args) + } // Set flags based on options - args, err = SerializeSchema(s, args) + args, err = SerializeSchema(schema, args) if err != nil { return } - conn := i.pool.Get() defer conn.Close() _, err = conn.Do("FT.CREATE", args...) diff --git a/redisearch/client_test.go b/redisearch/client_test.go index 444a630..bbf6d72 100644 --- a/redisearch/client_test.go +++ b/redisearch/client_test.go @@ -614,3 +614,46 @@ func TestClient_GetRediSearchVersion(t *testing.T) { _, err := c.getRediSearchVersion() assert.Nil(t, err) } + +func TestClient_CreateIndexWithIndexDefinition(t *testing.T) { + i := createClient("index-definition-test") + version, err := i.getRediSearchVersion() + assert.Nil(t, err) + if version >= 20000 { + + type args struct { + schema *Schema + definition *IndexDefinition + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"no-indexDefinition", args{NewSchema(DefaultOptions). + AddField(NewTextField("name")). + AddField(NewTextField("addr")), nil}, false}, + {"default-indexDefinition", args{NewSchema(DefaultOptions). + AddField(NewTextField("name")). + AddField(NewTextField("addr")), NewIndexDefinition()}, false}, + {"score-indexDefinition", args{NewSchema(DefaultOptions). + AddField(NewTextField("name")). + AddField(NewTextField("addr")), NewIndexDefinition().SetScore(0.25)}, false}, + {"language-indexDefinition", args{NewSchema(DefaultOptions). + AddField(NewTextField("name")). + AddField(NewTextField("addr")), NewIndexDefinition().SetLanguage("portuguese")}, false}, + {"language_field-indexDefinition", args{NewSchema(DefaultOptions). + AddField(NewTextField("name")). + AddField(NewTextField("lang")). + AddField(NewTextField("addr")), NewIndexDefinition().SetLanguageField("lang")}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := i.CreateIndexWithIndexDefinition(tt.args.schema, tt.args.definition); (err != nil) != tt.wantErr { + t.Errorf("CreateIndexWithIndexDefinition() error = %v, wantErr %v", err, tt.wantErr) + } + teardown(i) + }) + } + } +} diff --git a/redisearch/filter.go b/redisearch/filter.go index 16e0caf..2e19463 100644 --- a/redisearch/filter.go +++ b/redisearch/filter.go @@ -6,7 +6,7 @@ type Filter struct { Options interface{} } -// Filter the results to a given radius from lon and lat. Radius is given as a number and units +// FilterExpression the results to a given radius from lon and lat. Radius is given as a number and units type GeoFilterOptions struct { Lon float64 Lat float64 diff --git a/redisearch/index.go b/redisearch/index.go index 47c93e9..e1f6ab5 100644 --- a/redisearch/index.go +++ b/redisearch/index.go @@ -1,6 +1,8 @@ package redisearch -import "github.com/gomodule/redigo/redis" +import ( + "github.com/gomodule/redigo/redis" +) // IndexInfo - Structure showing information about an existing index type IndexInfo struct { @@ -22,6 +24,117 @@ type IndexInfo struct { PercentIndexed float64 `redis:"percent_indexed"` } +// IndexDefinition is used to define a index definition for automatic indexing on Hash update +// This is only valid for >= RediSearch 2.0 +type IndexDefinition struct { + IndexOn string + Async bool + Prefix []string + FilterExpression string + Language string + LanguageField string + Score float64 + ScoreField string + PayloadField string +} + +// This is only valid for >= RediSearch 2.0 +func NewIndexDefinition() *IndexDefinition { + prefixArray := make([]string, 0) + return &IndexDefinition{"HASH", false, prefixArray, "", "", "", -1, "", ""} +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) SetAsync(value bool) (outDef *IndexDefinition) { + outDef = defintion + outDef.Async = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) AddPrefix(prefix string) (outDef *IndexDefinition) { + outDef = defintion + outDef.Prefix = append(outDef.Prefix, prefix) + return +} + +func (defintion *IndexDefinition) SetFilterExpression(value string) (outDef *IndexDefinition) { + outDef = defintion + outDef.FilterExpression = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) SetLanguage(value string) (outDef *IndexDefinition) { + outDef = defintion + outDef.Language = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) SetLanguageField(value string) (outDef *IndexDefinition) { + outDef = defintion + outDef.LanguageField = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) SetScore(value float64) (outDef *IndexDefinition) { + outDef = defintion + outDef.Score = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) SetScoreField(value string) (outDef *IndexDefinition) { + outDef = defintion + outDef.ScoreField = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) SetPayloadField(value string) (outDef *IndexDefinition) { + outDef = defintion + outDef.PayloadField = value + return +} + +// This is only valid for >= RediSearch 2.0 +func (defintion *IndexDefinition) Serialize(args redis.Args) redis.Args { + args = append(args, "ON", defintion.IndexOn) + if defintion.Async { + args = append(args, "ASYNC") + } + if len(defintion.Prefix) > 0 { + args = append(args, "PREFIX", len(defintion.Prefix)) + for _, p := range defintion.Prefix { + args = append(args, p) + } + } + if defintion.FilterExpression != "" { + args = append(args, "FILTER", defintion.FilterExpression) + } + if defintion.Language != "" { + args = append(args, "LANGUAGE", defintion.Language) + } + + if defintion.LanguageField != "" { + args = append(args, "LANGUAGE_FIELD", defintion.LanguageField) + } + + if defintion.Score >= 0.0 && defintion.Score <= 1.0 { + args = append(args, "SCORE", defintion.Score) + } + + if defintion.ScoreField != "" { + args = append(args, "SCORE_FIELD", defintion.ScoreField) + } + if defintion.PayloadField != "" { + args = append(args, "PAYLOAD_FIELD", defintion.PayloadField) + } + return args +} + func SerializeIndexingOptions(opts IndexingOptions, args redis.Args) redis.Args { // apply options if opts.NoSave { diff --git a/redisearch/index_test.go b/redisearch/index_test.go new file mode 100644 index 0000000..425ec2f --- /dev/null +++ b/redisearch/index_test.go @@ -0,0 +1,39 @@ +package redisearch + +import ( + "github.com/gomodule/redigo/redis" + "reflect" + "testing" +) + +func TestIndexDefinition_Serialize(t *testing.T) { + type fields struct { + Definition *IndexDefinition + } + type args struct { + args redis.Args + } + tests := []struct { + name string + fields fields + args args + want redis.Args + }{ + {"default", fields{NewIndexDefinition()}, args{redis.Args{}}, redis.Args{"ON", "HASH"}}, + {"default+score", fields{NewIndexDefinition().SetScore(0.75)}, args{redis.Args{}}, redis.Args{"ON", "HASH", "SCORE", 0.75}}, + {"default+score_field", fields{NewIndexDefinition().SetScoreField("myscore")}, args{redis.Args{}}, redis.Args{"ON", "HASH", "SCORE_FIELD", "myscore"}}, + {"default+language", fields{NewIndexDefinition().SetLanguage("portuguese")}, args{redis.Args{}}, redis.Args{"ON", "HASH", "LANGUAGE", "portuguese"}}, + {"default+language_field", fields{NewIndexDefinition().SetLanguageField("mylanguage")}, args{redis.Args{}}, redis.Args{"ON", "HASH", "LANGUAGE_FIELD", "mylanguage"}}, + {"default+prefix", fields{NewIndexDefinition().AddPrefix("products:*")}, args{redis.Args{}}, redis.Args{"ON", "HASH", "PREFIX", 1, "products:*"}}, + {"default+payload_field", fields{NewIndexDefinition().SetPayloadField("products_description")}, args{redis.Args{}}, redis.Args{"ON", "HASH", "PAYLOAD_FIELD", "products_description"}}, + {"default+filter", fields{NewIndexDefinition().SetFilterExpression("@score:[0 50]")}, args{redis.Args{}}, redis.Args{"ON", "HASH", "FILTER", "@score:[0 50]"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defintion := tt.fields.Definition + if got := defintion.Serialize(tt.args.args); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Serialize() = %v, want %v", got, tt.want) + } + }) + } +}