2021-11-10 15:56:01 +00:00
|
|
|
// Licensed to the LF AI & Data foundation under one
|
|
|
|
// or more contributor license agreements. See the NOTICE file
|
|
|
|
// distributed with this work for additional information
|
|
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
|
|
// to you under the Apache License, Version 2.0 (the
|
|
|
|
// "License"); you may not use this file except in compliance
|
2021-04-30 07:19:52 +00:00
|
|
|
// with the License. You may obtain a copy of the License at
|
|
|
|
//
|
2021-11-10 15:56:01 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2021-04-30 07:19:52 +00:00
|
|
|
//
|
2021-11-10 15:56:01 +00:00
|
|
|
// 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.
|
2021-04-30 07:19:52 +00:00
|
|
|
|
2021-06-22 06:40:07 +00:00
|
|
|
package proxy
|
2021-04-29 08:48:06 +00:00
|
|
|
|
|
|
|
import (
|
2021-04-30 07:19:52 +00:00
|
|
|
"fmt"
|
2021-04-29 08:48:06 +00:00
|
|
|
"testing"
|
|
|
|
|
2021-09-14 08:15:47 +00:00
|
|
|
ant_ast "github.com/antonmedv/expr/ast"
|
2021-04-29 08:48:06 +00:00
|
|
|
ant_parser "github.com/antonmedv/expr/parser"
|
|
|
|
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
|
|
|
"github.com/milvus-io/milvus/internal/proto/planpb"
|
|
|
|
"github.com/milvus-io/milvus/internal/proto/schemapb"
|
|
|
|
"github.com/milvus-io/milvus/internal/util/typeutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
func newTestSchema() *schemapb.CollectionSchema {
|
2021-04-30 07:19:52 +00:00
|
|
|
fields := []*schemapb.FieldSchema{
|
|
|
|
{FieldID: 0, Name: "FieldID", IsPrimaryKey: false, Description: "field no.1", DataType: schemapb.DataType_Int64},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, value := range schemapb.DataType_value {
|
|
|
|
dataType := schemapb.DataType(value)
|
2022-04-29 05:35:49 +00:00
|
|
|
if !typeutil.IsIntegerType(dataType) && !typeutil.IsFloatingType(dataType) && !typeutil.IsVectorType(dataType) && !typeutil.IsStringType(dataType) {
|
2021-04-30 07:19:52 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
newField := &schemapb.FieldSchema{
|
|
|
|
FieldID: int64(100 + value), Name: name + "Field", IsPrimaryKey: false, Description: "", DataType: dataType,
|
|
|
|
}
|
|
|
|
fields = append(fields, newField)
|
|
|
|
}
|
|
|
|
|
2021-04-29 08:48:06 +00:00
|
|
|
return &schemapb.CollectionSchema{
|
|
|
|
Name: "test",
|
|
|
|
Description: "schema for test used",
|
|
|
|
AutoID: true,
|
2021-04-30 07:19:52 +00:00
|
|
|
Fields: fields,
|
2021-04-29 08:48:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-25 05:43:56 +00:00
|
|
|
func TestParseExpr_Naive(t *testing.T) {
|
2021-04-29 08:48:06 +00:00
|
|
|
schemaPb := newTestSchema()
|
|
|
|
schema, err := typeutil.CreateSchemaHelper(schemaPb)
|
|
|
|
assert.Nil(t, err)
|
2021-09-25 05:43:56 +00:00
|
|
|
|
|
|
|
t.Run("test UnaryNode", func(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
"Int64Field > +1",
|
|
|
|
"Int64Field > -1",
|
|
|
|
"FloatField > +1.0",
|
|
|
|
"FloatField > -1.0",
|
2022-04-29 05:35:49 +00:00
|
|
|
`VarCharField > "str"`,
|
|
|
|
`VarCharField startsWith "str"`,
|
|
|
|
`VarCharField endsWith "str"`,
|
2021-09-25 05:43:56 +00:00
|
|
|
}
|
|
|
|
for _, exprStr := range exprStrs {
|
|
|
|
exprProto, err := parseExpr(schema, exprStr)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
str := proto.MarshalTextString(exprProto)
|
|
|
|
println(str)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("test UnaryNode invalid", func(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
"Int64Field > +aa",
|
|
|
|
"FloatField > -aa",
|
2022-04-29 05:35:49 +00:00
|
|
|
`VarCharField > -aa`,
|
2021-09-25 05:43:56 +00:00
|
|
|
}
|
|
|
|
for _, exprStr := range exprStrs {
|
|
|
|
exprProto, err := parseExpr(schema, exprStr)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, exprProto)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("test BinaryNode", func(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
// "+"
|
|
|
|
"FloatField > 1 + 2",
|
|
|
|
"FloatField > 1 + 2.0",
|
|
|
|
"FloatField > 1.0 + 2",
|
|
|
|
"FloatField > 1.0 + 2.0",
|
|
|
|
// "-"
|
|
|
|
"FloatField > 1 - 2",
|
|
|
|
"FloatField > 1 - 2.0",
|
|
|
|
"FloatField > 1.0 - 2",
|
|
|
|
"FloatField > 1.0 - 2.0",
|
|
|
|
// "*"
|
|
|
|
"FloatField > 1 * 2",
|
|
|
|
"FloatField > 1 * 2.0",
|
|
|
|
"FloatField > 1.0 * 2",
|
|
|
|
"FloatField > 1.0 * 2.0",
|
|
|
|
// "/"
|
|
|
|
"FloatField > 1 / 2",
|
|
|
|
"FloatField > 1 / 2.0",
|
|
|
|
"FloatField > 1.0 / 2",
|
|
|
|
"FloatField > 1.0 / 2.0",
|
|
|
|
// "%"
|
|
|
|
"FloatField > 1 % 2",
|
|
|
|
// "**"
|
|
|
|
"FloatField > 1 ** 2",
|
|
|
|
"FloatField > 1 ** 2.0",
|
|
|
|
"FloatField > 1.0 ** 2",
|
|
|
|
"FloatField > 1.0 ** 2.0",
|
|
|
|
}
|
|
|
|
for _, exprStr := range exprStrs {
|
|
|
|
exprProto, err := parseExpr(schema, exprStr)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
str := proto.MarshalTextString(exprProto)
|
|
|
|
println(str)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("test BinaryNode invalid", func(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
// "+"
|
|
|
|
"FloatField > 1 + aa",
|
|
|
|
"FloatField > aa + 2.0",
|
|
|
|
// "-"
|
|
|
|
"FloatField > 1 - aa",
|
|
|
|
"FloatField > aa - 2.0",
|
|
|
|
// "*"
|
|
|
|
"FloatField > 1 * aa",
|
|
|
|
"FloatField > aa * 2.0",
|
|
|
|
// "/"
|
|
|
|
"FloatField > 1 / 0",
|
|
|
|
"FloatField > 1 / 0.0",
|
|
|
|
"FloatField > 1.0 / 0",
|
|
|
|
"FloatField > 1.0 / 0.0",
|
|
|
|
"FloatField > 1 / aa",
|
|
|
|
"FloatField > aa / 2.0",
|
|
|
|
// "%"
|
|
|
|
"FloatField > 1 % aa",
|
2021-09-26 01:42:02 +00:00
|
|
|
"FloatField > 1 % 0",
|
|
|
|
"FloatField > 1 % 0.0",
|
2021-09-25 05:43:56 +00:00
|
|
|
// "**"
|
|
|
|
"FloatField > 1 ** aa",
|
|
|
|
"FloatField > aa ** 2.0",
|
|
|
|
}
|
|
|
|
for _, exprStr := range exprStrs {
|
|
|
|
exprProto, err := parseExpr(schema, exprStr)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, exprProto)
|
|
|
|
}
|
|
|
|
})
|
2022-04-24 08:43:45 +00:00
|
|
|
|
|
|
|
t.Run("test BinaryArithOpNode", func(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
// "+"
|
|
|
|
"FloatField + 1.2 == 3",
|
|
|
|
"Int64Field + 3 == 5",
|
|
|
|
"1.2 + FloatField != 3",
|
|
|
|
"3 + Int64Field != 5",
|
|
|
|
// "-"
|
|
|
|
"FloatField - 1.2 == 3",
|
|
|
|
"Int64Field - 3 != 5",
|
|
|
|
// "*"
|
|
|
|
"FloatField * 1.2 == 3",
|
|
|
|
"Int64Field * 3 == 5",
|
|
|
|
"1.2 * FloatField != 3",
|
|
|
|
"3 * Int64Field != 5",
|
|
|
|
// "/"
|
|
|
|
"FloatField / 1.2 == 3",
|
|
|
|
"Int64Field / 3 != 5",
|
|
|
|
// "%"
|
|
|
|
"Int64Field % 7 == 5",
|
|
|
|
}
|
|
|
|
for _, exprStr := range exprStrs {
|
|
|
|
exprProto, err := parseExpr(schema, exprStr)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
str := proto.MarshalTextString(exprProto)
|
|
|
|
println(str)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("test BinaryArithOpNode invalid", func(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
// "+"
|
|
|
|
"FloatField + FloatField == 20",
|
|
|
|
"Int64Field + Int64Field != 10",
|
|
|
|
// "-"
|
|
|
|
"FloatField - FloatField == 20.0",
|
|
|
|
"Int64Field - Int64Field != 10",
|
|
|
|
"10 - FloatField == 20",
|
|
|
|
"20 - Int64Field != 10",
|
|
|
|
// "*"
|
|
|
|
"FloatField * FloatField == 20",
|
|
|
|
"Int64Field * Int64Field != 10",
|
|
|
|
// "/"
|
|
|
|
"FloatField / FloatField == 20",
|
|
|
|
"Int64Field / Int64Field != 10",
|
|
|
|
"FloatField / 0 == 20",
|
|
|
|
"Int64Field / 0 != 10",
|
|
|
|
// "%"
|
|
|
|
"Int64Field % Int64Field != 10",
|
|
|
|
"FloatField % 0 == 20",
|
|
|
|
"Int64Field % 0 != 10",
|
|
|
|
"FloatField % 2.3 == 20",
|
|
|
|
}
|
|
|
|
for _, exprStr := range exprStrs {
|
|
|
|
exprProto, err := parseExpr(schema, exprStr)
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Nil(t, exprProto)
|
|
|
|
}
|
|
|
|
})
|
2021-04-29 08:48:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestParsePlanNode_Naive(t *testing.T) {
|
2021-04-30 07:19:52 +00:00
|
|
|
exprStrs := []string{
|
|
|
|
"not (Int64Field > 3)",
|
|
|
|
"not (3 > Int64Field)",
|
|
|
|
"Int64Field in [1, 2, 3]",
|
|
|
|
"Int64Field < 3 and (Int64Field > 2 || Int64Field == 1)",
|
|
|
|
"DoubleField in [1.0, 2, 3]",
|
|
|
|
"DoubleField in [1.0, 2, 3] && Int64Field < 3 or Int64Field > 2",
|
2022-04-29 05:35:49 +00:00
|
|
|
`not (VarCharField > "str")`,
|
|
|
|
`not ("str" > VarCharField)`,
|
|
|
|
`not (VarCharField startsWith "str")`,
|
|
|
|
`not (VarCharField endsWith "str")`,
|
|
|
|
`VarCharField in ["term0", "term1", "term2"]`,
|
|
|
|
`VarCharField < "str3" and (VarCharField > "str2" || VarCharField == "str1")`,
|
|
|
|
`VarCharField < "str3" and (VarCharField startsWith "str2" || VarCharField endsWith "str1")`,
|
|
|
|
`DoubleField in [1.0, 2, 3] && VarCharField < "str3" or Int64Field > 2`,
|
2021-04-30 07:19:52 +00:00
|
|
|
}
|
|
|
|
|
2021-04-29 08:48:06 +00:00
|
|
|
schema := newTestSchema()
|
|
|
|
queryInfo := &planpb.QueryInfo{
|
|
|
|
Topk: 10,
|
|
|
|
MetricType: "L2",
|
|
|
|
SearchParams: "{\"nprobe\": 10}",
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: use pointer to string to represent nullable string
|
|
|
|
// TODO: change it to better solution
|
2021-04-30 07:19:52 +00:00
|
|
|
for offset, exprStr := range exprStrs {
|
|
|
|
fmt.Printf("case %d: %s\n", offset, exprStr)
|
2021-10-13 12:30:32 +00:00
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "FloatVectorField", queryInfo)
|
2021-04-30 07:19:52 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
2021-04-29 08:48:06 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestExternalParser(t *testing.T) {
|
2022-04-29 05:35:49 +00:00
|
|
|
ast, err := ant_parser.Parse(`!(1 < a < 2 or b in [1, 2, 3]) or (c < 3 and b > 5) and (d > "str1" or d < "str2")`)
|
2021-04-30 07:19:52 +00:00
|
|
|
// NOTE: probe ast here via IDE
|
2021-04-29 08:48:06 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
|
|
|
|
println(ast.Node.Location().Column)
|
|
|
|
}
|
2021-05-07 07:20:47 +00:00
|
|
|
|
|
|
|
func TestExprPlan_Str(t *testing.T) {
|
|
|
|
fields := []*schemapb.FieldSchema{
|
|
|
|
{FieldID: 100, Name: "fakevec", DataType: schemapb.DataType_FloatVector},
|
|
|
|
{FieldID: 101, Name: "age", DataType: schemapb.DataType_Int64},
|
|
|
|
}
|
|
|
|
|
|
|
|
schema := &schemapb.CollectionSchema{
|
|
|
|
Name: "default-collection",
|
|
|
|
Description: "",
|
|
|
|
AutoID: true,
|
|
|
|
Fields: fields,
|
|
|
|
}
|
|
|
|
|
|
|
|
queryInfo := &planpb.QueryInfo{
|
|
|
|
Topk: 10,
|
|
|
|
MetricType: "L2",
|
|
|
|
SearchParams: "{\"nprobe\": 10}",
|
|
|
|
}
|
|
|
|
|
|
|
|
// without filter
|
2021-10-13 12:30:32 +00:00
|
|
|
planProto, err := createQueryPlan(schema, "", "fakevec", queryInfo)
|
2021-05-07 07:20:47 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
|
|
|
|
exprStrs := []string{
|
|
|
|
"age >= 420000 && age < 420010", // range
|
|
|
|
"age == 420000 || age == 420001 || age == 420002 || age == 420003 || age == 420004", // term
|
2021-06-11 09:08:25 +00:00
|
|
|
"age not in [1, 2, 3]",
|
2021-05-07 07:20:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for offset, exprStr := range exprStrs {
|
|
|
|
fmt.Printf("case %d: %s\n", offset, exprStr)
|
2021-10-13 12:30:32 +00:00
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "fakevec", queryInfo)
|
2021-05-07 07:20:47 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
|
|
|
}
|
2021-07-13 06:37:03 +00:00
|
|
|
|
|
|
|
func TestExprMultiRange_Str(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
"3 < FloatN < 4.0",
|
|
|
|
"3 < age1 < 5 < age2 < 7 < FloatN < 9.0 < FloatN2",
|
|
|
|
"1 + 1 < age1 < 2 * 2",
|
|
|
|
"1 - 1 < age1 < 3 / 2",
|
|
|
|
"1.0 - 1 < FloatN < 3 / 2",
|
|
|
|
"2 ** 10 > FloatN >= 7 % 4",
|
|
|
|
"0.1 ** 2 < FloatN < 2 ** 0.1",
|
|
|
|
"0.1 ** 1.1 < FloatN < 3.1 / 4",
|
|
|
|
"4.1 / 3 < FloatN < 0.0 / 5.0",
|
2021-09-14 08:15:47 +00:00
|
|
|
"BoolN1 == True",
|
|
|
|
"True == BoolN1",
|
|
|
|
"BoolN1 == False",
|
2021-07-13 06:37:03 +00:00
|
|
|
}
|
2021-11-22 08:57:15 +00:00
|
|
|
invalidExprs := []string{
|
2021-11-23 02:57:16 +00:00
|
|
|
"BoolN1 == 1",
|
|
|
|
"BoolN1 == 0",
|
|
|
|
"BoolN1 > 0",
|
2021-11-22 08:57:15 +00:00
|
|
|
}
|
2021-07-13 06:37:03 +00:00
|
|
|
|
|
|
|
fields := []*schemapb.FieldSchema{
|
|
|
|
{FieldID: 100, Name: "fakevec", DataType: schemapb.DataType_FloatVector},
|
|
|
|
{FieldID: 101, Name: "age1", DataType: schemapb.DataType_Int64},
|
|
|
|
{FieldID: 102, Name: "age2", DataType: schemapb.DataType_Int64},
|
|
|
|
{FieldID: 103, Name: "FloatN", DataType: schemapb.DataType_Float},
|
|
|
|
{FieldID: 104, Name: "FloatN2", DataType: schemapb.DataType_Float},
|
2021-09-14 08:15:47 +00:00
|
|
|
{FieldID: 105, Name: "BoolN1", DataType: schemapb.DataType_Bool},
|
2021-07-13 06:37:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
schema := &schemapb.CollectionSchema{
|
|
|
|
Name: "default-collection",
|
|
|
|
Description: "",
|
|
|
|
AutoID: true,
|
|
|
|
Fields: fields,
|
|
|
|
}
|
|
|
|
|
|
|
|
queryInfo := &planpb.QueryInfo{
|
|
|
|
Topk: 10,
|
|
|
|
MetricType: "L2",
|
|
|
|
SearchParams: "{\"nprobe\": 10}",
|
|
|
|
}
|
|
|
|
|
|
|
|
for offset, exprStr := range exprStrs {
|
|
|
|
fmt.Printf("case %d: %s\n", offset, exprStr)
|
2021-10-13 12:30:32 +00:00
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "fakevec", queryInfo)
|
2021-07-13 06:37:03 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
2021-11-22 08:57:15 +00:00
|
|
|
for offset, exprStr := range invalidExprs {
|
|
|
|
fmt.Printf("invalid case %d: %s\n", offset, exprStr)
|
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "fakevec", queryInfo)
|
|
|
|
assert.Error(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
2021-07-13 06:37:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestExprFieldCompare_Str(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
"age1 < age2",
|
|
|
|
"3 < age1 <= age2 < 4",
|
|
|
|
}
|
|
|
|
|
|
|
|
fields := []*schemapb.FieldSchema{
|
|
|
|
{FieldID: 100, Name: "fakevec", DataType: schemapb.DataType_FloatVector},
|
|
|
|
{FieldID: 101, Name: "age1", DataType: schemapb.DataType_Int64},
|
|
|
|
{FieldID: 102, Name: "age2", DataType: schemapb.DataType_Int64},
|
|
|
|
{FieldID: 103, Name: "FloatN", DataType: schemapb.DataType_Float},
|
|
|
|
}
|
|
|
|
|
|
|
|
schema := &schemapb.CollectionSchema{
|
|
|
|
Name: "default-collection",
|
|
|
|
Description: "",
|
|
|
|
AutoID: true,
|
|
|
|
Fields: fields,
|
|
|
|
}
|
|
|
|
|
|
|
|
queryInfo := &planpb.QueryInfo{
|
|
|
|
Topk: 10,
|
|
|
|
MetricType: "L2",
|
|
|
|
SearchParams: "{\"nprobe\": 10}",
|
|
|
|
}
|
|
|
|
|
|
|
|
for offset, exprStr := range exprStrs {
|
|
|
|
fmt.Printf("case %d: %s\n", offset, exprStr)
|
2021-10-13 12:30:32 +00:00
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "fakevec", queryInfo)
|
2021-07-13 06:37:03 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
|
|
|
}
|
2021-09-14 08:15:47 +00:00
|
|
|
|
2022-04-24 08:43:45 +00:00
|
|
|
func TestExprBinaryArithOp_Str(t *testing.T) {
|
|
|
|
exprStrs := []string{
|
|
|
|
// Basic arithmetic
|
|
|
|
"(age1 + 5) == 2",
|
|
|
|
// Float data type
|
|
|
|
"(FloatN - 5.2) == 0",
|
|
|
|
// Other operators
|
|
|
|
"(age1 - 5) == 1",
|
|
|
|
"(age1 * 5) == 6",
|
|
|
|
"(age1 / 5) == 1",
|
|
|
|
"(age1 % 5) == 0",
|
|
|
|
// Allow for commutative property for + and *
|
|
|
|
"(6 + age1) != 2",
|
|
|
|
"(age1 * 4) != 9",
|
|
|
|
"(5 * FloatN) != 0",
|
|
|
|
"(9 * FloatN) != 0",
|
|
|
|
}
|
|
|
|
|
|
|
|
unsupportedExprStrs := []string{
|
|
|
|
// Comparison operators except for "==" and "!=" are unsupported
|
|
|
|
"(age1 + 2) > 4",
|
|
|
|
"(age1 + 2) >= 4",
|
|
|
|
"(age1 + 2) < 4",
|
|
|
|
"(age1 + 2) <= 4",
|
|
|
|
// Functional nodes at the right of the comparison are not allowed
|
|
|
|
"0 == (age1 + 3)",
|
|
|
|
// Field as the right operand for -, /, and % operators are not supported
|
|
|
|
"(10 - age1) == 0",
|
|
|
|
"(20 / age1) == 0",
|
|
|
|
"(30 % age1) == 0",
|
|
|
|
// Modulo is not supported in the parser but the engine can handle it since fmod is used
|
|
|
|
"(FloatN % 2.1) == 0",
|
|
|
|
// Different data types are not supported
|
|
|
|
"(age1 + 20.16) == 35.16",
|
|
|
|
// Left operand of the function must be an identifier
|
|
|
|
"(10.5 / floatN) == 5.75",
|
|
|
|
}
|
|
|
|
|
|
|
|
fields := []*schemapb.FieldSchema{
|
|
|
|
{FieldID: 100, Name: "fakevec", DataType: schemapb.DataType_FloatVector},
|
|
|
|
{FieldID: 101, Name: "age1", DataType: schemapb.DataType_Int64},
|
|
|
|
{FieldID: 102, Name: "FloatN", DataType: schemapb.DataType_Float},
|
|
|
|
}
|
|
|
|
|
|
|
|
schema := &schemapb.CollectionSchema{
|
|
|
|
Name: "default-collection",
|
|
|
|
Description: "",
|
|
|
|
AutoID: true,
|
|
|
|
Fields: fields,
|
|
|
|
}
|
|
|
|
|
|
|
|
queryInfo := &planpb.QueryInfo{
|
|
|
|
Topk: 10,
|
|
|
|
MetricType: "L2",
|
|
|
|
SearchParams: "{\"nprobe\": 10}",
|
|
|
|
}
|
|
|
|
|
|
|
|
for offset, exprStr := range exprStrs {
|
|
|
|
fmt.Printf("case %d: %s\n", offset, exprStr)
|
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "fakevec", queryInfo)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
|
|
|
for offset, exprStr := range unsupportedExprStrs {
|
|
|
|
fmt.Printf("case %d: %s\n", offset, exprStr)
|
|
|
|
planProto, err := createQueryPlan(schema, exprStr, "fakevec", queryInfo)
|
|
|
|
assert.Error(t, err)
|
|
|
|
dbgStr := proto.MarshalTextString(planProto)
|
|
|
|
println(dbgStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 01:46:04 +00:00
|
|
|
func TestPlanParseAPIs(t *testing.T) {
|
|
|
|
t.Run("get compare op type", func(t *testing.T) {
|
|
|
|
var op planpb.OpType
|
|
|
|
var reverse bool
|
|
|
|
|
|
|
|
reverse = false
|
|
|
|
op = getCompareOpType(">", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_GreaterThan, op)
|
|
|
|
op = getCompareOpType(">=", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_GreaterEqual, op)
|
|
|
|
op = getCompareOpType("<", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_LessThan, op)
|
|
|
|
op = getCompareOpType("<=", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_LessEqual, op)
|
|
|
|
op = getCompareOpType("==", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_Equal, op)
|
|
|
|
op = getCompareOpType("!=", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_NotEqual, op)
|
|
|
|
op = getCompareOpType("*", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_Invalid, op)
|
2022-04-29 05:35:49 +00:00
|
|
|
op = getCompareOpType("startsWith", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_PrefixMatch, op)
|
|
|
|
op = getCompareOpType("endsWith", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_PostfixMatch, op)
|
2021-09-28 01:46:04 +00:00
|
|
|
|
|
|
|
reverse = true
|
|
|
|
op = getCompareOpType(">", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_LessThan, op)
|
|
|
|
op = getCompareOpType(">=", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_LessEqual, op)
|
|
|
|
op = getCompareOpType("<", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_GreaterThan, op)
|
|
|
|
op = getCompareOpType("<=", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_GreaterEqual, op)
|
|
|
|
op = getCompareOpType("==", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_Equal, op)
|
|
|
|
op = getCompareOpType("!=", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_NotEqual, op)
|
|
|
|
op = getCompareOpType("*", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_Invalid, op)
|
2022-04-29 05:35:49 +00:00
|
|
|
op = getCompareOpType("startsWith", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_PrefixMatch, op)
|
|
|
|
op = getCompareOpType("endsWith", reverse)
|
|
|
|
assert.Equal(t, planpb.OpType_PostfixMatch, op)
|
2021-09-28 01:46:04 +00:00
|
|
|
})
|
2021-09-14 08:15:47 +00:00
|
|
|
|
2021-09-28 01:46:04 +00:00
|
|
|
t.Run("parse bool node", func(t *testing.T) {
|
|
|
|
var nodeRaw1, nodeRaw2, nodeRaw3, nodeRaw4 ant_ast.Node
|
|
|
|
nodeRaw1 = &ant_ast.IdentifierNode{
|
|
|
|
Value: "True",
|
|
|
|
}
|
|
|
|
boolNode1 := parseBoolNode(&nodeRaw1)
|
|
|
|
assert.Equal(t, boolNode1.Value, true)
|
2021-09-14 08:15:47 +00:00
|
|
|
|
2021-09-28 01:46:04 +00:00
|
|
|
nodeRaw2 = &ant_ast.IdentifierNode{
|
|
|
|
Value: "False",
|
|
|
|
}
|
|
|
|
boolNode2 := parseBoolNode(&nodeRaw2)
|
|
|
|
assert.Equal(t, boolNode2.Value, false)
|
2021-09-14 08:15:47 +00:00
|
|
|
|
2021-09-28 01:46:04 +00:00
|
|
|
nodeRaw3 = &ant_ast.IdentifierNode{
|
|
|
|
Value: "abcd",
|
|
|
|
}
|
|
|
|
assert.Nil(t, parseBoolNode(&nodeRaw3))
|
|
|
|
|
|
|
|
nodeRaw4 = &ant_ast.BoolNode{
|
|
|
|
Value: true,
|
|
|
|
}
|
|
|
|
assert.Nil(t, parseBoolNode(&nodeRaw4))
|
|
|
|
})
|
2021-09-14 08:15:47 +00:00
|
|
|
}
|