milvus/internal/parser/planparserv2/pattern_match.go
Jiquan Long e549148a19
enhance: full-support for wildcard pattern matching (#30288)
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>
2024-02-01 12:37:04 +08:00

70 lines
1.4 KiB
Go

package planparserv2
import (
"github.com/milvus-io/milvus/internal/proto/planpb"
)
var wildcards = map[byte]struct{}{
// '_': {}, // TODO
'%': {},
}
var escapeCharacter byte = '\\'
// hasWildcards returns true if pattern contains any wildcard.
func hasWildcards(pattern string) bool {
l := len(pattern)
i := l - 1
for ; i >= 0; i-- {
_, ok := wildcards[pattern[i]]
if ok {
if i > 0 && pattern[i-1] == escapeCharacter {
i--
continue
}
return true
}
}
return false
}
// findLastNotOfWildcards find the last location not of last wildcard.
func findLastNotOfWildcards(pattern string) int {
loc := len(pattern) - 1
for ; loc >= 0; loc-- {
_, ok := wildcards[pattern[loc]]
if !ok {
break
}
if ok {
if loc > 0 && pattern[loc-1] == escapeCharacter {
break
}
}
}
return loc
}
// translatePatternMatch translates pattern to related op type and operand.
func translatePatternMatch(pattern string) (op planpb.OpType, operand string, err error) {
l := len(pattern)
loc := findLastNotOfWildcards(pattern)
if loc < 0 {
// always match.
return planpb.OpType_PrefixMatch, "", nil
}
exist := hasWildcards(pattern[:loc+1])
if loc >= l-1 && !exist {
// equal match.
return planpb.OpType_Equal, pattern, nil
}
if !exist {
// prefix match.
return planpb.OpType_PrefixMatch, pattern[:loc+1], nil
}
return planpb.OpType_Match, pattern, nil
}