redisearch

package module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 7, 2024 License: MIT Imports: 8 Imported by: 0

README

Redisearch go client

Init client
search := redisearch.New(&redis.Options{
    Network:    "tcp",
    Addr:       "redisAddress",
    Password:   "redisPassword",
    DB:         0,
    MaxRetries: 5,
})
Create Index
err := search.CreateIndex(context.Background(), redisearch.IndexOptions{
    IndexName: "cities",
    Prefix:    []string{"city:"},
    Schema: map[string]redisearch.FieldSchema{
        "name": {
            Type: redisearch.FieldTypeText,
            Options: []redisearch.SchemaOpt{
                redisearch.SchemaOptWeight(2.0),
            },
        },
        "tags": {
            Type: redisearch.FieldTypeTag,
            Options: []redisearch.SchemaOpt{
                redisearch.SchemaOptTagSeparator(','),
            },
        },
        "population": {
            Type: redisearch.FieldTypeNumeric,
            Options: []redisearch.SchemaOpt{
                redisearch.SchemaOptSortable(),
            },
        },
    },
}, true) // warning, if 2nd argument is TRUE, all data matching the index prefix will be deleted from redis
if err != nil {
    println("got error: ", err.Error())
    return
}
println("index created")
Add item to index
// warning: if 3rd argument (override) is true, existing key/val will be deleted BEFORE writing new value to redis
err = search.Add(ctx, "city:popayan", map[string]interface{}{
    "name":       "Popayan",
    "tags":       "colombia,cauca",
    "population": 320000,
}, false)
if err != nil {
    println("got error: ", err.Error())
    return
}
println("value successfully added")

// Note: This is a wrapper of HSET command, and it's usage is optional
// Search results can be parsed in a list of structs or maps
// When using structs,the field names to parse from redis are taken from json tags (if set)
// by default field names will be taken from exported struct fields
var out []struct {
    Name       string `json:"name"`
    Tags       string `json:"tags"`
    Population int    `json:"population"`
}
res, err := search.Search(ctx, redisearch.SearchOptions{
    IndexName: "cities",
    Query:     "Popayan",
}, &out)
if err != nil {
    println("got error: ", err.Error())
    return
}
fmt.Printf("search results: %+v", res)


// Search storing results in map list
var outMap []map[string]string
res, err = search.Search(ctx, redisearch.SearchOptions{
    IndexName: "cities",
    Query:     "Popayan",
}, &outMap)
if err != nil {
    println("got error: ", err.Error())
    return
}
fmt.Printf("search results: %+v", res)
Drop index
// Remove the given index from redisearch.
// if purgeIndexData (last argument) is true, all the data(sets) related to the index will be deleted from redis
err = search.DropIndex(ctx, "cities", false)
if err != nil {
    println("got error: ", err.Error())
    return
}
println("index deleted")
Full example
package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/gustavotero7/redisearch"
)


func main() {
	// Init client
	search := redisearch.New(&redis.Options{
		Network:    "tcp",
		Addr:       "redisAddress",
		Password:   "redisPassword",
		DB:         0,
		MaxRetries: 5,
	})

	ctx := context.Background()
	err := search.CreateIndex(ctx, redisearch.IndexOptions{
		IndexName: "cities",
		Prefix:    []string{"city:"},
		Schema: map[string]redisearch.FieldSchema{
			"name": {
				Type: redisearch.FieldTypeText,
				Options: []redisearch.SchemaOpt{
					redisearch.SchemaOptWeight(2.0),
				},
			},
			"tags": {
				Type: redisearch.FieldTypeTag,
				Options: []redisearch.SchemaOpt{
					redisearch.SchemaOptTagSeparator(','),
				},
			},
			"population": {
				Type: redisearch.FieldTypeNumeric,
				Options: []redisearch.SchemaOpt{
					redisearch.SchemaOptSortable(),
				},
			},
		},
	}, true) // warning, if 2nd argument is TRUE, all data matching the index prefix will be deleted from redis
	if err != nil {
		println("got error: ", err.Error())
		return
	}
	println("index created")


	// Add new item to redis
	// warning: if 3rd argument (override) is true, existing key/val will be deleted BEFORE writing new value to redis
	err = search.Add(ctx, "city:popayan", map[string]interface{}{
		"name":       "Popayan",
		"tags":       "colombia,cauca",
		"population": 320000,
	}, false)
	if err != nil {
		println("got error: ", err.Error())
		return
	}
	println("value successfully added")


	// Search storing results in struct list
	var out []struct {
		Name       string `json:"name"`
		Tags       string `json:"tags"`
		Population int    `json:"population"`
	}
	res, err := search.Search(ctx, redisearch.SearchOptions{
		IndexName: "cities",
		Query:     "Popayan",
	}, &out)
	if err != nil {
		println("got error: ", err.Error())
		return
	}
	fmt.Printf("search results: %+v", res)


	// Search storing results in map list
	var outMap []map[string]string
	res, err = search.Search(ctx, redisearch.SearchOptions{
		IndexName: "cities",
		Query:     "Popayan",
	}, &outMap)
	if err != nil {
		println("got error: ", err.Error())
		return
	}
	fmt.Printf("search results: %+v", res)

	// Drop existing index
	// warning, if 2nd argument is TRUE, all data matching the index prefix will be deleted from redis
	err = search.DropIndex(ctx, "cities", false)
	if err != nil {
		println("got error: ", err.Error())
		return
	}
	println("index deleted")
}

TODO

  • Update parser to support search flags that modifies the number of returned items in search response (NOCONTENT, WITHSCORES, WITHPAYLOADS, WITHSORTKEYS)
  • Query builder

Documentation

Index

Examples

Constants

View Source
const (
	// FieldTypeText Allows full-text search queries against the value in this field.
	FieldTypeText string = "TEXT"
	// FieldTypeTag Allows exact-match queries, such as categories or primary keys,
	// against the value in this field. For more information, see https://oss.redislabs.com/redisearch/Tags/
	FieldTypeTag string = "TAG"
	// FieldTypeNumeric Allows numeric range queries against the value in this field.
	// See https://oss.redislabs.com/redisearch/Query_Syntax/ for details on how to use numeric ranges.
	FieldTypeNumeric string = "NUMERIC"
	// FieldTypeGeo Allows geographic range queries against the value in this field.
	// The value of the field must be a string containing a longitude (first) and latitude separated by a comma.
	FieldTypeGeo string = "GEO"

	// IndexFlagNoOffsets If set, we do not store term offsets for documents (saves memory, does not allow exact searches or highlighting). Implies NOHL .
	IndexFlagNoOffsets string = "NOOFFSETS"
	// IndexFlagNoHl Conserves storage space and memory by disabling highlighting support.
	// If set, we do not store corresponding byte offsets for term positions. NOHL is also implied by NOOFFSETS
	IndexFlagNoHl string = "NOHL"
	// IndexFlagNoFields If set, we do not store field bits for each term. Saves memory, does not allow filtering by specific fields.
	IndexFlagNoFields string = "NOFIELDS"
	// IndexFlagNoFreqs If set, we avoid saving the term frequencies in the index. This saves memory but does not allow sorting based on the frequencies of a given term within the document.
	IndexFlagNoFreqs string = "NOFREQS"
	// IndexFlagSkipInitialScan If set, we do not scan and index.
	IndexFlagSkipInitialScan string = "SKIPINITIALSCAN"
	// IndexFlagMaxTextFields For efficiency, RediSearch encodes indexes differently if they are created with less than 32 text fields.
	// This option forces RediSearch to encode indexes as if there were more than 32 text fields, which allows you to add additional fields (beyond 32) using FT.ALTER .
	IndexFlagMaxTextFields string = "MAXTEXTFIELDS"
	// SearchFlagVerbatim if set, we do not try to use stemming for query expansion but search the query terms verbatim.
	SearchFlagVerbatim = "VERBATIM"
	// SearchFlagNoStopWords If set, we do not filter stopwords from the query.
	SearchFlagNoStopWords = "NOSTOPWORDS"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client interface {
	Search(ctx stdContext.Context, opts SearchOptions, out interface{}) (int64, error)
	CreateIndex(ctx stdContext.Context, opts IndexOptions, dropIfExists bool) error
	DropIndex(ctx stdContext.Context, name string, purgeIndexData bool) error
	IndexExists(ctx stdContext.Context, name string) (bool, error)
	Add(ctx stdContext.Context, key string, value interface{}, override bool) error
	Put(ctx stdContext.Context, key string, value interface{}, override bool) error
	Delete(ctx stdContext.Context, key string) error
}

Client hold basic methods to interact with redisearch module for redis

func New

func New(opts *redis.Options) Client

New return a new redisearch implementation instance

type FieldFilter

type FieldFilter struct {
	NumericFieldName string
	Min              float32
	Max              float32
	Exclusive        bool // Do not include Min and Max, only in between
}

type FieldSchema

type FieldSchema struct {
	// Field types can be numeric, textual or geographical.
	// See FieldDataType constants
	Type    string
	Options []SchemaOpt
}

type GeoFilter

type GeoFilter struct {
	GeoFieldName string
	Latitude     float32
	Longitude    float32
	Radius       float32
	// valid values are m|km|mi|ft
	// by default set to 'm'
	Unit string
}

type Highlight

type Highlight struct {
	// If present, must be the first argument. This should be followed by the number of fields to highlight,
	// which itself is followed by a list of fields. Each field present is highlighted.
	// If no FIELDS directive is passed, then all fields returned are highlighted.
	Fields   []string
	OpenTag  string
	CloseTag string
}

type IndexOptions

type IndexOptions struct {
	// The index name to create. If it exists the old spec will be overwritten
	// This is a REQUIRED field
	IndexName string
	// Tells the index which keys it should index.
	// You can add several prefixes to index. Since the argument is optional, the default is * (all keys)
	Prefix []string
	// {filter} is a filter expression with the full RediSearch aggregation expression language.
	// It is possible to use @__key to access the key that was just added/changed.
	// A field can be used to set field name by passing 'FILTER @indexName=="myindexname"'
	Filter string
	// If set indicates the default language for documents in the index. Default to English.
	// The supported languages are: Arabic, Basque, Catalan, Danish, Dutch, English, Finnish, French, German, Greek, Hungarian,
	// Indonesian, Irish, Italian, Lithuanian, Nepali, Norwegian, Portuguese, Romanian, Russian, Spanish, Swedish, Tamil, Turkish, Chinese
	Language string
	// If set indicates the document field that should be used as the document language.
	LanguageField string
	// If set indicates the default score for documents in the index. Default score is 1.0.
	Score float32
	// If set indicates the document field that should be used as the document's rank based on the user's ranking.
	// Ranking must be between 0.0 and 1.0. If not set the default score is 1.
	ScoreField string
	// If set indicates the document field that should be used as a binary safe payload string to the document,
	// that can be evaluated at query time by a custom scoring function, or retrieved to the client.
	PayloadField string
	// Index options/flags
	Flags []string
	// If set, we set the index with a custom stopword list, to be ignored during indexing and search time.
	// If not set, we take the default list of stopwords.
	StopWords []string
	// 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 and therefore you should consider using SKIPINITIALSCAN to avoid costly scanning.
	Temporary float32
	// After the SCHEMA keyword we define the index fields.
	// The field name is the name of the field within the hashes that this index follows. Field types can be numeric, textual or geographical.
	Schema map[string]FieldSchema
}

type Limit

type Limit struct {
	Offset int
	Max    int
}

type RediSearch

type RediSearch struct {
	// contains filtered or unexported fields
}

RediSearch implements Client

func (*RediSearch) Add

func (r *RediSearch) Add(ctx stdContext.Context, key string, value interface{}, override bool) error

Add legacy name used for Put() DEPRECATED kept for compatibility

Example
search := New(&redis.Options{
	Network:    "tcp",
	Addr:       "redisAddress",
	Password:   "redisPassword",
	DB:         0,
	MaxRetries: 5,
})

ctx := context.Background()
// warning: if 3rd argument (override) is true, existing key/val will be deleted BEFORE writing new value to redis
err := search.Add(ctx, "", map[string]interface{}{
	"name":       "Popayan",
	"tags":       "colombia,cauca",
	"population": 320000,
}, false)
if err != nil {
	println("got error: ", err.Error())
	return
}
println("value successfully added")
Output:

func (*RediSearch) CreateIndex

func (r *RediSearch) CreateIndex(ctx stdContext.Context, opts IndexOptions, dropIfExists bool) error

CreateIndex with the given spec

Example
search := New(&redis.Options{
	Network:    "tcp",
	Addr:       "redisAddress",
	Password:   "redisPassword",
	DB:         0,
	MaxRetries: 5,
})

ctx := context.Background()
err := search.CreateIndex(ctx, IndexOptions{
	IndexName: "cities",
	Prefix:    []string{"city:"},
	Schema: map[string]FieldSchema{
		"name": {
			Type: FieldTypeText,
			Options: []SchemaOpt{
				SchemaOptWeight(2.0),
			},
		},
		"tags": {
			Type: FieldTypeTag,
			Options: []SchemaOpt{
				SchemaOptTagSeparator(','),
			},
		},
		"population": {
			Type: FieldTypeNumeric,
			Options: []SchemaOpt{
				SchemaOptSortable(),
			},
		},
	},
}, true) // warning, if 2nd argument is TRUE, all data matching the index prefix will be deleted from redis
if err != nil {
	println("got error: ", err.Error())
	return
}
println("index created")
Output:

func (*RediSearch) Delete added in v0.2.0

func (r *RediSearch) Delete(ctx stdContext.Context, key string) error

Delete drop document attached to the given key

func (*RediSearch) DropIndex

func (r *RediSearch) DropIndex(ctx stdContext.Context, name string, purgeIndexData bool) error

DropIndex with the given name. Optionally delete all indexed data

Example
search := New(&redis.Options{
	Network:    "tcp",
	Addr:       "redisAddress",
	Password:   "redisPassword",
	DB:         0,
	MaxRetries: 5,
})

ctx := context.Background()
// warning, if 2nd argument is TRUE, all data matching the index prefix will be deleted from redis
err := search.DropIndex(ctx, "cities", false)
if err != nil {
	println("got error: ", err.Error())
	return
}
println("index deleted")
Output:

func (*RediSearch) IndexExists

func (r *RediSearch) IndexExists(ctx stdContext.Context, name string) (bool, error)

IndexExists return true if the index exists

Example
search := New(&redis.Options{
	Network:    "tcp",
	Addr:       "redisAddress",
	Password:   "redisPassword",
	DB:         0,
	MaxRetries: 5,
})

ctx := context.Background()
exists, err := search.IndexExists(ctx, "cities")
if err != nil {
	println("got error: ", err.Error())
	return
}
println("exists: ", exists)
Output:

func (*RediSearch) Put added in v0.2.0

func (r *RediSearch) Put(ctx stdContext.Context, key string, value interface{}, override bool) error

Put simple wrapper for redis.HSet. This is just a utility, you can still use the data added using HSET command key: set key value: map ([string]string or [string]interface{}) or struct to be stored in the set override: Delete precious set to create a fresh one only with the values provided

func (*RediSearch) Search

func (r *RediSearch) Search(ctx stdContext.Context, opts SearchOptions, out interface{}) (int64, error)

Search the index with a textual query

Example
search := New(&redis.Options{
	Network:    "tcp",
	Addr:       "redisAddress",
	Password:   "redisPassword",
	DB:         0,
	MaxRetries: 5,
})

ctx := context.Background()
var out []struct {
	Name       string `json:"name"`
	Tags       string `json:"tags"`
	Population int    `json:"population"`
}
res, err := search.Search(ctx, SearchOptions{
	IndexName: "cities",
	Query:     "Popayan",
}, &out)
if err != nil {
	println("got error: ", err.Error())
	return
}
fmt.Printf("search results: %+v", res)
Output:

Example (Map)
search := New(&redis.Options{
	Network:    "tcp",
	Addr:       "redisAddress",
	Password:   "redisPassword",
	DB:         0,
	MaxRetries: 5,
})

ctx := context.Background()
var out []map[string]string
res, err := search.Search(ctx, SearchOptions{
	IndexName: "cities",
	Query:     "Popayan",
}, &out)
if err != nil {
	println("got error: ", err.Error())
	return
}
fmt.Printf("search results: %+v", res)
Output:

type SchemaOpt

type SchemaOpt []interface{}

func SchemaOptNoIndex

func SchemaOptNoIndex() SchemaOpt

SchemaOptNoIndex Fields can have the NOINDEX option, which means they will not be indexed. This is useful in conjunction with SORTABLE,

to create fields whose update using PARTIAL will not cause full reindexing of the document.
If a field has NOINDEX and doesn't have SORTABLE, it will just be ignored by the index.

func SchemaOptNoStem

func SchemaOptNoStem() SchemaOpt

SchemaOptNoStem Text fields can have the NOSTEM argument which will disable stemming when indexing its values. This may be ideal for things like proper names.

func SchemaOptPhonetic

func SchemaOptPhonetic(matcher string) SchemaOpt

SchemaOptPhonetic Declaring a text field as PHONETIC will perform phonetic matching on it in searches by default. The obligatory {matcher} argument specifies the phonetic algorithm and language used. The following matchers are supported: * dm:en - Double Metaphone for English * dm:fr - Double Metaphone for French * dm:pt - Double Metaphone for Portuguese * dm:es - Double Metaphone for Spanish For more details see Phonetic Matching .

func SchemaOptSortable

func SchemaOptSortable() SchemaOpt

SchemaOptSortable Numeric, tag or text fields can have the optional SORTABLE argument that allows the user to later sort the results by the value of this field (this adds memory overhead so do not declare it on large text fields).

func SchemaOptTagSeparator

func SchemaOptTagSeparator(character byte) SchemaOpt

SchemaOptTagSeparator or TAG fields, indicates how the text contained in the field is to be split into individual tags. The default is , . The value must be a single character

func SchemaOptWeight

func SchemaOptWeight(weight float32) SchemaOpt

SchemaOptWeight For TEXT fields, declares the importance of this field when calculating result accuracy. This is a multiplication factor, and defaults to 1 if not specified.

type SearchOptions

type SearchOptions struct {
	// IndexName The index name. The index must be first created with FT.CREATE .
	IndexName string
	// Query the text query to search. If it's more than a single word, put it in quotes. Refer to query syntax for more details.
	// See https://oss.redislabs.com/redisearch/Query_Syntax/
	Query string
	// Filters numeric_field min max : If set, and numeric_field is defined as a numeric field in FT.CREATE,
	// we will limit results to those having numeric values ranging between min and max
	// min and max follow ZRANGE syntax, and can be -inf , +inf and use ( for exclusive ranges
	Filters []FieldFilter
	// GeoFilter If set, we filter the results to a given radius from lon and lat.
	// Radius is given as a number and units. See GEORADIUS for more details.
	GeoFilter *GeoFilter
	// InKeys If set, we limit the result to a given set of keys specified in the list.
	// Non-existent keys are ignored - unless all the keys are non-existent.
	InKeys []string
	// InFields If set, filter the results to ones appearing only in specific fields of the document, like title or URL.
	InFields []string
	// Return Use this keyword to limit which fields from the document are returned.
	Return []string
	// Summarize Use this option to return only the sections of the field which contain the matched text.
	// See https://oss.redislabs.com/redisearch/Highlight/ for more details
	Summarize *Summarize
	// Highlight Use this option to format occurrences of matched text.
	// See https://oss.redislabs.com/redisearch/Highlight/ for more details
	Highlight *Highlight
	// Slop If set, we allow a maximum of N intervening number of unmatched offsets between phrase terms. (i.e the slop for exact phrases is 0)
	Slop *int
	// Language If set, we use a stemmer for the supplied language during search for query expansion.
	//If querying documents in Chinese, this should be set to chinese in order to properly tokenize the query terms.
	//Defaults to English. If an unsupported language is sent, the command returns an error. See FT.ADD for the list of languages.
	Language string
	// Expander If set, we will use a custom query expander instead of the stemmer.
	// See https://oss.redislabs.com/redisearch/Extensions/
	Expander string
	// Scorer If set, we will use a custom scoring function defined by the user.
	// See https://oss.redislabs.com/redisearch/Extensions/
	Scorer string
	// Payload Add an arbitrary, binary safe payload that will be exposed to custom scoring functions.
	// See https://oss.redislabs.com/redisearch/Extensions/
	Payload string
	// SortBy {field} [ASC|DESC] : If specified, the results are ordered by the value of this field. This applies to both text and numeric fields.
	SortBy *SortBy
	// Limit first num : Limit the results to the offset and number of results given. Note that the offset is zero-indexed.
	// The default is 0 10, which returns 10 items starting from the first result.
	Limit *Limit
	// Flags see SearchFlag* constants
	Flags []string
}

type SortBy

type SortBy struct {
	FieldName  string
	Descending bool
}

type Summarize

type Summarize struct {
	// If present, must be the first argument. This should be followed by the number of fields to summarize,
	// which itself is followed by a list of fields. Each field present is summarized.
	//If no FIELDS directive is passed, then all fields returned are summarized.
	Fields []string
	// How many fragments should be returned. If not specified, a default of 3 is used.
	Fragments int
	// The number of context words each fragment should contain. Context words surround the found term.
	// A higher value will return a larger block of text. If not specified, the default value is 20.
	Length int
	// The string used to divide between individual summary snippets.
	// The default is ... which is common among search engines; but you may override this with any other string if you desire to programmatically divide them later on.
	// You may use a newline sequence, as newlines are stripped from the result body anyway (thus, it will not be conflated with an embedded newline in the text)
	Separator string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL