Skip to content

Commit

Permalink
support for [TEMPORARY {seconds}] on FT.CREATE (RediSearch#70)
Browse files Browse the repository at this point in the history
* support for [TEMPORARY {seconds}] on FT.CREATE
  • Loading branch information
filipecosta90 committed May 11, 2020
1 parent 51a0cb3 commit 69202df
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 11 deletions.
12 changes: 6 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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

51 changes: 51 additions & 0 deletions examples/quickstart/quickstart.go
Original file line number Diff line number Diff line change
@@ -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 <nil>
}
59 changes: 59 additions & 0 deletions examples/temporary/temporary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package main

import (
"fmt"
"log"
"time"

"github.com/RediSearch/redisearch-go/redisearch"
)

/**
* 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
// 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 <nil>

time.Sleep(15*time.Second)
// Searching with limit and sorting
_, err = c.Info()
fmt.Println(err)
// Output: Unknown Index name
}
29 changes: 29 additions & 0 deletions redisearch/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
})
}
}
51 changes: 51 additions & 0 deletions redisearch/example_schema_test.go
Original file line number Diff line number Diff line change
@@ -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 <nil>
// Unknown Index name
}
52 changes: 47 additions & 5 deletions redisearch/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -44,6 +80,8 @@ var DefaultOptions = Options{
NoFrequencies: false,
NoOffsetVectors: false,
Stopwords: nil,
Temporary: false,
TemporaryPeriod: 0,
}

const (
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions redisearch/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down

0 comments on commit 69202df

Please sign in to comment.