redis: fix querying numeric values (#1595)
Signed-off-by: Dmitry Shmulevich <dmitsh@users.noreply.github.com> Co-authored-by: Dmitry Shmulevich <dmitsh@users.noreply.github.com>
This commit is contained in:
parent
88fb6b32ff
commit
6e504186a5
|
@ -26,6 +26,8 @@ import (
|
|||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
var ErrMultipleSortBy error = errors.New("multiple SORTBY steps are not allowed. Sort multiple fields in a single step")
|
||||
|
||||
type Query struct {
|
||||
schemaName string
|
||||
aliases map[string]string
|
||||
|
@ -50,31 +52,56 @@ func (q *Query) getAlias(jsonPath string) (string, error) {
|
|||
}
|
||||
|
||||
func (q *Query) VisitEQ(f *query.EQ) (string, error) {
|
||||
// @<key>:(<val>)
|
||||
// string: @<key>:(<val>)
|
||||
// numeric: @<key>:[<val> <val>]
|
||||
alias, err := q.getAlias(f.Key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("@%s:(%s)", alias, f.Val), nil
|
||||
switch v := f.Val.(type) {
|
||||
case string:
|
||||
return fmt.Sprintf("@%s:(%s)", alias, v), nil
|
||||
default:
|
||||
return fmt.Sprintf("@%s:[%v %v]", alias, v, v), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) VisitIN(f *query.IN) (string, error) {
|
||||
// @<key>:(<val1>|<val2>...)
|
||||
if len(f.Vals) == 0 {
|
||||
return "", fmt.Errorf("empty IN operator for key %q", f.Key)
|
||||
// string: @<key>:(<val1>|<val2>...)
|
||||
// numeric: replace with OR
|
||||
n := len(f.Vals)
|
||||
if n < 2 {
|
||||
return "", fmt.Errorf("too few values in IN operator for key %q", f.Key)
|
||||
}
|
||||
alias, err := q.getAlias(f.Key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vals := make([]string, len(f.Vals))
|
||||
for i := range f.Vals {
|
||||
vals[i] = f.Vals[i].(string)
|
||||
}
|
||||
str := fmt.Sprintf("@%s:(%s)", alias, strings.Join(vals, "|"))
|
||||
|
||||
return str, nil
|
||||
switch f.Vals[0].(type) {
|
||||
case string:
|
||||
alias, err := q.getAlias(f.Key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vals := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
vals[i] = f.Vals[i].(string)
|
||||
}
|
||||
str := fmt.Sprintf("@%s:(%s)", alias, strings.Join(vals, "|"))
|
||||
|
||||
return str, nil
|
||||
|
||||
default:
|
||||
or := &query.OR{
|
||||
Filters: make([]query.Filter, n),
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
or.Filters[i] = &query.EQ{
|
||||
Key: f.Key,
|
||||
Val: f.Vals[i],
|
||||
}
|
||||
}
|
||||
|
||||
return q.VisitOR(or)
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) visitFilters(op string, filters []query.Filter) (string, error) {
|
||||
|
@ -132,7 +159,7 @@ func (q *Query) Finalize(filters string, qq *query.Query) error {
|
|||
// sorting
|
||||
if len(qq.Sort) > 0 {
|
||||
if len(qq.Sort) != 1 {
|
||||
return errors.New("multiple SORTBY steps are not allowed. Sort multiple fields in a single step")
|
||||
return ErrMultipleSortBy
|
||||
}
|
||||
alias, err := q.getAlias(qq.Sort[0].Key)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright 2022 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package redis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/dapr/components-contrib/state/query"
|
||||
)
|
||||
|
||||
func TestMongoQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
query []interface{}
|
||||
err error
|
||||
}{
|
||||
{
|
||||
input: "../../tests/state/query/q1.json",
|
||||
query: []interface{}{"*", "LIMIT", "0", "2"},
|
||||
},
|
||||
{
|
||||
input: "../../tests/state/query/q2.json",
|
||||
query: []interface{}{"@state:(CA)", "LIMIT", "0", "2"},
|
||||
},
|
||||
{
|
||||
input: "../../tests/state/query/q3.json",
|
||||
err: ErrMultipleSortBy,
|
||||
},
|
||||
{
|
||||
input: "../../tests/state/query/q6.json",
|
||||
query: []interface{}{"((@id:[123 123])|((@org:(B)) (((@id:[567 567])|(@id:[890 890])))))", "SORTBY", "id", "LIMIT", "0", "2"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
data, err := ioutil.ReadFile(test.input)
|
||||
assert.NoError(t, err)
|
||||
var qq query.Query
|
||||
err = json.Unmarshal(data, &qq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
q := &Query{
|
||||
aliases: map[string]string{"person.org": "org", "person.id": "id", "state": "state"},
|
||||
}
|
||||
qbuilder := query.NewQueryBuilder(q)
|
||||
if err = qbuilder.BuildQuery(&qq); err != nil {
|
||||
assert.EqualError(t, err, test.err.Error())
|
||||
} else {
|
||||
assert.Equal(t, test.query, q.query)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,11 +24,7 @@
|
|||
},
|
||||
"sort": [
|
||||
{
|
||||
"key": "state",
|
||||
"order": "DESC"
|
||||
},
|
||||
{
|
||||
"key": "person.name"
|
||||
"key": "person.id"
|
||||
}
|
||||
],
|
||||
"page": {
|
||||
|
|
Loading…
Reference in New Issue