Skip to content

Commit f30b262

Browse files
authored
FT.SEARCH (#297)
* FT.SEARCH
1 parent f9d3d8a commit f30b262

File tree

20 files changed

+1068
-110
lines changed

20 files changed

+1068
-110
lines changed

data/dump.rdb

-14.6 MB
Binary file not shown.

docker-compose.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ version: '3.4'
22

33
services:
44
redis:
5-
container_name: redismod
6-
image: redislabs/redismod:latest
5+
image: redis/redis-stack-server:latest
76
ports:
87
- '6379:6379'
98

docker-compose/dev.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ version: '3.4'
22

33
services:
44
redis:
5-
container_name: redismod
6-
image: redislabs/redismod:latest
5+
image: redis/redis-stack-server
76
ports:
87
- '6379:6379'
98
volumes:

docker-compose/master.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ version: '3.4'
22

33
services:
44
redis:
5-
container_name: redismod
6-
image: redislabs/redismod:latest
5+
image: redis/redis-stack-server
76
ports:
87
- '6379:6379'
98
volumes:

docker-compose/test.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ version: '3.4'
22

33
services:
44
redis:
5-
container_name: redis-test
6-
image: redislabs/redismod:latest
5+
image: redis/redis-stack-server
76
ports:
8-
- '63790:6379'
7+
- '6379:6379'
98
volumes:
109
- '../data:/data/:rw'

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
"@types/enzyme": "^3.10.11",
1010
"@types/enzyme-adapter-react-16": "^1.0.6",
1111
"@types/lodash": "4.14.178",
12+
"@wojtekmaj/enzyme-adapter-react-17": "^0.8.0",
1213
"enzyme": "^3.11.0",
13-
"enzyme-adapter-react-16": "^1.15.5"
14+
"enzyme-adapter-react-16": "^1.15.5",
15+
"@types/react-dom": "18.0.11",
16+
"sinon": "^15.1.0"
1417
},
1518
"engines": {
1619
"node": ">=14"

pkg/models/redis-search.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package models
55
*/
66
const (
77
SearchInfo = "ft.info"
8+
Search = "ft.search"
89
)
910

1011
/**

pkg/query.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ func query(ctx context.Context, query backend.DataQuery, client redisClient, qm
102102
case models.SearchInfo:
103103
return queryFtInfo(qm, client)
104104

105+
case models.Search:
106+
return queryFtSearch(qm, client)
107+
105108
/**
106109
* Custom commands
107110
*/

pkg/query_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func TestQuery(t *testing.T) {
4040
{queryModel{Command: models.ClusterInfo}},
4141
{queryModel{Command: models.ClusterNodes}},
4242
{queryModel{Command: models.SearchInfo}},
43+
{queryModel{Command: models.Search}},
4344
{queryModel{Command: models.XInfoStream}},
4445
{queryModel{Command: models.TMScan}},
4546
{queryModel{Command: models.GearsPyStats}},

pkg/redis-search.go

Lines changed: 122 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,135 @@
11
package main
22

33
import (
4-
"strconv"
4+
"strconv"
55

6-
"github.com/grafana/grafana-plugin-sdk-go/backend"
7-
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
8-
"github.com/grafana/grafana-plugin-sdk-go/data"
9-
"github.com/redisgrafana/grafana-redis-datasource/pkg/models"
6+
"github.com/grafana/grafana-plugin-sdk-go/backend"
7+
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
8+
"github.com/grafana/grafana-plugin-sdk-go/data"
9+
"github.com/redisgrafana/grafana-redis-datasource/pkg/models"
1010
)
1111

12+
func queryFtSearch(qm queryModel, client redisClient) backend.DataResponse {
13+
response := backend.DataResponse{}
14+
15+
var result interface{}
16+
args := []string{qm.Key}
17+
if qm.SearchQuery == "" {
18+
args = append(args, "*")
19+
} else {
20+
args = append(args, qm.SearchQuery)
21+
}
22+
23+
if qm.ReturnFields != nil && len(qm.ReturnFields) > 0 {
24+
args = append(args, "RETURN")
25+
args = append(args, strconv.Itoa(len(qm.ReturnFields)))
26+
args = append(args, qm.ReturnFields...)
27+
}
28+
29+
if qm.Count != 0 || qm.Offset > 0 {
30+
var count int
31+
if qm.Count == 0 {
32+
count = 10
33+
} else {
34+
count = qm.Count
35+
}
36+
args = append(args, "LIMIT", strconv.Itoa(qm.Offset), strconv.Itoa(count))
37+
}
38+
39+
if qm.SortBy != "" {
40+
args = append(args, "SORTBY", qm.SortBy, qm.SortDirection)
41+
}
42+
43+
err := client.RunCmd(&result, qm.Command, args...)
44+
45+
if err != nil {
46+
return errorHandler(response, err)
47+
}
48+
49+
frame := data.NewFrame("Results")
50+
fieldValuesMap := make(map[string][]string)
51+
52+
fieldValuesMap["keyName"] = make([]string, len(result.([]interface{}))/2)
53+
54+
for i := 1; i < len(result.([]interface{})); i += 2 {
55+
keyName := string((result.([]interface{}))[i].([]uint8))
56+
fieldValuesMap["keyName"][i/2] = keyName
57+
fieldValueArr := (result.([]interface{}))[i+1].([]interface{})
58+
59+
for j := 0; j < len(fieldValueArr); j += 2 {
60+
fieldName := string(fieldValueArr[j].([]uint8))
61+
62+
if _, ok := fieldValuesMap[fieldName]; !ok {
63+
fieldValuesMap[fieldName] = make([]string, len(result.([]interface{}))/2)
64+
}
65+
66+
fieldValue := string(fieldValueArr[j+1].([]uint8))
67+
fieldValuesMap[fieldName][i/2] = fieldValue
68+
}
69+
}
70+
71+
for fieldName, slice := range fieldValuesMap {
72+
frame.Fields = append(frame.Fields, data.NewField(fieldName, nil, slice))
73+
}
74+
75+
response.Frames = append(response.Frames, frame)
76+
77+
return response
78+
}
79+
1280
/**
1381
* FT.INFO {index}
1482
*
1583
* @see https://oss.redislabs.com/redisearch/Commands/#ftinfo
1684
*/
1785
func queryFtInfo(qm queryModel, client redisClient) backend.DataResponse {
18-
response := backend.DataResponse{}
19-
20-
// Execute command
21-
var result map[string]interface{}
22-
err := client.RunCmd(&result, qm.Command, qm.Key)
23-
24-
// Check error
25-
if err != nil {
26-
return errorHandler(response, err)
27-
}
28-
29-
// Create data frame response
30-
frame := data.NewFrame(qm.Key)
31-
32-
// Add fields and values
33-
for key := range result {
34-
// Value
35-
switch value := result[key].(type) {
36-
case int64:
37-
// Add field
38-
field := data.NewField(key, nil, []int64{value})
39-
frame.Fields = append(frame.Fields, field)
40-
case []byte:
41-
// Parse Float
42-
if floatValue, err := strconv.ParseFloat(string(value), 64); err == nil {
43-
field := data.NewField(key, nil, []float64{floatValue})
44-
45-
// Set unit
46-
if models.SearchInfoConfig[key] != "" {
47-
field.Config = &data.FieldConfig{Unit: models.SearchInfoConfig[key]}
48-
}
49-
50-
frame.Fields = append(frame.Fields, field)
51-
} else {
52-
frame.Fields = append(frame.Fields, data.NewField(key, nil, []string{string(value)}))
53-
}
54-
case string:
55-
frame.Fields = append(frame.Fields, data.NewField(key, nil, []string{string(value)}))
56-
case []interface{}:
57-
default:
58-
log.DefaultLogger.Error(models.SearchInfo, "Conversion Error", "Unsupported Value type")
59-
}
60-
}
61-
62-
// Add the frame to the response
63-
response.Frames = append(response.Frames, frame)
64-
65-
// Return Response
66-
return response
86+
response := backend.DataResponse{}
87+
88+
// Execute command
89+
var result map[string]interface{}
90+
err := client.RunCmd(&result, qm.Command, qm.Key)
91+
92+
// Check error
93+
if err != nil {
94+
return errorHandler(response, err)
95+
}
96+
97+
// Create data frame response
98+
frame := data.NewFrame(qm.Key)
99+
100+
// Add fields and values
101+
for key := range result {
102+
// Value
103+
switch value := result[key].(type) {
104+
case int64:
105+
// Add field
106+
field := data.NewField(key, nil, []int64{value})
107+
frame.Fields = append(frame.Fields, field)
108+
case []byte:
109+
// Parse Float
110+
if floatValue, err := strconv.ParseFloat(string(value), 64); err == nil {
111+
field := data.NewField(key, nil, []float64{floatValue})
112+
113+
// Set unit
114+
if models.SearchInfoConfig[key] != "" {
115+
field.Config = &data.FieldConfig{Unit: models.SearchInfoConfig[key]}
116+
}
117+
118+
frame.Fields = append(frame.Fields, field)
119+
} else {
120+
frame.Fields = append(frame.Fields, data.NewField(key, nil, []string{string(value)}))
121+
}
122+
case string:
123+
frame.Fields = append(frame.Fields, data.NewField(key, nil, []string{string(value)}))
124+
case []interface{}:
125+
default:
126+
log.DefaultLogger.Error(models.SearchInfo, "Conversion Error", "Unsupported Value type")
127+
}
128+
}
129+
130+
// Add the frame to the response
131+
response.Frames = append(response.Frames, frame)
132+
133+
// Return Response
134+
return response
67135
}

0 commit comments

Comments
 (0)