From cd1d2ae4f6e5b21c6ea90354b5490a7928712403 Mon Sep 17 00:00:00 2001 From: filipecosta90 Date: Fri, 8 May 2020 14:05:29 +0100 Subject: [PATCH 1/3] [add] added TEMPORARY index option. Added example ExampleCreateIndex_temporary. --- Makefile | 26 ++++++++++++++ examples/quickstart/quickstart.go | 51 +++++++++++++++++++++++++++ examples/temporary/temporary.go | 57 +++++++++++++++++++++++++++++++ redisearch/client_test.go | 29 ++++++++++++++++ redisearch/example_schema_test.go | 51 +++++++++++++++++++++++++++ redisearch/schema.go | 52 +++++++++++++++++++++++++--- redisearch/schema_test.go | 4 +++ 7 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 Makefile create mode 100644 examples/quickstart/quickstart.go create mode 100644 examples/temporary/temporary.go create mode 100644 redisearch/example_schema_test.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..186edbc --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +# Go parameters +GOCMD=go +GOBUILD=$(GOCMD) build +GOINSTALL=$(GOCMD) install +GOCLEAN=$(GOCMD) clean +GOTEST=$(GOCMD) test +GOGET=$(GOCMD) get +GOMOD=$(GOCMD) mod + +.PHONY: all test coverage +all: test coverage + +get: + $(GOGET) -t -v ./... + +examples: get + $(GOBUILD) ./examples/quickstart/. + $(GOBUILD) ./examples/temporary/. + ./quickstart > /dev/null + +test: get examples + $(GOTEST) -race -covermode=atomic ./... + +coverage: get test + $(GOTEST) -race -coverprofile=coverage.txt -covermode=atomic ./redisearch + diff --git a/examples/quickstart/quickstart.go b/examples/quickstart/quickstart.go new file mode 100644 index 0000000..2569179 --- /dev/null +++ b/examples/quickstart/quickstart.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + "github.com/RediSearch/redisearch-go/redisearch" + "log" + "time" +) + +/** + * This demo should be updated in RediSearch.io if changed + * Update at: https://github.com/RediSearch/RediSearch/blob/master/docs/go_client.md + */ +func main() { + // Create a client. By default a client is schemaless + // unless a schema is provided when creating the index + c := redisearch.NewClient("localhost:6379", "myIndex") + + // Create a schema + sc := redisearch.NewSchema(redisearch.DefaultOptions). + AddField(redisearch.NewTextField("body")). + AddField(redisearch.NewTextFieldOptions("title", redisearch.TextFieldOptions{Weight: 5.0, Sortable: true})). + AddField(redisearch.NewNumericField("date")) + + // Drop an existing index. If the index does not exist an error is returned + c.Drop() + + // Create the index with the given schema + if err := c.CreateIndex(sc); err != nil { + log.Fatal(err) + } + + // Create a document with an id and given score + doc := redisearch.NewDocument("doc1", 1.0) + doc.Set("title", "Hello world"). + Set("body", "foo bar"). + Set("date", time.Now().Unix()) + + // Index the document. The API accepts multiple documents at a time + if err := c.IndexOptions(redisearch.DefaultIndexingOptions, doc); err != nil { + log.Fatal(err) + } + + // Searching with limit and sorting + docs, total, err := c.Search(redisearch.NewQuery("hello world"). + Limit(0, 2). + SetReturnFields("title")) + + fmt.Println(docs[0].Id, docs[0].Properties["title"], total, err) + // Output: doc1 Hello world 1 +} diff --git a/examples/temporary/temporary.go b/examples/temporary/temporary.go new file mode 100644 index 0000000..87fd374 --- /dev/null +++ b/examples/temporary/temporary.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "log" + "time" + + "github.com/RediSearch/redisearch-go/redisearch" +) + +/** + * This demo demonstrates the usage of Create a lightweight temporary index which will expire after the specified period of inactivity. The internal idle timer is reset whenever the index is searched or added to. Because such indexes are lightweight, you can create thousands of such indexes without negative performance implications. + */ +func main() { + // Create a client. By default a client is schemaless + // unless a schema is provided when creating the index + c := redisearch.NewClient("localhost:6379", "myTemporaryIndex") + + // Create a schema with a temporary index with temporary period of 10s + sc := redisearch.NewSchema(*redisearch.NewOptions().SetTemporaryPeriod(10)). + AddField(redisearch.NewTextField("body")). + AddField(redisearch.NewTextFieldOptions("title", redisearch.TextFieldOptions{Weight: 5.0, Sortable: true})). + AddField(redisearch.NewNumericField("date")) + + // Drop an existing index. If the index does not exist an error is returned + c.Drop() + + // Create the index with the given schema + if err := c.CreateIndex(sc); err != nil { + log.Fatal(err) + } + + // Create a document with an id and given score + doc := redisearch.NewDocument("doc1", 1.0) + doc.Set("title", "Hello world"). + Set("body", "foo bar"). + Set("date", time.Now().Unix()) + + // Index the document. The API accepts multiple documents at a time + if err := c.IndexOptions(redisearch.DefaultIndexingOptions, doc); err != nil { + log.Fatal(err) + } + + docs, total, err := c.Search(redisearch.NewQuery("hello world"). + Limit(0, 2). + SetReturnFields("title")) + + // Verify that the we're able to search on the temporary created index + fmt.Println(docs[0].Id, docs[0].Properties["title"], total, err) + // Output: doc1 Hello world 1 + + time.Sleep(15*time.Second) + // Searching with limit and sorting + _, err = c.Info() + fmt.Println(err) + // Output: Unknown Index name +} diff --git a/redisearch/client_test.go b/redisearch/client_test.go index cfdb2a3..5bd68c7 100644 --- a/redisearch/client_test.go +++ b/redisearch/client_test.go @@ -543,4 +543,33 @@ func TestClient_AddField(t *testing.T) { assert.Nil(t, err) err = c.Index(NewDocument("doc-n1",1.0).Set("age",15 )) assert.Nil(t, err) +} + +func TestClient_CreateIndex(t *testing.T) { + type fields struct { + pool ConnPool + name string + } + type args struct { + s *Schema + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + i := &Client{ + pool: tt.fields.pool, + name: tt.fields.name, + } + if err := i.CreateIndex(tt.args.s); (err != nil) != tt.wantErr { + t.Errorf("CreateIndex() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } } \ No newline at end of file diff --git a/redisearch/example_schema_test.go b/redisearch/example_schema_test.go new file mode 100644 index 0000000..23d0d7a --- /dev/null +++ b/redisearch/example_schema_test.go @@ -0,0 +1,51 @@ +package redisearch_test + +import ( + "fmt" + "github.com/RediSearch/redisearch-go/redisearch" + "log" + "time" +) + +// exemplifies the CreateIndex function with a temporary index specification +func ExampleCreateIndex_temporary() { + // Create a client. By default a client is schemaless + // unless a schema is provided when creating the index + c := redisearch.NewClient("localhost:6379", "myTemporaryIndex") + + // Create a schema with a temporary period of 60seconds + sc := redisearch.NewSchema(*redisearch.NewOptions().SetTemporaryPeriod(10)). + AddField(redisearch.NewTextField("body")). + AddField(redisearch.NewTextFieldOptions("title", redisearch.TextFieldOptions{Weight: 5.0, Sortable: true})). + AddField(redisearch.NewNumericField("date")) + + // Create the index with the given schema + if err := c.CreateIndex(sc); err != nil { + log.Fatal(err) + } + + // Create a document with an id and given score + doc := redisearch.NewDocument("doc1", 1.0) + doc.Set("title", "Hello world"). + Set("body", "foo bar"). + Set("date", time.Now().Unix()) + + // Index the document. The API accepts multiple documents at a time + if err := c.IndexOptions(redisearch.DefaultIndexingOptions, doc); err != nil { + log.Fatal(err) + } + + docs, total, err := c.Search(redisearch.NewQuery("hello world"). + Limit(0, 2). + SetReturnFields("title")) + + // Verify that the we're able to search on the temporary created index + fmt.Println(docs[0].Id, docs[0].Properties["title"], total, err) + + time.Sleep(15 * time.Second) + // Searching with limit and sorting + _, err = c.Info() + fmt.Println(err) + // Output: doc1 Hello world 1 + // Unknown Index name +} diff --git a/redisearch/schema.go b/redisearch/schema.go index 3fa6632..bc28055 100644 --- a/redisearch/schema.go +++ b/redisearch/schema.go @@ -35,6 +35,42 @@ type Options struct { // If the list is nil the default stop-words list is used. // See https://oss.redislabs.com/redisearch/Stopwords.html#default_stop-word_list Stopwords []string + + // If set to true, creates a lightweight temporary index which will expire after the specified period of inactivity. + // The internal idle timer is reset whenever the index is searched or added to. + // Because such indexes are lightweight, you can create thousands of such indexes without negative performance implications. + Temporary bool + TemporaryPeriod int +} + +func NewOptions() *Options { + var opts = DefaultOptions + return &opts +} + +// If set to true, creates a lightweight temporary index which will expire after the specified period of inactivity. +// The internal idle timer is reset whenever the index is searched or added to. +// To enable the temporary index creation, use SetTemporaryPeriod(). This method should be preferably used for disabling the flag +func (options *Options) SetTemporary(temporary bool) *Options { + options.Temporary = temporary + return options +} + +// If set to a positive integer, creates a lightweight temporary index which will expire after the specified period of inactivity (in seconds). +// The internal idle timer is reset whenever the index is searched or added to. +func (options *Options) SetTemporaryPeriod(period int) *Options { + options.TemporaryPeriod = period + options.Temporary = true + return options +} + +// Set the index with a custom stop-words list, to be ignored during indexing and search time +// This is an option that is applied and index level. +// If the list is nil the default stop-words list is used. +// See https://oss.redislabs.com/redisearch/Stopwords.html#default_stop-word_list +func (options *Options) SetStopWords(stopwords []string) *Options { + options.Stopwords = stopwords + return options } // DefaultOptions represents the default options @@ -44,6 +80,8 @@ var DefaultOptions = Options{ NoFrequencies: false, NoOffsetVectors: false, Stopwords: nil, + Temporary: false, + TemporaryPeriod: 0, } const ( @@ -204,15 +242,19 @@ func (m *Schema) AddField(f Field) *Schema { func SerializeSchema(s *Schema, args redis.Args) (argsOut redis.Args, err error) { argsOut = args + if s.Options.NoOffsetVectors { + argsOut = append(argsOut, "NOOFFSETS") + } + if s.Options.Temporary { + argsOut = append(argsOut, "TEMPORARY",s.Options.TemporaryPeriod) + } if s.Options.NoFieldFlags { argsOut = append(argsOut, "NOFIELDS") } if s.Options.NoFrequencies { argsOut = append(argsOut, "NOFREQS") } - if s.Options.NoOffsetVectors { - argsOut = append(argsOut, "NOOFFSETS") - } + if s.Options.Stopwords != nil { argsOut = argsOut.Add("STOPWORDS", len(s.Options.Stopwords)) if len(s.Options.Stopwords) > 0 { @@ -224,7 +266,7 @@ func SerializeSchema(s *Schema, args redis.Args) (argsOut redis.Args, err error) for _, f := range s.Fields { argsOut, err = serializeField(f, argsOut) if err != nil { - return nil,err + return nil, err } } return @@ -300,7 +342,7 @@ func serializeField(f Field, args redis.Args) (argsOut redis.Args, err error) { } } default: - err = fmt.Errorf("Unrecognized field type %v serialization", f.Type ) + err = fmt.Errorf("Unrecognized field type %v serialization", f.Type) return } return diff --git a/redisearch/schema_test.go b/redisearch/schema_test.go index 1f2d725..abf40eb 100644 --- a/redisearch/schema_test.go +++ b/redisearch/schema_test.go @@ -43,10 +43,14 @@ func TestSerializeSchema(t *testing.T) { want redis.Args wantErr bool }{ + {"default-args", args{NewSchema(DefaultOptions), redis.Args{}}, redis.Args{"SCHEMA"}, false}, + {"default-args-with-different-constructor", args{NewSchema(*NewOptions()), redis.Args{}}, redis.Args{"SCHEMA"}, false}, + {"temporary", args{NewSchema(*NewOptions().SetTemporaryPeriod(60)), redis.Args{}}, redis.Args{"TEMPORARY",60,"SCHEMA"}, false}, {"no-frequencies", args{NewSchema(Options{NoFrequencies: true}), redis.Args{}}, redis.Args{"NOFREQS", "SCHEMA"}, false}, {"no-fields", args{NewSchema(Options{NoFieldFlags: true}), redis.Args{}}, redis.Args{"NOFIELDS", "SCHEMA"}, false}, {"custom-stopwords", args{NewSchema(Options{Stopwords: []string{"custom"}}), redis.Args{}}, redis.Args{"STOPWORDS", 1, "custom", "SCHEMA"}, false}, + {"custom-stopwords-with-different-constructor", args{NewSchema(*NewOptions().SetStopWords([]string{"custom"})), redis.Args{}}, redis.Args{"STOPWORDS", 1, "custom", "SCHEMA"}, false}, {"no-offsets", args{NewSchema(Options{NoOffsetVectors: true}), redis.Args{}}, redis.Args{"NOOFFSETS", "SCHEMA"}, false}, {"default-and-numeric", args{NewSchema(DefaultOptions).AddField(NewNumericField("numeric-field")), redis.Args{}}, redis.Args{"SCHEMA", "numeric-field", "NUMERIC"}, false}, {"default-and-numeric-sortable", args{NewSchema(DefaultOptions).AddField(NewSortableNumericField("numeric-field")), redis.Args{}}, redis.Args{"SCHEMA", "numeric-field", "NUMERIC", "SORTABLE"}, false}, From 5fffb458bcf3733920e1577599c900527f93894f Mon Sep 17 00:00:00 2001 From: filipecosta90 Date: Fri, 8 May 2020 14:14:23 +0100 Subject: [PATCH 2/3] [add] updated circleCI config make to use makefile --- .circleci/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index edae4f3..e41af62 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,16 +3,16 @@ # Check https://circleci.com/docs/2.0/language-go/ for more details version: 2 jobs: - build: # test with redisearch:latest + build: # test with redisearch:edge docker: - image: circleci/golang:1.9 - - image: redislabs/redisearch:latest + - image: redislabs/redisearch:edge working_directory: /go/src/github.com/RediSearch/redisearch-go steps: - checkout - - run: go get -v -t -d ./... - - run: go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic + - run: make get + - run: make coverage - run: bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN} build_nightly: # test nightly with redisearch:edge @@ -23,8 +23,8 @@ jobs: working_directory: /go/src/github.com/RediSearch/redisearch-go steps: - checkout - - run: go get -v -t -d ./... - - run: go test -v ./... -race #no need for codecov on nightly + - run: make get + - run: make test workflows: version: 2 From 55aa74eaf2dc6a2fe41144d511fc1c4c04ce6908 Mon Sep 17 00:00:00 2001 From: filipecosta90 Date: Fri, 8 May 2020 14:16:56 +0100 Subject: [PATCH 3/3] [add] pruned the example description --- examples/temporary/temporary.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/temporary/temporary.go b/examples/temporary/temporary.go index 87fd374..c979c6f 100644 --- a/examples/temporary/temporary.go +++ b/examples/temporary/temporary.go @@ -9,7 +9,9 @@ import ( ) /** - * This demo demonstrates the usage of Create a lightweight temporary index which will expire after the specified period of inactivity. The internal idle timer is reset whenever the index is searched or added to. Because such indexes are lightweight, you can create thousands of such indexes without negative performance implications. + * This demo demonstrates the usage of CreateIndex(), to create a lightweight temporary index that will expire after the specified period of inactivity. + * The internal idle timer is reset whenever the index is searched or added to. + * Because such indexes are lightweight, you can create thousands of such indexes without negative performance implications. */ func main() { // Create a client. By default a client is schemaless