mirror of
https://gitee.com/milvus-io/milvus.git
synced 2024-12-02 20:09:57 +08:00
e549148a19
issue: #29988 This pr adds full-support for wildcard pattern matching from end to end. Before this pr, the users can only use prefix match in their expression, for example, "like 'prefix%'". With this pr, more flexible syntax can be combined. To do so, this pr makes these changes: - 1. support regex query both on index and raw data; - 2. translate the pattern matching to regex query, so that it can be handled by the regex query logic; - 3. loose the limit of the expression parsing, which allows general pattern matching syntax; With the support of regex query in segcore backend, we can also add mysql-like `REGEXP` syntax later easily. --------- Signed-off-by: longjiquan <jiquan.long@zilliz.com>
141 lines
2.6 KiB
Go
141 lines
2.6 KiB
Go
package planparserv2
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/milvus-io/milvus/internal/proto/planpb"
|
|
)
|
|
|
|
func Test_hasWildcards(t *testing.T) {
|
|
type args struct {
|
|
pattern string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want bool
|
|
}{
|
|
{
|
|
args: args{
|
|
pattern: "no-wildcards",
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
args: args{
|
|
pattern: "has\\%",
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
args: args{
|
|
pattern: "%",
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
args: args{
|
|
pattern: "has%",
|
|
},
|
|
want: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := hasWildcards(tt.args.pattern); got != tt.want {
|
|
t.Errorf("hasWildcards(%s) = %v, want %v", tt.args.pattern, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_findLocOfLastWildcard(t *testing.T) {
|
|
type args struct {
|
|
pattern string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want int
|
|
}{
|
|
{
|
|
args: args{
|
|
pattern: "no-wildcards",
|
|
},
|
|
want: 11,
|
|
},
|
|
{
|
|
args: args{
|
|
pattern: "only\\%",
|
|
},
|
|
want: 5,
|
|
},
|
|
{
|
|
args: args{
|
|
pattern: "prefix%%",
|
|
},
|
|
want: 5,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := findLastNotOfWildcards(tt.args.pattern); got != tt.want {
|
|
t.Errorf("findLastNotOfWildcards(%s) = %v, want %v", tt.args.pattern, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_translatePatternMatch(t *testing.T) {
|
|
type args struct {
|
|
pattern string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantOp planpb.OpType
|
|
wantOperand string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
args: args{pattern: "prefix%"},
|
|
wantOp: planpb.OpType_PrefixMatch,
|
|
wantOperand: "prefix",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
args: args{pattern: "equal"},
|
|
wantOp: planpb.OpType_Equal,
|
|
wantOperand: "equal",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
args: args{pattern: "%%%%%%"},
|
|
wantOp: planpb.OpType_PrefixMatch,
|
|
wantOperand: "",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
args: args{pattern: "prefix%suffix"},
|
|
wantOp: planpb.OpType_Match,
|
|
wantOperand: "prefix%suffix",
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotOp, gotOperand, err := translatePatternMatch(tt.args.pattern)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("translatePatternMatch(%s) error = %v, wantErr %v", tt.args.pattern, err, tt.wantErr)
|
|
return
|
|
}
|
|
if gotOp != tt.wantOp {
|
|
t.Errorf("translatePatternMatch(%s) gotOp = %v, want %v", tt.args.pattern, gotOp, tt.wantOp)
|
|
}
|
|
if gotOperand != tt.wantOperand {
|
|
t.Errorf("translatePatternMatch(%s) gotOperand = %v, want %v", tt.args.pattern, gotOperand, tt.wantOperand)
|
|
}
|
|
})
|
|
}
|
|
}
|