mirror of https://github.com/go-redis/redis.git
859 lines
22 KiB
Go
859 lines
22 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
)
|
|
|
|
type SearchCmdable interface {
|
|
}
|
|
|
|
type FTCreateOptions struct {
|
|
OnHash bool
|
|
OnJSON bool
|
|
Prefix []interface{}
|
|
Filter string
|
|
DefaultLanguage string
|
|
LanguageField string
|
|
Score float64
|
|
ScoreField string
|
|
PayloadField string
|
|
MaxTextFields int
|
|
NoOffsets bool
|
|
Temporary int
|
|
NoHL bool
|
|
NoFields bool
|
|
NoFreqs bool
|
|
StopWords []interface{}
|
|
SkipInitalScan bool
|
|
}
|
|
|
|
type SearchSchema struct {
|
|
Identifier string
|
|
Attribute string
|
|
AttributeType string
|
|
Sortable bool
|
|
UNF bool
|
|
NoStem bool
|
|
NoIndex bool
|
|
PhoneticMatcher string
|
|
Weight float64
|
|
Seperator string
|
|
CaseSensitive bool
|
|
WithSuffix bool
|
|
}
|
|
type FTDropIndexOptions struct {
|
|
DeleteDocs bool
|
|
}
|
|
|
|
type SpellCheckTerms struct {
|
|
Include bool
|
|
Exclude bool
|
|
Dictionary string
|
|
}
|
|
|
|
type FTSpellCheckOptions struct {
|
|
Distance int
|
|
Terms SpellCheckTerms
|
|
Dialect int
|
|
}
|
|
|
|
type FTExplainOptions struct {
|
|
Dialect string
|
|
}
|
|
|
|
type FTSynUpdateOptions struct {
|
|
SkipInitialScan bool
|
|
}
|
|
|
|
type SearchAggregator int
|
|
|
|
const (
|
|
SearchInvalid = SearchAggregator(iota)
|
|
SearchAvg
|
|
SearchSum
|
|
SearchMin
|
|
SearchMax
|
|
SearchCount
|
|
SearchCountDistinct
|
|
SearchCountDistinctish
|
|
SearchStdDev
|
|
SearchQuantile
|
|
SearchToList
|
|
SearchFirstValue
|
|
SearchRandomSample
|
|
)
|
|
|
|
func (a SearchAggregator) String() string {
|
|
switch a {
|
|
case SearchInvalid:
|
|
return ""
|
|
case SearchAvg:
|
|
return "AVG"
|
|
case SearchSum:
|
|
return "SUM"
|
|
case SearchMin:
|
|
return "MIN"
|
|
case SearchMax:
|
|
return "MAX"
|
|
case SearchCount:
|
|
return "COUNT"
|
|
case SearchCountDistinct:
|
|
return "COUNT_DISTINCT"
|
|
case SearchCountDistinctish:
|
|
return "COUNT_DISTINCTISH"
|
|
case SearchStdDev:
|
|
return "STDDEV"
|
|
case SearchQuantile:
|
|
return "QUANTILE"
|
|
case SearchToList:
|
|
return "TOLIST"
|
|
case SearchFirstValue:
|
|
return "FIRST_VALUE"
|
|
case SearchRandomSample:
|
|
return "RANDOM_SAMPLE"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// Each AggregateReducer have different args.
|
|
// Please follow https://redis.io/docs/interact/search-and-query/search/aggregations/#supported-groupby-reducers for more information.
|
|
type FTAggregateReducer struct {
|
|
Reducer SearchAggregator
|
|
Args []interface{}
|
|
As string
|
|
}
|
|
|
|
type FTAggregateGroupBy struct {
|
|
Fields []interface{}
|
|
Reduce []FTAggregateReducer
|
|
}
|
|
|
|
type FTAggregateSortBy struct {
|
|
FieldName string
|
|
Asc bool
|
|
Desc bool
|
|
}
|
|
|
|
type FTAggregateApply struct {
|
|
Field string
|
|
As string
|
|
}
|
|
|
|
type FTAggregateLoad struct {
|
|
Field string
|
|
As string
|
|
}
|
|
|
|
type FTAggregateWithCursor struct {
|
|
Count int
|
|
MaxIdle int
|
|
}
|
|
|
|
type FTAggregateOptions struct {
|
|
Verbatim bool
|
|
LoadAll bool
|
|
Load []FTAggregateLoad
|
|
Timeout int
|
|
GroupBy []FTAggregateGroupBy
|
|
SortBy []FTAggregateSortBy
|
|
SortByMax int
|
|
Apply []FTAggregateApply
|
|
LimitOffset int
|
|
Limit int
|
|
Filter string
|
|
WithCursor bool
|
|
WithCursorOptions *FTAggregateWithCursor
|
|
Params map[string]interface{}
|
|
DialectVersion int
|
|
}
|
|
|
|
type FTSearchFilter struct {
|
|
FieldName interface{}
|
|
Min interface{}
|
|
Max interface{}
|
|
}
|
|
|
|
type FTSearchGeoFilter struct {
|
|
FieldName string
|
|
Longitude float64
|
|
Latitude float64
|
|
Radius float64
|
|
Unit string
|
|
}
|
|
|
|
type FTSearchReturn struct {
|
|
FieldName string
|
|
As string
|
|
}
|
|
|
|
type FTSearchSortBy struct {
|
|
FieldName string
|
|
Asc bool
|
|
Desc bool
|
|
}
|
|
|
|
type FTSearchOptions struct {
|
|
NoContent bool
|
|
Verbatim bool
|
|
NoStopWrods bool
|
|
WithScores bool
|
|
WithPayloads bool
|
|
WithSortKeys bool
|
|
Filters []FTSearchFilter
|
|
GeoFilter []FTSearchGeoFilter
|
|
InKeys []interface{}
|
|
InFields []interface{}
|
|
Return []FTSearchReturn
|
|
Slop int
|
|
Timeout int
|
|
InOrder bool
|
|
Language string
|
|
Expander string
|
|
Scorer string
|
|
ExplainScore bool
|
|
Payload string
|
|
SortBy []FTSearchSortBy
|
|
SortByWithCount bool
|
|
LimitOffset int
|
|
Limit int
|
|
Params map[string]interface{}
|
|
DialectVersion int
|
|
}
|
|
|
|
func (c cmdable) FT_List(ctx context.Context) *StringSliceCmd {
|
|
cmd := NewStringSliceCmd(ctx, "FT._LIST")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTAggregate(ctx context.Context, index string, query string) *MapStringInterfaceCmd {
|
|
args := []interface{}{"FT.AGGREGATE", index, query}
|
|
cmd := NewMapStringInterfaceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTAggregateWithArgs(ctx context.Context, index string, query string, options *FTAggregateOptions) *MapStringInterfaceCmd {
|
|
args := []interface{}{"FT.AGGREGATE", index, query}
|
|
if options != nil {
|
|
if options.Verbatim {
|
|
args = append(args, "VERBATIM")
|
|
}
|
|
if options.LoadAll && options.Load != nil {
|
|
panic("FT.AGGREGATE: LOADALL and LOAD are mutually exclusive")
|
|
}
|
|
if options.LoadAll {
|
|
args = append(args, "LOAD", "*")
|
|
}
|
|
if options.Load != nil {
|
|
args = append(args, "LOAD", len(options.Load))
|
|
for _, load := range options.Load {
|
|
args = append(args, load.Field)
|
|
if load.As != "" {
|
|
args = append(args, "AS", load.As)
|
|
}
|
|
}
|
|
}
|
|
if options.Timeout > 0 {
|
|
args = append(args, "TIMEOUT", options.Timeout)
|
|
}
|
|
if options.GroupBy != nil {
|
|
for _, groupBy := range options.GroupBy {
|
|
args = append(args, "GROUPBY", len(groupBy.Fields))
|
|
args = append(args, groupBy.Fields...)
|
|
|
|
for _, reducer := range groupBy.Reduce {
|
|
args = append(args, "REDUCE")
|
|
args = append(args, reducer.Reducer.String())
|
|
if reducer.Args != nil {
|
|
args = append(args, len(reducer.Args))
|
|
args = append(args, reducer.Args...)
|
|
} else {
|
|
args = append(args, 0)
|
|
}
|
|
if reducer.As != "" {
|
|
args = append(args, "AS", reducer.As)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if options.SortBy != nil {
|
|
args = append(args, "SORTBY")
|
|
sortByOptions := []interface{}{}
|
|
for _, sortBy := range options.SortBy {
|
|
sortByOptions = append(sortByOptions, sortBy.FieldName)
|
|
if sortBy.Asc && sortBy.Desc {
|
|
panic("FT.AGGREGATE: ASC and DESC are mutually exclusive")
|
|
}
|
|
if sortBy.Asc {
|
|
sortByOptions = append(sortByOptions, "ASC")
|
|
}
|
|
if sortBy.Desc {
|
|
sortByOptions = append(sortByOptions, "DESC")
|
|
}
|
|
}
|
|
args = append(args, len(sortByOptions))
|
|
args = append(args, sortByOptions...)
|
|
}
|
|
if options.SortByMax > 0 {
|
|
args = append(args, "MAX", options.SortByMax)
|
|
}
|
|
if options.Apply != nil {
|
|
args = append(args, "APPLY", len(options.Apply))
|
|
for _, apply := range options.Apply {
|
|
args = append(args, apply.Field)
|
|
if apply.As != "" {
|
|
args = append(args, "AS", apply.As)
|
|
}
|
|
}
|
|
}
|
|
if options.LimitOffset > 0 {
|
|
args = append(args, "LIMIT", options.LimitOffset)
|
|
}
|
|
if options.Limit > 0 {
|
|
args = append(args, options.Limit)
|
|
}
|
|
if options.Filter != "" {
|
|
args = append(args, "FILTER", options.Filter)
|
|
}
|
|
if options.WithCursor {
|
|
args = append(args, "WITHCURSOR")
|
|
if options.WithCursorOptions != nil {
|
|
if options.WithCursorOptions.Count > 0 {
|
|
args = append(args, "COUNT", options.WithCursorOptions.Count)
|
|
}
|
|
if options.WithCursorOptions.MaxIdle > 0 {
|
|
args = append(args, "MAXIDLE", options.WithCursorOptions.MaxIdle)
|
|
}
|
|
}
|
|
}
|
|
if options.Params != nil {
|
|
args = append(args, "PARAMS", len(options.Params)*2)
|
|
for key, value := range options.Params {
|
|
args = append(args, key, value)
|
|
}
|
|
}
|
|
if options.DialectVersion > 0 {
|
|
args = append(args, "DIALECT", options.DialectVersion)
|
|
}
|
|
}
|
|
|
|
cmd := NewMapStringInterfaceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTAliasAdd(ctx context.Context, index string, alias string) *StatusCmd {
|
|
args := []interface{}{"FT.ALIASADD", alias, index}
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTAliasDel(ctx context.Context, alias string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "FT.ALIASDEL", alias)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTAliasUpdate(ctx context.Context, index string, alias string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "FT.ALIASUPDATE", alias, index)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTAlter(ctx context.Context, index string, skipInitalScan bool, definition []interface{}) *StatusCmd {
|
|
args := []interface{}{"FT.ALTER", index}
|
|
if skipInitalScan {
|
|
args = append(args, "SKIPINITIALSCAN")
|
|
}
|
|
args = append(args, "SCHEMA", "ADD")
|
|
args = append(args, definition...)
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTConfigGet(ctx context.Context, option string) *MapStringInterfaceCmd {
|
|
cmd := NewMapStringInterfaceCmd(ctx, "FT.CONFIG", "GET", option)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTConfigSet(ctx context.Context, option string, value interface{}) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "FT.CONFIG", "SET", option, value)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// TODO Fix schema for loop
|
|
func (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOptions, schema ...*SearchSchema) *StatusCmd {
|
|
args := []interface{}{"FT.CREATE", index}
|
|
if options != nil {
|
|
if options.OnHash && !options.OnJSON {
|
|
args = append(args, "ON", "HASH")
|
|
}
|
|
if options.OnJSON && !options.OnHash {
|
|
args = append(args, "ON", "JSON")
|
|
}
|
|
if options.OnHash && options.OnJSON {
|
|
panic("FT.CREATE: ON HASH and ON JSON are mutually exclusive")
|
|
}
|
|
if options.Prefix != nil {
|
|
args = append(args, "PREFIX", len(options.Prefix))
|
|
args = append(args, options.Prefix...)
|
|
}
|
|
if options.Filter != "" {
|
|
args = append(args, "FILTER", options.Filter)
|
|
}
|
|
if options.DefaultLanguage != "" {
|
|
args = append(args, "LANGUAGE", options.DefaultLanguage)
|
|
}
|
|
if options.LanguageField != "" {
|
|
args = append(args, "LANGUAGE_FIELD", options.LanguageField)
|
|
}
|
|
if options.Score > 0 {
|
|
args = append(args, "SCORE", options.Score)
|
|
}
|
|
if options.ScoreField != "" {
|
|
args = append(args, "SCORE_FIELD", options.ScoreField)
|
|
}
|
|
if options.PayloadField != "" {
|
|
args = append(args, "PAYLOAD_FIELD", options.PayloadField)
|
|
}
|
|
if options.MaxTextFields > 0 {
|
|
args = append(args, "MAXTEXTFIELDS", options.MaxTextFields)
|
|
}
|
|
if options.NoOffsets {
|
|
args = append(args, "NOOFFSETS")
|
|
}
|
|
if options.Temporary > 0 {
|
|
args = append(args, "TEMPORARY", options.Temporary)
|
|
}
|
|
if options.NoHL {
|
|
args = append(args, "NOHL")
|
|
}
|
|
if options.NoFields {
|
|
args = append(args, "NOFIELDS")
|
|
}
|
|
if options.NoFreqs {
|
|
args = append(args, "NOFREQS")
|
|
}
|
|
if options.StopWords != nil {
|
|
args = append(args, "STOPWORDS", len(options.StopWords))
|
|
args = append(args, options.StopWords...)
|
|
}
|
|
if options.SkipInitalScan {
|
|
args = append(args, "SKIPINITIALSCAN")
|
|
}
|
|
}
|
|
if schema == nil {
|
|
panic("FT.CREATE: SCHEMA is required")
|
|
}
|
|
args = append(args, "SCHEMA")
|
|
for _, schema := range schema {
|
|
if schema.Identifier == "" || schema.AttributeType == "" {
|
|
panic("FT.CREATE: SCHEMA IDENTIFIER and ATTRIBUTE_TYPE are required")
|
|
}
|
|
args = append(args, schema.Identifier)
|
|
if schema.Attribute != "" {
|
|
args = append(args, "AS", schema.Attribute)
|
|
}
|
|
args = append(args, schema.AttributeType)
|
|
if schema.NoStem {
|
|
args = append(args, "NOSTEM")
|
|
}
|
|
if schema.Sortable {
|
|
args = append(args, "SORTABLE")
|
|
}
|
|
if schema.UNF {
|
|
args = append(args, "UNF")
|
|
}
|
|
if schema.NoIndex {
|
|
args = append(args, "NOINDEX")
|
|
}
|
|
if schema.PhoneticMatcher != "" {
|
|
args = append(args, "PHONETIC", schema.PhoneticMatcher)
|
|
}
|
|
if schema.Weight > 0 {
|
|
args = append(args, "WEIGHT", schema.Weight)
|
|
}
|
|
if schema.Seperator != "" {
|
|
args = append(args, "SEPERATOR", schema.Seperator)
|
|
}
|
|
if schema.CaseSensitive {
|
|
args = append(args, "CASESENSITIVE")
|
|
}
|
|
if schema.WithSuffix {
|
|
args = append(args, "WITHSUFFIX")
|
|
}
|
|
}
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTCursorDel(ctx context.Context, index string, cursorId int) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "FT.CURSOR", "DEL", index, cursorId)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// TODO TEST IT
|
|
func (c cmdable) FTCursorRead(ctx context.Context, index string, cursorId int, count int) *MapStringInterfaceCmd {
|
|
args := []interface{}{"FT.CURSOR", "READ", index, cursorId}
|
|
if count > 0 {
|
|
args = append(args, "COUNT", count)
|
|
}
|
|
cmd := NewMapStringInterfaceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTDictAdd(ctx context.Context, dict string, term []interface{}) *IntCmd {
|
|
args := []interface{}{"FT.DICTADD", dict}
|
|
args = append(args, term...)
|
|
cmd := NewIntCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTDictDel(ctx context.Context, dict string, term []interface{}) *IntCmd {
|
|
args := []interface{}{"FT.DICTDEL", dict}
|
|
args = append(args, term...)
|
|
cmd := NewIntCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTDictDump(ctx context.Context, dict string) *StringSliceCmd {
|
|
cmd := NewStringSliceCmd(ctx, "FT.DICTDUMP", dict)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTDropIndex(ctx context.Context, index string) *StatusCmd {
|
|
args := []interface{}{"FT.DROPINDEX", index}
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTDropIndexWithArgs(ctx context.Context, index string, options *FTDropIndexOptions) *StatusCmd {
|
|
args := []interface{}{"FT.DROPINDEX", index}
|
|
if options != nil {
|
|
if options.DeleteDocs {
|
|
args = append(args, "DD")
|
|
}
|
|
}
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTExplain(ctx context.Context, index string, query string) *StringCmd {
|
|
cmd := NewStringCmd(ctx, "FT.EXPLAIN", index, query)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTExplainWithArgs(ctx context.Context, index string, query string, options *FTExplainOptions) *StringCmd {
|
|
args := []interface{}{"FT.EXPLAIN", index, query}
|
|
if options.Dialect != "" {
|
|
args = append(args, "DIALECT", options.Dialect)
|
|
}
|
|
cmd := NewStringCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTInfo(ctx context.Context, index string) *MapStringInterfaceCmd {
|
|
cmd := NewMapStringInterfaceCmd(ctx, "FT.INFO", index)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// TODO - When ft search is ready
|
|
// func (c cmdable) FTProfileSearch(ctx context.Context, index string, limited bool, query string) *StringCmd {
|
|
// args := []interface{}{"FT.PROFILE", index, "SEARCH"}
|
|
// if limited {
|
|
// args = append(args, "LIMITED")
|
|
// }
|
|
// args = append(args, "QUERY", query)
|
|
|
|
// cmd := NewStringCmd(ctx, args...)
|
|
// _ = c(ctx, cmd)
|
|
// return cmd
|
|
// }
|
|
|
|
// func (c cmdable) FTProfileAggregate(ctx context.Context, index string, limited bool, query string) *FTProfileAggregateCmd {
|
|
// args := []interface{}{"FT.PROFILE", index, "AGGREGATE"}
|
|
// if limited {
|
|
// args = append(args, "LIMITED")
|
|
// }
|
|
// args = append(args, "QUERY", query)
|
|
|
|
// cmd := newFTProfileAggregateCmd(ctx, args...)
|
|
// _ = c(ctx, cmd)
|
|
// return cmd
|
|
// }
|
|
|
|
// type FTProfileAggregateResult struct {
|
|
// aggregateResult MapStringInterfaceCmd
|
|
// profileResult KeyValueSliceCmd
|
|
// }
|
|
|
|
// type FTProfileAggregateCmd struct {
|
|
// baseCmd
|
|
|
|
// val FTProfileAggregateResult
|
|
// }
|
|
|
|
// func newFTProfileAggregateCmd(ctx context.Context, args ...interface{}) *FTProfileAggregateCmd {
|
|
// return &FTProfileAggregateCmd{
|
|
// baseCmd: baseCmd{
|
|
// ctx: ctx,
|
|
// args: args,
|
|
// },
|
|
// }
|
|
// }
|
|
|
|
// func (cmd *FTProfileAggregateCmd) String() string {
|
|
// return cmdString(cmd, cmd.val)
|
|
// }
|
|
|
|
// func (cmd *FTProfileAggregateCmd) SetVal(val FTProfileAggregateResult) {
|
|
// cmd.val = val
|
|
// }
|
|
|
|
// func (cmd *FTProfileAggregateCmd) Result() (FTProfileAggregateResult, error) {
|
|
// return cmd.val, cmd.err
|
|
// }
|
|
|
|
// func (cmd *FTProfileAggregateCmd) Val() FTProfileAggregateResult {
|
|
// return cmd.val
|
|
// }
|
|
|
|
// func (cmd *FTProfileAggregateCmd) readReply(rd *proto.Reader) (err error) {
|
|
// _, err = rd.ReadArrayLen()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// _, err = rd.ReadArrayLen()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// cmd.val = FTProfileAggregateResult{}
|
|
// status, err := rd.ReadInt()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// cmd.val.aggregateResult.Status = status
|
|
// nn, err := rd.ReadArrayLen()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// cmd.val.aggregateResult.Fields = make(map[string]string, nn/2)
|
|
// for i := 0; i < nn; i++ {
|
|
// key, err := rd.ReadString()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// value, err := rd.ReadString()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// cmd.val.aggregateResult.Fields[key] = value
|
|
// }
|
|
// cmd.val.profileResult = *NewKeyValueSliceCmd(cmd.ctx, cmd.args...)
|
|
// return nil
|
|
|
|
// }
|
|
|
|
// For more details about spellcheck query please follow:
|
|
// https://redis.io/docs/interact/search-and-query/advanced-concepts/spellcheck/
|
|
func (c cmdable) FTSpellCheck(ctx context.Context, index string, query string) *MapStringInterfaceCmd {
|
|
cmd := NewMapStringInterfaceCmd(ctx, "FT.SPELLCHECK", index, query)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTSpellCheckWithArgs(ctx context.Context, index string, query string, options *FTSpellCheckOptions) *MapStringInterfaceCmd {
|
|
args := []interface{}{"FT.SPELLCHECK", index, query}
|
|
if options != nil {
|
|
if options.Distance > 4 {
|
|
panic("FT.SPELLCHECK: DISTANCE must be between 0 and 4")
|
|
}
|
|
if options.Distance > 0 {
|
|
args = append(args, "DISTANCE", options.Distance)
|
|
}
|
|
if options.Terms.Include && options.Terms.Exclude {
|
|
panic("FT.SPELLCHECK: INCLUDE and EXCLUDE are mutually exclusive")
|
|
}
|
|
if options.Terms.Include {
|
|
args = append(args, "TERMS", "INCLUDE")
|
|
}
|
|
if options.Terms.Exclude {
|
|
args = append(args, "TERMS", "EXCLUDE")
|
|
}
|
|
if options.Terms.Dictionary != "" {
|
|
args = append(args, options.Terms.Dictionary)
|
|
}
|
|
if options.Dialect > 0 {
|
|
args = append(args, "DIALECT", options.Dialect)
|
|
}
|
|
}
|
|
cmd := NewMapStringInterfaceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTSearch(ctx context.Context, index string, query string) *Cmd {
|
|
args := []interface{}{"FT.SEARCH", index, query}
|
|
cmd := NewCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTSearchWithArgs(ctx context.Context, index string, query string, options *FTSearchOptions) *Cmd {
|
|
args := []interface{}{"FT.SEARCH", index, query}
|
|
if options != nil {
|
|
if options.NoContent {
|
|
args = append(args, "NOCONTENT")
|
|
}
|
|
if options.Verbatim {
|
|
args = append(args, "VERBATIM")
|
|
}
|
|
if options.NoStopWrods {
|
|
args = append(args, "NOSTOPWORDS")
|
|
}
|
|
if options.WithScores {
|
|
args = append(args, "WITHSCORES")
|
|
}
|
|
if options.WithPayloads {
|
|
args = append(args, "WITHPAYLOADS")
|
|
}
|
|
if options.WithSortKeys {
|
|
args = append(args, "WITHSORTKEYS")
|
|
}
|
|
if options.Filters != nil {
|
|
for _, filter := range options.Filters {
|
|
args = append(args, "FILTER", filter.FieldName, filter.Min, filter.Max)
|
|
}
|
|
}
|
|
if options.GeoFilter != nil {
|
|
for _, geoFilter := range options.GeoFilter {
|
|
args = append(args, "GEOFILTER", geoFilter.FieldName, geoFilter.Longitude, geoFilter.Latitude, geoFilter.Radius, geoFilter.Unit)
|
|
}
|
|
}
|
|
if options.InKeys != nil {
|
|
args = append(args, "INKEYS", len(options.InKeys))
|
|
args = append(args, options.InKeys...)
|
|
}
|
|
if options.InFields != nil {
|
|
args = append(args, "INFIELDS", len(options.InFields))
|
|
args = append(args, options.InFields...)
|
|
}
|
|
if options.Return != nil {
|
|
args = append(args, "RETURN", len(options.Return))
|
|
for _, ret := range options.Return {
|
|
args = append(args, ret.FieldName)
|
|
if ret.As != "" {
|
|
args = append(args, "AS", ret.As)
|
|
}
|
|
}
|
|
}
|
|
if options.Slop > 0 {
|
|
args = append(args, "SLOP", options.Slop)
|
|
}
|
|
if options.Timeout > 0 {
|
|
args = append(args, "TIMEOUT", options.Timeout)
|
|
}
|
|
if options.InOrder {
|
|
args = append(args, "INORDER")
|
|
}
|
|
if options.Language != "" {
|
|
args = append(args, "LANGUAGE", options.Language)
|
|
}
|
|
if options.Expander != "" {
|
|
args = append(args, "EXPANDER", options.Expander)
|
|
}
|
|
if options.Scorer != "" {
|
|
args = append(args, "SCORER", options.Scorer)
|
|
}
|
|
if options.ExplainScore {
|
|
args = append(args, "EXPLAINSCORE")
|
|
}
|
|
if options.Payload != "" {
|
|
args = append(args, "PAYLOAD", options.Payload)
|
|
}
|
|
if options.SortBy != nil {
|
|
args = append(args, "SORTBY")
|
|
for _, sortBy := range options.SortBy {
|
|
args = append(args, sortBy.FieldName)
|
|
if sortBy.Asc && sortBy.Desc {
|
|
panic("FT.SEARCH: ASC and DESC are mutually exclusive")
|
|
}
|
|
if sortBy.Asc {
|
|
args = append(args, "ASC")
|
|
}
|
|
if sortBy.Desc {
|
|
args = append(args, "DESC")
|
|
}
|
|
}
|
|
if options.SortByWithCount {
|
|
args = append(args, "WITHCOUT")
|
|
}
|
|
}
|
|
if options.LimitOffset >= 0 && options.Limit > 0 {
|
|
args = append(args, "LIMIT", options.LimitOffset, options.Limit)
|
|
}
|
|
if options.Params != nil {
|
|
args = append(args, "PARAMS", len(options.Params)*2)
|
|
for key, value := range options.Params {
|
|
args = append(args, key, value)
|
|
}
|
|
}
|
|
if options.DialectVersion > 0 {
|
|
args = append(args, "DIALECT", options.DialectVersion)
|
|
}
|
|
}
|
|
cmd := NewCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTSynDump(ctx context.Context, index string) *StringSliceCmd {
|
|
cmd := NewStringSliceCmd(ctx, "FT.SYNDUMP", index)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTSynUpdate(ctx context.Context, index string, synGroupId interface{}, terms []interface{}) *StatusCmd {
|
|
args := []interface{}{"FT.SYNUPDATE", index, synGroupId}
|
|
args = append(args, terms...)
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTSynUpdateWithArgs(ctx context.Context, index string, synGroupId interface{}, options *FTSynUpdateOptions, terms []interface{}) *StatusCmd {
|
|
args := []interface{}{"FT.SYNUPDATE", index, synGroupId}
|
|
if options.SkipInitialScan {
|
|
args = append(args, "SKIPINITIALSCAN")
|
|
}
|
|
args = append(args, terms...)
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FTTagVals(ctx context.Context, index string, field string) *StringSliceCmd {
|
|
cmd := NewStringSliceCmd(ctx, "FT.TAGVALS", index, field)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|