RediSearch / redisearch-go

Go client for RediSearch
https://redisearch.io
BSD 3-Clause "New" or "Revised" License
288 stars 65 forks source link

The Info() method missing `FieldOptions` info #171

Closed shumin1027 closed 1 year ago

shumin1027 commented 1 year ago

RediSearch version: v2.2.10

The Info() method missing important FieldOptions info Ref: https://redis.io/commands/ft.create/#field-options for example:

Cause Analysis:

  1. while fixing this issue:#141,missing identifier information https://github.com/RediSearch/redisearch-go/blob/81cf50130875f104f2c32c59fcadd544f2179c80/redisearch/client.go#L591-L597

  2. if a field or alias named type, this code will go wrong https://github.com/RediSearch/redisearch-go/blob/81cf50130875f104f2c32c59fcadd544f2179c80/redisearch/client.go#L526

  3. FieldOptions is not processed correctly and completely

shumin1027 commented 1 year ago

@chayim @Avital-Fine maybe this can fix the problem,but it seems that some options(such as WITHSUFFIXTRIE UNF ) are still missing, and lack of support for VECTOR field type Ref: https://redis.io/commands/ft.create/#field-options

diff --git a/redisearch/client.go b/redisearch/client.go
index f74f0b7..51e7669 100644
--- a/redisearch/client.go
+++ b/redisearch/client.go
@@ -471,6 +471,16 @@ func sliceIndex(haystack []string, needle string) int {
        return -1
 }

+func sliceIndexLast(haystack []string, needle string) int {
+       index := -1
+       for pos, elem := range haystack {
+               if elem == needle {
+                       index = pos
+               }
+       }
+       return index
+}
+
 func (info *IndexInfo) loadSchema(values []interface{}, options []string) {
        // Values are a list of fields
        scOptions := Options{}
@@ -523,26 +533,59 @@ func (info *IndexInfo) loadSchema(values []interface{}, options []string) {
                }

                f := Field{Name: spec[sliceIndex(spec, "identifier")+1]}
-               switch strings.ToUpper(spec[sliceIndex(spec, "type")+1]) {
+               switch strings.ToUpper(spec[sliceIndexLast(spec, "type")+1]) {
                case "TAG":
                        f.Type = TagField
-                       tfOptions := TagFieldOptions{}
+                       tfOptions := TagFieldOptions{
+                               As: options[0],
+                       }
+                       if sliceIndex(options, "NOINDEX") != -1 {
+                               tfOptions.NoIndex = true
+                       }
+                       if sliceIndex(options, "SORTABLE") != -1 {
+                               tfOptions.Sortable = true
+                       }
+                       if sliceIndex(options, "CASESENSITIVE") != -1 {
+                               tfOptions.CaseSensitive = true
+                       }
                        if wIdx := sliceIndex(options, "SEPARATOR"); wIdx != -1 {
                                tfOptions.Separator = options[wIdx+1][0]
                        }
                        f.Options = tfOptions
+                       f.Sortable = tfOptions.Sortable
                case "GEO":
                        f.Type = GeoField
+                       gfOptions := GeoFieldOptions{
+                               As: options[0],
+                       }
+                       if sliceIndex(options, "NOINDEX") != -1 {
+                               gfOptions.NoIndex = true
+                       }
+                       f.Options = gfOptions
                case "NUMERIC":
                        f.Type = NumericField
-                       nfOptions := NumericFieldOptions{}
+                       nfOptions := NumericFieldOptions{
+                               As: options[0],
+                       }
+                       if sliceIndex(options, "NOINDEX") != -1 {
+                               nfOptions.NoIndex = true
+                       }
                        if sliceIndex(options, "SORTABLE") != -1 {
                                nfOptions.Sortable = true
                        }
                        f.Options = nfOptions
+                       f.Sortable = nfOptions.Sortable
                case "TEXT":
                        f.Type = TextField
-                       tfOptions := TextFieldOptions{}
+                       tfOptions := TextFieldOptions{
+                               As: options[0],
+                       }
+                       if sliceIndex(options, "NOSTEM") != -1 {
+                               tfOptions.NoStem = true
+                       }
+                       if sliceIndex(options, "NOINDEX") != -1 {
+                               tfOptions.NoIndex = true
+                       }
                        if sliceIndex(options, "SORTABLE") != -1 {
                                tfOptions.Sortable = true
                        }
@@ -552,6 +595,7 @@ func (info *IndexInfo) loadSchema(values []interface{}, options []string) {
                                tfOptions.Weight = float32(weight64)
                        }
                        f.Options = tfOptions
+                       f.Sortable = tfOptions.Sortable
                case "VECTOR":
                        f.Type = VectorField
                        f.Options = VectorFieldOptions{}
@@ -589,11 +633,7 @@ func (i *Client) Info() (*IndexInfo, error) {
                case "fields":
                        schemaAttributes, _ = redis.Values(res[ii+1], nil)
                case "attributes":
-                       for _, attr := range res[ii+1].([]interface{}) {
-                               l := len(attr.([]interface{}))
-                               schemaAttributes = append(schemaAttributes, attr.([]interface{})[3:l])
-
-                       }
+                       schemaAttributes, _ = redis.Values(res[ii+1], nil)
                }
        }
Avital-Fine commented 1 year ago

Hey @shumin1027, What version of RediSearch are you using?

The name "fields" had been changed to "attributes", so try to use a new version and see if it works for you. I think that this change should help you:

case "attributes", "fields":
    for _, attr := range res[ii+1].([]interface{}) {
        l := len(attr.([]interface{}))
    schemaAttributes = append(schemaAttributes, attr.([]interface{})[3:l])
    }
}

You can see how to get to the FieldOptions in the test here

Please let me know if it works and I'll make a PR.

shumin1027 commented 1 year ago

Hey @shumin1027, What version of RediSearch are you using?

The name "fields" had been changed to "attributes", so try to use a new version and see if it works for you. I think that this change should help you:

case "attributes", "fields":
    for _, attr := range res[ii+1].([]interface{}) {
        l := len(attr.([]interface{}))
  schemaAttributes = append(schemaAttributes, attr.([]interface{})[3:l])
    }
}

You can see how to get to the FieldOptions in the test here

Please let me know if it works and I'll make a PR.

@Avital-Fine thank you , I know this question:#141 , but the code is buggy, and there are so many questions about FieldOptions, I have added issue information, you can take a look