This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git


The following commit(s) were added to refs/heads/main by this push:
     new e504468  Introduce full text searching to stream and measure (#166)
e504468 is described below

commit e504468406051afb873a9878c6d6ca8ad14d6ca1
Author: Gao Hongtao <[email protected]>
AuthorDate: Tue Aug 30 10:03:49 2022 +0800

    Introduce full text searching to stream and measure (#166)
    
    Signed-off-by: Gao Hongtao <[email protected]>
---
 api/proto/banyandb/database/v1/schema.pb.go        | 311 +++++++++++++--------
 .../banyandb/database/v1/schema.pb.validate.go     |   2 +
 api/proto/banyandb/database/v1/schema.proto        |  13 +
 api/proto/banyandb/model/v1/query.pb.go            |  96 ++++---
 api/proto/banyandb/model/v1/query.proto            |   4 +
 .../openapi/banyandb/database/v1/rpc.swagger.json  |  24 +-
 .../openapi/banyandb/measure/v1/rpc.swagger.json   |   5 +-
 .../openapi/banyandb/stream/v1/rpc.swagger.json    |   5 +-
 banyand/tsdb/index/writer.go                       |   4 +-
 banyand/tsdb/series_seek.go                        |   7 +-
 banyand/tsdb/series_seek_filter.go                 |  15 +-
 docs/api-reference.md                              |  20 ++
 pkg/index/index.go                                 |   3 +
 pkg/index/inverted/inverted.go                     |  59 +++-
 pkg/index/inverted/inverted_test.go                | 111 ++++++++
 pkg/index/lsm/search.go                            |   6 +
 pkg/index/testcases/duration.go                    |   2 +-
 pkg/index/tree.go                                  |  37 +++
 pkg/pb/v1/query.go                                 |   1 +
 pkg/query/logical/common_test.go                   |   4 +-
 pkg/query/logical/expr.go                          |   9 +
 pkg/query/logical/measure_analyzer_test.go         |  42 +++
 pkg/query/logical/measure_plan_execution_test.go   |  88 +++++-
 pkg/query/logical/stream_analyzer_test.go          |   4 +-
 pkg/query/logical/stream_plan_execution_test.go    |   7 +
 .../logical/testdata/measure_search_data.json      | 102 +++++++
 pkg/query/logical/testdata/multiple_shards.json    |  17 +-
 .../service_instance_traffic.json                  |   3 +-
 .../testdata/index_rules/searchable_name.json      |  14 +
 .../stream/testdata/index_rules/db.instance.json   |   1 +
 30 files changed, 825 insertions(+), 191 deletions(-)

diff --git a/api/proto/banyandb/database/v1/schema.pb.go 
b/api/proto/banyandb/database/v1/schema.pb.go
index 107b9ea..b5ef0c9 100644
--- a/api/proto/banyandb/database/v1/schema.pb.go
+++ b/api/proto/banyandb/database/v1/schema.pb.go
@@ -344,6 +344,63 @@ func (IndexRule_Location) EnumDescriptor() ([]byte, []int) 
{
        return file_banyandb_database_v1_schema_proto_rawDescGZIP(), []int{7, 1}
 }
 
+type IndexRule_Analyzer int32
+
+const (
+       IndexRule_ANALYZER_UNSPECIFIED IndexRule_Analyzer = 0
+       // Keyword analyzer is a “noop” analyzer which returns the entire input 
string as a single token.
+       IndexRule_ANALYZER_KEYWORD IndexRule_Analyzer = 1
+       // Standard analyzer provides grammar based tokenization
+       IndexRule_ANALYZER_STANDARD IndexRule_Analyzer = 2
+       // Simple analyzer breaks text into tokens at any non-letter character,
+       // such as numbers, spaces, hyphens and apostrophes, discards 
non-letter characters,
+       // and changes uppercase to lowercase.
+       IndexRule_ANALYZER_SIMPLE IndexRule_Analyzer = 3
+)
+
+// Enum value maps for IndexRule_Analyzer.
+var (
+       IndexRule_Analyzer_name = map[int32]string{
+               0: "ANALYZER_UNSPECIFIED",
+               1: "ANALYZER_KEYWORD",
+               2: "ANALYZER_STANDARD",
+               3: "ANALYZER_SIMPLE",
+       }
+       IndexRule_Analyzer_value = map[string]int32{
+               "ANALYZER_UNSPECIFIED": 0,
+               "ANALYZER_KEYWORD":     1,
+               "ANALYZER_STANDARD":    2,
+               "ANALYZER_SIMPLE":      3,
+       }
+)
+
+func (x IndexRule_Analyzer) Enum() *IndexRule_Analyzer {
+       p := new(IndexRule_Analyzer)
+       *p = x
+       return p
+}
+
+func (x IndexRule_Analyzer) String() string {
+       return protoimpl.X.EnumStringOf(x.Descriptor(), 
protoreflect.EnumNumber(x))
+}
+
+func (IndexRule_Analyzer) Descriptor() protoreflect.EnumDescriptor {
+       return file_banyandb_database_v1_schema_proto_enumTypes[6].Descriptor()
+}
+
+func (IndexRule_Analyzer) Type() protoreflect.EnumType {
+       return &file_banyandb_database_v1_schema_proto_enumTypes[6]
+}
+
+func (x IndexRule_Analyzer) Number() protoreflect.EnumNumber {
+       return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use IndexRule_Analyzer.Descriptor instead.
+func (IndexRule_Analyzer) EnumDescriptor() ([]byte, []int) {
+       return file_banyandb_database_v1_schema_proto_rawDescGZIP(), []int{7, 2}
+}
+
 type TagFamilySpec struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
@@ -883,6 +940,8 @@ type IndexRule struct {
        Location IndexRule_Location 
`protobuf:"varint,4,opt,name=location,proto3,enum=banyandb.database.v1.IndexRule_Location"
 json:"location,omitempty"`
        // updated_at indicates when the IndexRule is updated
        UpdatedAt *timestamppb.Timestamp 
`protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" 
json:"updated_at,omitempty"`
+       // analyzer analyzes tag value to support the full-text searching for 
TYPE_INVERTED indices.
+       Analyzer IndexRule_Analyzer 
`protobuf:"varint,6,opt,name=analyzer,proto3,enum=banyandb.database.v1.IndexRule_Analyzer"
 json:"analyzer,omitempty"`
 }
 
 func (x *IndexRule) Reset() {
@@ -952,6 +1011,13 @@ func (x *IndexRule) GetUpdatedAt() *timestamppb.Timestamp 
{
        return nil
 }
 
+func (x *IndexRule) GetAnalyzer() IndexRule_Analyzer {
+       if x != nil {
+               return x.Analyzer
+       }
+       return IndexRule_ANALYZER_UNSPECIFIED
+}
+
 // Subject defines which stream or measure would generate indices
 type Subject struct {
        state         protoimpl.MessageState
@@ -1214,7 +1280,7 @@ var file_banyandb_database_v1_schema_proto_rawDesc = 
[]byte{
        0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 
0x28, 0x0b, 0x32, 0x1a,
        0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 
0x6f, 0x62, 0x75, 0x66,
        0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 
0x75, 0x70, 0x64, 0x61,
-       0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xa4, 0x03, 0x0a, 0x09, 0x49, 0x6e, 
0x64, 0x65, 0x78, 0x52,
+       0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xd2, 0x04, 0x0a, 0x09, 0x49, 0x6e, 
0x64, 0x65, 0x78, 0x52,
        0x75, 0x6c, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 
0x61, 0x74, 0x61, 0x18,
        0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x61, 0x6e, 0x79, 
0x61, 0x6e, 0x64, 0x62,
        0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 
0x65, 0x74, 0x61, 0x64,
@@ -1231,78 +1297,89 @@ var file_banyandb_database_v1_schema_proto_rawDesc = 
[]byte{
        0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 
0x5f, 0x61, 0x74, 0x18,
        0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 
0x6c, 0x65, 0x2e, 0x70,
        0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 
0x73, 0x74, 0x61, 0x6d,
-       0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 
0x22, 0x3e, 0x0a, 0x04,
-       0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x59, 0x50, 0x45, 
0x5f, 0x55, 0x4e, 0x53,
-       0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 
0x0a, 0x09, 0x54, 0x59,
-       0x50, 0x45, 0x5f, 0x54, 0x52, 0x45, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 
0x0d, 0x54, 0x59, 0x50,
-       0x45, 0x5f, 0x49, 0x4e, 0x56, 0x45, 0x52, 0x54, 0x45, 0x44, 0x10, 0x02, 
0x22, 0x4e, 0x0a, 0x08,
-       0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x14, 
0x4c, 0x4f, 0x43, 0x41,
-       0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 
0x46, 0x49, 0x45, 0x44,
-       0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 
0x4f, 0x4e, 0x5f, 0x53,
-       0x45, 0x52, 0x49, 0x45, 0x53, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 
0x4f, 0x43, 0x41, 0x54,
-       0x49, 0x4f, 0x4e, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x10, 0x02, 
0x22, 0x54, 0x0a, 0x07,
-       0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x07, 0x63, 
0x61, 0x74, 0x61, 0x6c,
-       0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x62, 
0x61, 0x6e, 0x79, 0x61,
-       0x6e, 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 
0x31, 0x2e, 0x43, 0x61,
-       0x74, 0x61, 0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 
0x6f, 0x67, 0x12, 0x12,
-       0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 
0x52, 0x04, 0x6e, 0x61,
-       0x6d, 0x65, 0x22, 0xc6, 0x02, 0x0a, 0x10, 0x49, 0x6e, 0x64, 0x65, 0x78, 
0x52, 0x75, 0x6c, 0x65,
-       0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x38, 0x0a, 0x08, 0x6d, 
0x65, 0x74, 0x61, 0x64,
-       0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 
0x62, 0x61, 0x6e, 0x79,
-       0x61, 0x6e, 0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 
0x76, 0x31, 0x2e, 0x4d,
-       0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 
0x61, 0x64, 0x61, 0x74,
-       0x61, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 
0x20, 0x03, 0x28, 0x09,
-       0x52, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x73, 
0x75, 0x62, 0x6a, 0x65,
-       0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 
0x61, 0x6e, 0x79, 0x61,
-       0x6e, 0x64, 0x62, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 
0x2e, 0x76, 0x31, 0x2e,
-       0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x73, 0x75, 0x62, 
0x6a, 0x65, 0x63, 0x74,
-       0x12, 0x35, 0x0a, 0x08, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x61, 0x74, 
0x18, 0x04, 0x20, 0x01,
+       0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 
0x12, 0x44, 0x0a, 0x08,
+       0x61, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 
0x28, 0x0e, 0x32, 0x28,
+       0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x64, 0x61, 
0x74, 0x61, 0x62, 0x61,
+       0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 
0x75, 0x6c, 0x65, 0x2e,
+       0x41, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 0x65, 0x72, 0x52, 0x08, 0x61, 0x6e, 
0x61, 0x6c, 0x79, 0x7a,
+       0x65, 0x72, 0x22, 0x3e, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 
0x0a, 0x10, 0x54, 0x59,
+       0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 
0x45, 0x44, 0x10, 0x00,
+       0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x45, 
0x45, 0x10, 0x01, 0x12,
+       0x11, 0x0a, 0x0d, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x45, 
0x52, 0x54, 0x45, 0x44,
+       0x10, 0x02, 0x22, 0x4e, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 
0x6f, 0x6e, 0x12, 0x18,
+       0x0a, 0x14, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 
0x4e, 0x53, 0x50, 0x45,
+       0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 
0x4c, 0x4f, 0x43, 0x41,
+       0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x52, 0x49, 0x45, 0x53, 0x10, 
0x01, 0x12, 0x13, 0x0a,
+       0x0f, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x47, 0x4c, 
0x4f, 0x42, 0x41, 0x4c,
+       0x10, 0x02, 0x22, 0x66, 0x0a, 0x08, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x7a, 
0x65, 0x72, 0x12, 0x18,
+       0x0a, 0x14, 0x41, 0x4e, 0x41, 0x4c, 0x59, 0x5a, 0x45, 0x52, 0x5f, 0x55, 
0x4e, 0x53, 0x50, 0x45,
+       0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 
0x41, 0x4e, 0x41, 0x4c,
+       0x59, 0x5a, 0x45, 0x52, 0x5f, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52, 0x44, 
0x10, 0x01, 0x12, 0x15,
+       0x0a, 0x11, 0x41, 0x4e, 0x41, 0x4c, 0x59, 0x5a, 0x45, 0x52, 0x5f, 0x53, 
0x54, 0x41, 0x4e, 0x44,
+       0x41, 0x52, 0x44, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x4e, 0x41, 
0x4c, 0x59, 0x5a, 0x45,
+       0x52, 0x5f, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x10, 0x03, 0x22, 0x54, 
0x0a, 0x07, 0x53, 0x75,
+       0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x07, 0x63, 0x61, 0x74, 
0x61, 0x6c, 0x6f, 0x67,
+       0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
+       0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 
0x43, 0x61, 0x74, 0x61,
+       0x6c, 0x6f, 0x67, 0x52, 0x07, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 
0x12, 0x12, 0x0a, 0x04,
+       0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 
0x6e, 0x61, 0x6d, 0x65,
+       0x22, 0xc6, 0x02, 0x0a, 0x10, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x75, 
0x6c, 0x65, 0x42, 0x69,
+       0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 
0x61, 0x64, 0x61, 0x74,
+       0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x61, 
0x6e, 0x79, 0x61, 0x6e,
+       0x64, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 
0x2e, 0x4d, 0x65, 0x74,
+       0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 
0x61, 0x74, 0x61, 0x12,
+       0x14, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 
0x28, 0x09, 0x52, 0x05,
+       0x72, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x73, 0x75, 0x62, 
0x6a, 0x65, 0x63, 0x74,
+       0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
+       0x62, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 
0x31, 0x2e, 0x53, 0x75,
+       0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 
0x63, 0x74, 0x12, 0x35,
+       0x0a, 0x08, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x61, 0x74, 0x18, 0x04, 
0x20, 0x01, 0x28, 0x0b,
+       0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 
0x6f, 0x74, 0x6f, 0x62,
+       0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 
0x52, 0x07, 0x62, 0x65,
+       0x67, 0x69, 0x6e, 0x41, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x65, 0x78, 0x70, 
0x69, 0x72, 0x65, 0x5f,
+       0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 
0x6f, 0x6f, 0x67, 0x6c,
+       0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 
0x69, 0x6d, 0x65, 0x73,
+       0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 
0x41, 0x74, 0x12, 0x39,
+       0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 
0x18, 0x06, 0x20, 0x01,
        0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 
0x70, 0x72, 0x6f, 0x74,
-       0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 
0x6d, 0x70, 0x52, 0x07,
-       0x62, 0x65, 0x67, 0x69, 0x6e, 0x41, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x65, 
0x78, 0x70, 0x69, 0x72,
-       0x65, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 
0x2e, 0x67, 0x6f, 0x6f,
-       0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 
0x2e, 0x54, 0x69, 0x6d,
-       0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x65, 0x78, 0x70, 0x69, 
0x72, 0x65, 0x41, 0x74,
-       0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 
0x61, 0x74, 0x18, 0x06,
-       0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 
0x65, 0x2e, 0x70, 0x72,
-       0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 
0x74, 0x61, 0x6d, 0x70,
-       0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x2a, 
0xa8, 0x01, 0x0a, 0x07,
-       0x54, 0x61, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x54, 
0x41, 0x47, 0x5f, 0x54,
-       0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 
0x49, 0x45, 0x44, 0x10,
-       0x00, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x41, 0x47, 0x5f, 0x54, 0x59, 0x50, 
0x45, 0x5f, 0x53, 0x54,
-       0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x41, 
0x47, 0x5f, 0x54, 0x59,
-       0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 
0x54, 0x41, 0x47, 0x5f,
-       0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 
0x41, 0x52, 0x52, 0x41,
-       0x59, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x41, 0x47, 0x5f, 0x54, 
0x59, 0x50, 0x45, 0x5f,
-       0x49, 0x4e, 0x54, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x10, 0x04, 0x12, 
0x18, 0x0a, 0x14, 0x54,
-       0x41, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 
0x5f, 0x42, 0x49, 0x4e,
-       0x41, 0x52, 0x59, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x41, 0x47, 
0x5f, 0x54, 0x59, 0x50,
-       0x45, 0x5f, 0x49, 0x44, 0x10, 0x06, 0x2a, 0x6e, 0x0a, 0x09, 0x46, 0x69, 
0x65, 0x6c, 0x64, 0x54,
-       0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x45, 0x4c, 0x44, 
0x5f, 0x54, 0x59, 0x50,
+       0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 
0x6d, 0x70, 0x52, 0x09,
+       0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x2a, 0xa8, 0x01, 
0x0a, 0x07, 0x54, 0x61,
+       0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x41, 0x47, 
0x5f, 0x54, 0x59, 0x50,
        0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 
0x44, 0x10, 0x00, 0x12,
-       0x15, 0x0a, 0x11, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x54, 0x59, 0x50, 
0x45, 0x5f, 0x53, 0x54,
-       0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x49, 
0x45, 0x4c, 0x44, 0x5f,
-       0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1a, 
0x0a, 0x16, 0x46, 0x49,
-       0x45, 0x4c, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 
0x41, 0x5f, 0x42, 0x49,
-       0x4e, 0x41, 0x52, 0x59, 0x10, 0x03, 0x2a, 0x4e, 0x0a, 0x0e, 0x45, 0x6e, 
0x63, 0x6f, 0x64, 0x69,
-       0x6e, 0x67, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 
0x45, 0x4e, 0x43, 0x4f,
-       0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 
0x55, 0x4e, 0x53, 0x50,
-       0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 
0x17, 0x45, 0x4e, 0x43,
-       0x4f, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 
0x5f, 0x47, 0x4f, 0x52,
-       0x49, 0x4c, 0x4c, 0x41, 0x10, 0x01, 0x2a, 0x54, 0x0a, 0x11, 0x43, 0x6f, 
0x6d, 0x70, 0x72, 0x65,
-       0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 
0x22, 0x0a, 0x1e, 0x43,
-       0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 
0x45, 0x54, 0x48, 0x4f,
-       0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 
0x44, 0x10, 0x00, 0x12,
-       0x1b, 0x0a, 0x17, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 
0x4f, 0x4e, 0x5f, 0x4d,
-       0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x5a, 0x53, 0x54, 0x44, 0x10, 0x01, 
0x42, 0x72, 0x0a, 0x2a,
-       0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x73, 
0x6b, 0x79, 0x77, 0x61,
-       0x6c, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 
0x64, 0x62, 0x2e, 0x64,
-       0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x5a, 0x44, 
0x67, 0x69, 0x74, 0x68,
-       0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 
0x65, 0x2f, 0x73, 0x6b,
-       0x79, 0x77, 0x61, 0x6c, 0x6b, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
-       0x62, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 
0x62, 0x61, 0x6e, 0x79,
-       0x61, 0x6e, 0x64, 0x62, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 
0x65, 0x2f, 0x76, 0x31,
-       0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+       0x13, 0x0a, 0x0f, 0x54, 0x41, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 
0x53, 0x54, 0x52, 0x49,
+       0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x41, 0x47, 0x5f, 
0x54, 0x59, 0x50, 0x45,
+       0x5f, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x41, 
0x47, 0x5f, 0x54, 0x59,
+       0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x52, 
0x52, 0x41, 0x59, 0x10,
+       0x03, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x41, 0x47, 0x5f, 0x54, 0x59, 0x50, 
0x45, 0x5f, 0x49, 0x4e,
+       0x54, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x10, 0x04, 0x12, 0x18, 0x0a, 
0x14, 0x54, 0x41, 0x47,
+       0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 
0x49, 0x4e, 0x41, 0x52,
+       0x59, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x41, 0x47, 0x5f, 0x54, 
0x59, 0x50, 0x45, 0x5f,
+       0x49, 0x44, 0x10, 0x06, 0x2a, 0x6e, 0x0a, 0x09, 0x46, 0x69, 0x65, 0x6c, 
0x64, 0x54, 0x79, 0x70,
+       0x65, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x54, 
0x59, 0x50, 0x45, 0x5f,
+       0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 
0x00, 0x12, 0x15, 0x0a,
+       0x11, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 
0x53, 0x54, 0x52, 0x49,
+       0x4e, 0x47, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x49, 0x45, 0x4c, 
0x44, 0x5f, 0x54, 0x59,
+       0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 
0x46, 0x49, 0x45, 0x4c,
+       0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 
0x42, 0x49, 0x4e, 0x41,
+       0x52, 0x59, 0x10, 0x03, 0x2a, 0x4e, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x6f, 
0x64, 0x69, 0x6e, 0x67,
+       0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x4e, 
0x43, 0x4f, 0x44, 0x49,
+       0x4e, 0x47, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 
0x53, 0x50, 0x45, 0x43,
+       0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x45, 
0x4e, 0x43, 0x4f, 0x44,
+       0x49, 0x4e, 0x47, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x47, 
0x4f, 0x52, 0x49, 0x4c,
+       0x4c, 0x41, 0x10, 0x01, 0x2a, 0x54, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 
0x72, 0x65, 0x73, 0x73,
+       0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x22, 0x0a, 
0x1e, 0x43, 0x4f, 0x4d,
+       0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 
0x48, 0x4f, 0x44, 0x5f,
+       0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 
0x00, 0x12, 0x1b, 0x0a,
+       0x17, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 
0x5f, 0x4d, 0x45, 0x54,
+       0x48, 0x4f, 0x44, 0x5f, 0x5a, 0x53, 0x54, 0x44, 0x10, 0x01, 0x42, 0x72, 
0x0a, 0x2a, 0x6f, 0x72,
+       0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x73, 0x6b, 0x79, 
0x77, 0x61, 0x6c, 0x6b,
+       0x69, 0x6e, 0x67, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 
0x2e, 0x64, 0x61, 0x74,
+       0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x5a, 0x44, 0x67, 0x69, 
0x74, 0x68, 0x75, 0x62,
+       0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 
0x73, 0x6b, 0x79, 0x77,
+       0x61, 0x6c, 0x6b, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x61, 0x6e, 0x79, 0x61, 
0x6e, 0x64, 0x62, 0x2f,
+       0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x61, 
0x6e, 0x79, 0x61, 0x6e,
+       0x64, 0x62, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2f, 
0x76, 0x31, 0x62, 0x06,
+       0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1317,7 +1394,7 @@ func file_banyandb_database_v1_schema_proto_rawDescGZIP() 
[]byte {
        return file_banyandb_database_v1_schema_proto_rawDescData
 }
 
-var file_banyandb_database_v1_schema_proto_enumTypes = 
make([]protoimpl.EnumInfo, 6)
+var file_banyandb_database_v1_schema_proto_enumTypes = 
make([]protoimpl.EnumInfo, 7)
 var file_banyandb_database_v1_schema_proto_msgTypes = 
make([]protoimpl.MessageInfo, 10)
 var file_banyandb_database_v1_schema_proto_goTypes = []interface{}{
        (TagType)(0),                  // 0: banyandb.database.v1.TagType
@@ -1326,57 +1403,59 @@ var file_banyandb_database_v1_schema_proto_goTypes = 
[]interface{}{
        (CompressionMethod)(0),        // 3: 
banyandb.database.v1.CompressionMethod
        (IndexRule_Type)(0),           // 4: banyandb.database.v1.IndexRule.Type
        (IndexRule_Location)(0),       // 5: 
banyandb.database.v1.IndexRule.Location
-       (*TagFamilySpec)(nil),         // 6: banyandb.database.v1.TagFamilySpec
-       (*TagSpec)(nil),               // 7: banyandb.database.v1.TagSpec
-       (*Stream)(nil),                // 8: banyandb.database.v1.Stream
-       (*Entity)(nil),                // 9: banyandb.database.v1.Entity
-       (*FieldSpec)(nil),             // 10: banyandb.database.v1.FieldSpec
-       (*Measure)(nil),               // 11: banyandb.database.v1.Measure
-       (*TopNAggregation)(nil),       // 12: 
banyandb.database.v1.TopNAggregation
-       (*IndexRule)(nil),             // 13: banyandb.database.v1.IndexRule
-       (*Subject)(nil),               // 14: banyandb.database.v1.Subject
-       (*IndexRuleBinding)(nil),      // 15: 
banyandb.database.v1.IndexRuleBinding
-       (*v1.Metadata)(nil),           // 16: banyandb.common.v1.Metadata
-       (*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp
-       (v11.Sort)(0),                 // 18: banyandb.model.v1.Sort
-       (*v11.Criteria)(nil),          // 19: banyandb.model.v1.Criteria
-       (v1.Catalog)(0),               // 20: banyandb.common.v1.Catalog
+       (IndexRule_Analyzer)(0),       // 6: 
banyandb.database.v1.IndexRule.Analyzer
+       (*TagFamilySpec)(nil),         // 7: banyandb.database.v1.TagFamilySpec
+       (*TagSpec)(nil),               // 8: banyandb.database.v1.TagSpec
+       (*Stream)(nil),                // 9: banyandb.database.v1.Stream
+       (*Entity)(nil),                // 10: banyandb.database.v1.Entity
+       (*FieldSpec)(nil),             // 11: banyandb.database.v1.FieldSpec
+       (*Measure)(nil),               // 12: banyandb.database.v1.Measure
+       (*TopNAggregation)(nil),       // 13: 
banyandb.database.v1.TopNAggregation
+       (*IndexRule)(nil),             // 14: banyandb.database.v1.IndexRule
+       (*Subject)(nil),               // 15: banyandb.database.v1.Subject
+       (*IndexRuleBinding)(nil),      // 16: 
banyandb.database.v1.IndexRuleBinding
+       (*v1.Metadata)(nil),           // 17: banyandb.common.v1.Metadata
+       (*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
+       (v11.Sort)(0),                 // 19: banyandb.model.v1.Sort
+       (*v11.Criteria)(nil),          // 20: banyandb.model.v1.Criteria
+       (v1.Catalog)(0),               // 21: banyandb.common.v1.Catalog
 }
 var file_banyandb_database_v1_schema_proto_depIdxs = []int32{
-       7,  // 0: banyandb.database.v1.TagFamilySpec.tags:type_name -> 
banyandb.database.v1.TagSpec
+       8,  // 0: banyandb.database.v1.TagFamilySpec.tags:type_name -> 
banyandb.database.v1.TagSpec
        0,  // 1: banyandb.database.v1.TagSpec.type:type_name -> 
banyandb.database.v1.TagType
-       16, // 2: banyandb.database.v1.Stream.metadata:type_name -> 
banyandb.common.v1.Metadata
-       6,  // 3: banyandb.database.v1.Stream.tag_families:type_name -> 
banyandb.database.v1.TagFamilySpec
-       9,  // 4: banyandb.database.v1.Stream.entity:type_name -> 
banyandb.database.v1.Entity
-       17, // 5: banyandb.database.v1.Stream.updated_at:type_name -> 
google.protobuf.Timestamp
+       17, // 2: banyandb.database.v1.Stream.metadata:type_name -> 
banyandb.common.v1.Metadata
+       7,  // 3: banyandb.database.v1.Stream.tag_families:type_name -> 
banyandb.database.v1.TagFamilySpec
+       10, // 4: banyandb.database.v1.Stream.entity:type_name -> 
banyandb.database.v1.Entity
+       18, // 5: banyandb.database.v1.Stream.updated_at:type_name -> 
google.protobuf.Timestamp
        1,  // 6: banyandb.database.v1.FieldSpec.field_type:type_name -> 
banyandb.database.v1.FieldType
        2,  // 7: banyandb.database.v1.FieldSpec.encoding_method:type_name -> 
banyandb.database.v1.EncodingMethod
        3,  // 8: banyandb.database.v1.FieldSpec.compression_method:type_name 
-> banyandb.database.v1.CompressionMethod
-       16, // 9: banyandb.database.v1.Measure.metadata:type_name -> 
banyandb.common.v1.Metadata
-       6,  // 10: banyandb.database.v1.Measure.tag_families:type_name -> 
banyandb.database.v1.TagFamilySpec
-       10, // 11: banyandb.database.v1.Measure.fields:type_name -> 
banyandb.database.v1.FieldSpec
-       9,  // 12: banyandb.database.v1.Measure.entity:type_name -> 
banyandb.database.v1.Entity
-       17, // 13: banyandb.database.v1.Measure.updated_at:type_name -> 
google.protobuf.Timestamp
-       16, // 14: banyandb.database.v1.TopNAggregation.metadata:type_name -> 
banyandb.common.v1.Metadata
-       16, // 15: 
banyandb.database.v1.TopNAggregation.source_measure:type_name -> 
banyandb.common.v1.Metadata
-       18, // 16: 
banyandb.database.v1.TopNAggregation.field_value_sort:type_name -> 
banyandb.model.v1.Sort
-       19, // 17: banyandb.database.v1.TopNAggregation.criteria:type_name -> 
banyandb.model.v1.Criteria
-       17, // 18: banyandb.database.v1.TopNAggregation.updated_at:type_name -> 
google.protobuf.Timestamp
-       16, // 19: banyandb.database.v1.IndexRule.metadata:type_name -> 
banyandb.common.v1.Metadata
+       17, // 9: banyandb.database.v1.Measure.metadata:type_name -> 
banyandb.common.v1.Metadata
+       7,  // 10: banyandb.database.v1.Measure.tag_families:type_name -> 
banyandb.database.v1.TagFamilySpec
+       11, // 11: banyandb.database.v1.Measure.fields:type_name -> 
banyandb.database.v1.FieldSpec
+       10, // 12: banyandb.database.v1.Measure.entity:type_name -> 
banyandb.database.v1.Entity
+       18, // 13: banyandb.database.v1.Measure.updated_at:type_name -> 
google.protobuf.Timestamp
+       17, // 14: banyandb.database.v1.TopNAggregation.metadata:type_name -> 
banyandb.common.v1.Metadata
+       17, // 15: 
banyandb.database.v1.TopNAggregation.source_measure:type_name -> 
banyandb.common.v1.Metadata
+       19, // 16: 
banyandb.database.v1.TopNAggregation.field_value_sort:type_name -> 
banyandb.model.v1.Sort
+       20, // 17: banyandb.database.v1.TopNAggregation.criteria:type_name -> 
banyandb.model.v1.Criteria
+       18, // 18: banyandb.database.v1.TopNAggregation.updated_at:type_name -> 
google.protobuf.Timestamp
+       17, // 19: banyandb.database.v1.IndexRule.metadata:type_name -> 
banyandb.common.v1.Metadata
        4,  // 20: banyandb.database.v1.IndexRule.type:type_name -> 
banyandb.database.v1.IndexRule.Type
        5,  // 21: banyandb.database.v1.IndexRule.location:type_name -> 
banyandb.database.v1.IndexRule.Location
-       17, // 22: banyandb.database.v1.IndexRule.updated_at:type_name -> 
google.protobuf.Timestamp
-       20, // 23: banyandb.database.v1.Subject.catalog:type_name -> 
banyandb.common.v1.Catalog
-       16, // 24: banyandb.database.v1.IndexRuleBinding.metadata:type_name -> 
banyandb.common.v1.Metadata
-       14, // 25: banyandb.database.v1.IndexRuleBinding.subject:type_name -> 
banyandb.database.v1.Subject
-       17, // 26: banyandb.database.v1.IndexRuleBinding.begin_at:type_name -> 
google.protobuf.Timestamp
-       17, // 27: banyandb.database.v1.IndexRuleBinding.expire_at:type_name -> 
google.protobuf.Timestamp
-       17, // 28: banyandb.database.v1.IndexRuleBinding.updated_at:type_name 
-> google.protobuf.Timestamp
-       29, // [29:29] is the sub-list for method output_type
-       29, // [29:29] is the sub-list for method input_type
-       29, // [29:29] is the sub-list for extension type_name
-       29, // [29:29] is the sub-list for extension extendee
-       0,  // [0:29] is the sub-list for field type_name
+       18, // 22: banyandb.database.v1.IndexRule.updated_at:type_name -> 
google.protobuf.Timestamp
+       6,  // 23: banyandb.database.v1.IndexRule.analyzer:type_name -> 
banyandb.database.v1.IndexRule.Analyzer
+       21, // 24: banyandb.database.v1.Subject.catalog:type_name -> 
banyandb.common.v1.Catalog
+       17, // 25: banyandb.database.v1.IndexRuleBinding.metadata:type_name -> 
banyandb.common.v1.Metadata
+       15, // 26: banyandb.database.v1.IndexRuleBinding.subject:type_name -> 
banyandb.database.v1.Subject
+       18, // 27: banyandb.database.v1.IndexRuleBinding.begin_at:type_name -> 
google.protobuf.Timestamp
+       18, // 28: banyandb.database.v1.IndexRuleBinding.expire_at:type_name -> 
google.protobuf.Timestamp
+       18, // 29: banyandb.database.v1.IndexRuleBinding.updated_at:type_name 
-> google.protobuf.Timestamp
+       30, // [30:30] is the sub-list for method output_type
+       30, // [30:30] is the sub-list for method input_type
+       30, // [30:30] is the sub-list for extension type_name
+       30, // [30:30] is the sub-list for extension extendee
+       0,  // [0:30] is the sub-list for field type_name
 }
 
 func init() { file_banyandb_database_v1_schema_proto_init() }
@@ -1511,7 +1590,7 @@ func file_banyandb_database_v1_schema_proto_init() {
                File: protoimpl.DescBuilder{
                        GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
                        RawDescriptor: 
file_banyandb_database_v1_schema_proto_rawDesc,
-                       NumEnums:      6,
+                       NumEnums:      7,
                        NumMessages:   10,
                        NumExtensions: 0,
                        NumServices:   0,
diff --git a/api/proto/banyandb/database/v1/schema.pb.validate.go 
b/api/proto/banyandb/database/v1/schema.pb.validate.go
index fc76c03..6aa1f09 100644
--- a/api/proto/banyandb/database/v1/schema.pb.validate.go
+++ b/api/proto/banyandb/database/v1/schema.pb.validate.go
@@ -1263,6 +1263,8 @@ func (m *IndexRule) validate(all bool) error {
                }
        }
 
+       // no validation rules for Analyzer
+
        if len(errors) > 0 {
                return IndexRuleMultiError(errors)
        }
diff --git a/api/proto/banyandb/database/v1/schema.proto 
b/api/proto/banyandb/database/v1/schema.proto
index 86b9cad..4751434 100644
--- a/api/proto/banyandb/database/v1/schema.proto
+++ b/api/proto/banyandb/database/v1/schema.proto
@@ -158,6 +158,19 @@ message IndexRule {
     Location location = 4;
     // updated_at indicates when the IndexRule is updated
     google.protobuf.Timestamp updated_at = 5;
+    enum Analyzer {
+        ANALYZER_UNSPECIFIED = 0;
+        // Keyword analyzer is a “noop” analyzer which returns the entire 
input string as a single token.
+        ANALYZER_KEYWORD = 1;
+        // Standard analyzer provides grammar based tokenization
+        ANALYZER_STANDARD = 2;
+        // Simple analyzer breaks text into tokens at any non-letter 
character, 
+        // such as numbers, spaces, hyphens and apostrophes, discards 
non-letter characters, 
+        // and changes uppercase to lowercase.
+        ANALYZER_SIMPLE = 3;
+    }
+    // analyzer analyzes tag value to support the full-text searching for 
TYPE_INVERTED indices.
+    Analyzer analyzer = 6;
 }
 
 // Subject defines which stream or measure would generate indices
diff --git a/api/proto/banyandb/model/v1/query.pb.go 
b/api/proto/banyandb/model/v1/query.pb.go
index c9e2fcf..e4248ed 100644
--- a/api/proto/banyandb/model/v1/query.pb.go
+++ b/api/proto/banyandb/model/v1/query.pb.go
@@ -92,6 +92,9 @@ func (Sort) EnumDescriptor() ([]byte, []int) {
 // For EQ, NE, LT, GT, LE and GE, only one operand should be given, i.e. 
one-to-one relationship.
 // HAVING and NOT_HAVING allow multi-value to be the operand such as 
array/vector, i.e. one-to-many relationship.
 // For example, "keyA" contains "valueA" **and** "valueB"
+// MATCH performances a full-text search if the tag is analyzed.
+// The string value applies to the same analyzer as the tag, but string array 
value does not.
+// Each item in a string array is seen as a token instead of a query 
expression.
 type Condition_BinaryOp int32
 
 const (
@@ -106,6 +109,7 @@ const (
        Condition_BINARY_OP_NOT_HAVING  Condition_BinaryOp = 8
        Condition_BINARY_OP_IN          Condition_BinaryOp = 9
        Condition_BINARY_OP_NOT_IN      Condition_BinaryOp = 10
+       Condition_BINARY_OP_MATCH       Condition_BinaryOp = 11
 )
 
 // Enum value maps for Condition_BinaryOp.
@@ -122,6 +126,7 @@ var (
                8:  "BINARY_OP_NOT_HAVING",
                9:  "BINARY_OP_IN",
                10: "BINARY_OP_NOT_IN",
+               11: "BINARY_OP_MATCH",
        }
        Condition_BinaryOp_value = map[string]int32{
                "BINARY_OP_UNSPECIFIED": 0,
@@ -135,6 +140,7 @@ var (
                "BINARY_OP_NOT_HAVING":  8,
                "BINARY_OP_IN":          9,
                "BINARY_OP_NOT_IN":      10,
+               "BINARY_OP_MATCH":       11,
        }
 )
 
@@ -639,7 +645,7 @@ var file_banyandb_model_v1_query_proto_rawDesc = []byte{
        0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 
0x04, 0x74, 0x61, 0x67,
        0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x61, 
0x6e, 0x79, 0x61, 0x6e,
        0x64, 0x62, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 
0x54, 0x61, 0x67, 0x52,
-       0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0xf5, 0x02, 0x0a, 0x09, 0x43, 0x6f, 
0x6e, 0x64, 0x69, 0x74,
+       0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x8a, 0x03, 0x0a, 0x09, 0x43, 0x6f, 
0x6e, 0x64, 0x69, 0x74,
        0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 
0x01, 0x20, 0x01, 0x28,
        0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x02, 0x6f, 
0x70, 0x18, 0x02, 0x20,
        0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 
0x64, 0x62, 0x2e, 0x6d,
@@ -648,7 +654,7 @@ var file_banyandb_model_v1_query_proto_rawDesc = []byte{
        0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 
0x0b, 0x32, 0x1b, 0x2e,
        0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x6d, 0x6f, 0x64, 
0x65, 0x6c, 0x2e, 0x76,
        0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 
0x76, 0x61, 0x6c, 0x75,
-       0x65, 0x22, 0xe9, 0x01, 0x0a, 0x08, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 
0x4f, 0x70, 0x12, 0x19,
+       0x65, 0x22, 0xfe, 0x01, 0x0a, 0x08, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 
0x4f, 0x70, 0x12, 0x19,
        0x0a, 0x15, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x5f, 0x4f, 0x50, 0x5f, 
0x55, 0x4e, 0x53, 0x50,
        0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 
0x0c, 0x42, 0x49, 0x4e,
        0x41, 0x52, 0x59, 0x5f, 0x4f, 0x50, 0x5f, 0x45, 0x51, 0x10, 0x01, 0x12, 
0x10, 0x0a, 0x0c, 0x42,
@@ -662,48 +668,50 @@ var file_banyandb_model_v1_query_proto_rawDesc = []byte{
        0x49, 0x4e, 0x41, 0x52, 0x59, 0x5f, 0x4f, 0x50, 0x5f, 0x4e, 0x4f, 0x54, 
0x5f, 0x48, 0x41, 0x56,
        0x49, 0x4e, 0x47, 0x10, 0x08, 0x12, 0x10, 0x0a, 0x0c, 0x42, 0x49, 0x4e, 
0x41, 0x52, 0x59, 0x5f,
        0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x14, 0x0a, 0x10, 0x42, 
0x49, 0x4e, 0x41, 0x52,
-       0x59, 0x5f, 0x4f, 0x50, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4e, 0x10, 
0x0a, 0x22, 0x70, 0x0a,
-       0x08, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x12, 0x26, 0x0a, 
0x0f, 0x74, 0x61, 0x67,
-       0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 
0x18, 0x01, 0x20, 0x01,
-       0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x67, 0x46, 0x61, 0x6d, 0x69, 0x6c, 
0x79, 0x4e, 0x61, 0x6d,
-       0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 
0x6f, 0x6e, 0x73, 0x18,
-       0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x61, 0x6e, 0x79, 
0x61, 0x6e, 0x64, 0x62,
-       0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 
0x6e, 0x64, 0x69, 0x74,
-       0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 
0x6f, 0x6e, 0x73, 0x22,
-       0x61, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x72, 0x64, 0x65, 
0x72, 0x12, 0x26, 0x0a,
-       0x0f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x5f, 
0x6e, 0x61, 0x6d, 0x65,
-       0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x64, 0x65, 
0x78, 0x52, 0x75, 0x6c,
-       0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x73, 0x6f, 0x72, 
0x74, 0x18, 0x02, 0x20,
-       0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 
0x64, 0x62, 0x2e, 0x6d,
-       0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x72, 0x74, 
0x52, 0x04, 0x73, 0x6f,
-       0x72, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x0d, 0x54, 0x61, 0x67, 0x50, 0x72, 
0x6f, 0x6a, 0x65, 0x63,
-       0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, 0x0c, 0x74, 0x61, 0x67, 0x5f, 
0x66, 0x61, 0x6d, 0x69,
-       0x6c, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 
0x2e, 0x62, 0x61, 0x6e,
-       0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 
0x76, 0x31, 0x2e, 0x54,
-       0x61, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 
0x2e, 0x54, 0x61, 0x67,
-       0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 
0x01, 0x02, 0x08, 0x01,
-       0x52, 0x0b, 0x74, 0x61, 0x67, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 
0x73, 0x1a, 0x33, 0x0a,
-       0x09, 0x54, 0x61, 0x67, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x12, 
0x0a, 0x04, 0x6e, 0x61,
-       0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 
0x6d, 0x65, 0x12, 0x12,
-       0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 
0x52, 0x04, 0x74, 0x61,
-       0x67, 0x73, 0x22, 0x6b, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 
0x6e, 0x67, 0x65, 0x12,
-       0x30, 0x0a, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 
0x28, 0x0b, 0x32, 0x1a,
-       0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 
0x6f, 0x62, 0x75, 0x66,
-       0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 
0x62, 0x65, 0x67, 0x69,
-       0x6e, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 
0x28, 0x0b, 0x32, 0x1a,
-       0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 
0x6f, 0x62, 0x75, 0x66,
-       0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 
0x65, 0x6e, 0x64, 0x2a,
-       0x39, 0x0a, 0x04, 0x53, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x10, 0x53, 
0x4f, 0x52, 0x54, 0x5f,
-       0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 
0x00, 0x12, 0x0d, 0x0a,
-       0x09, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x44, 0x45, 0x53, 0x43, 0x10, 0x01, 
0x12, 0x0c, 0x0a, 0x08,
-       0x53, 0x4f, 0x52, 0x54, 0x5f, 0x41, 0x53, 0x43, 0x10, 0x02, 0x42, 0x6c, 
0x0a, 0x27, 0x6f, 0x72,
-       0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x73, 0x6b, 0x79, 
0x77, 0x61, 0x6c, 0x6b,
-       0x69, 0x6e, 0x67, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 
0x2e, 0x6d, 0x6f, 0x64,
-       0x65, 0x6c, 0x2e, 0x76, 0x31, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 
0x62, 0x2e, 0x63, 0x6f,
-       0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x73, 0x6b, 0x79, 
0x77, 0x61, 0x6c, 0x6b,
-       0x69, 0x6e, 0x67, 0x2d, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 
0x2f, 0x61, 0x70, 0x69,
-       0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x62, 0x61, 0x6e, 0x79, 0x61, 
0x6e, 0x64, 0x62, 0x2f,
-       0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 
0x6f, 0x74, 0x6f, 0x33,
+       0x59, 0x5f, 0x4f, 0x50, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4e, 0x10, 
0x0a, 0x12, 0x13, 0x0a,
+       0x0f, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x5f, 0x4f, 0x50, 0x5f, 0x4d, 
0x41, 0x54, 0x43, 0x48,
+       0x10, 0x0b, 0x22, 0x70, 0x0a, 0x08, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 
0x69, 0x61, 0x12, 0x26,
+       0x0a, 0x0f, 0x74, 0x61, 0x67, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 
0x5f, 0x6e, 0x61, 0x6d,
+       0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x67, 
0x46, 0x61, 0x6d, 0x69,
+       0x6c, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x63, 0x6f, 
0x6e, 0x64, 0x69, 0x74,
+       0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 
0x2e, 0x62, 0x61, 0x6e,
+       0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 
0x76, 0x31, 0x2e, 0x43,
+       0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 
0x6e, 0x64, 0x69, 0x74,
+       0x69, 0x6f, 0x6e, 0x73, 0x22, 0x61, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 
0x79, 0x4f, 0x72, 0x64,
+       0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 
0x72, 0x75, 0x6c, 0x65,
+       0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 
0x0d, 0x69, 0x6e, 0x64,
+       0x65, 0x78, 0x52, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 
0x0a, 0x04, 0x73, 0x6f,
+       0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x62, 
0x61, 0x6e, 0x79, 0x61,
+       0x6e, 0x64, 0x62, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 
0x2e, 0x53, 0x6f, 0x72,
+       0x74, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x0d, 
0x54, 0x61, 0x67, 0x50,
+       0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, 
0x0c, 0x74, 0x61, 0x67,
+       0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 
0x03, 0x28, 0x0b, 0x32,
+       0x2a, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x6d, 
0x6f, 0x64, 0x65, 0x6c,
+       0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, 
0x63, 0x74, 0x69, 0x6f,
+       0x6e, 0x2e, 0x54, 0x61, 0x67, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x42, 
0x08, 0xfa, 0x42, 0x05,
+       0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x0b, 0x74, 0x61, 0x67, 0x46, 0x61, 
0x6d, 0x69, 0x6c, 0x69,
+       0x65, 0x73, 0x1a, 0x33, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x46, 0x61, 0x6d, 
0x69, 0x6c, 0x79, 0x12,
+       0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 
0x09, 0x52, 0x04, 0x6e,
+       0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 
0x02, 0x20, 0x03, 0x28,
+       0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x6b, 0x0a, 0x09, 0x54, 
0x69, 0x6d, 0x65, 0x52,
+       0x61, 0x6e, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x62, 0x65, 0x67, 0x69, 
0x6e, 0x18, 0x01, 0x20,
+       0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 
0x2e, 0x70, 0x72, 0x6f,
+       0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 
0x61, 0x6d, 0x70, 0x52,
+       0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 
0x64, 0x18, 0x02, 0x20,
+       0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 
0x2e, 0x70, 0x72, 0x6f,
+       0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 
0x61, 0x6d, 0x70, 0x52,
+       0x03, 0x65, 0x6e, 0x64, 0x2a, 0x39, 0x0a, 0x04, 0x53, 0x6f, 0x72, 0x74, 
0x12, 0x14, 0x0a, 0x10,
+       0x53, 0x4f, 0x52, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 
0x46, 0x49, 0x45, 0x44,
+       0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x44, 
0x45, 0x53, 0x43, 0x10,
+       0x01, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x41, 0x53, 
0x43, 0x10, 0x02, 0x42,
+       0x6c, 0x0a, 0x27, 0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 
0x65, 0x2e, 0x73, 0x6b,
+       0x79, 0x77, 0x61, 0x6c, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
+       0x62, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x5a, 0x41, 
0x67, 0x69, 0x74, 0x68,
+       0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 
0x65, 0x2f, 0x73, 0x6b,
+       0x79, 0x77, 0x61, 0x6c, 0x6b, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
+       0x62, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 
0x62, 0x61, 0x6e, 0x79,
+       0x61, 0x6e, 0x64, 0x62, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x76, 
0x31, 0x62, 0x06, 0x70,
+       0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/proto/banyandb/model/v1/query.proto 
b/api/proto/banyandb/model/v1/query.proto
index 83b6631..e26e0c5 100644
--- a/api/proto/banyandb/model/v1/query.proto
+++ b/api/proto/banyandb/model/v1/query.proto
@@ -48,6 +48,9 @@ message Condition {
     // For EQ, NE, LT, GT, LE and GE, only one operand should be given, i.e. 
one-to-one relationship.
     // HAVING and NOT_HAVING allow multi-value to be the operand such as 
array/vector, i.e. one-to-many relationship.
     // For example, "keyA" contains "valueA" **and** "valueB"
+    // MATCH performances a full-text search if the tag is analyzed.
+    // The string value applies to the same analyzer as the tag, but string 
array value does not.
+    // Each item in a string array is seen as a token instead of a query 
expression.
     enum BinaryOp {
         BINARY_OP_UNSPECIFIED = 0;
         BINARY_OP_EQ = 1;
@@ -60,6 +63,7 @@ message Condition {
         BINARY_OP_NOT_HAVING = 8;
         BINARY_OP_IN = 9;
         BINARY_OP_NOT_IN = 10;
+        BINARY_OP_MATCH = 11;
     }
     string name = 1;
     BinaryOp op = 2;
diff --git a/api/proto/openapi/banyandb/database/v1/rpc.swagger.json 
b/api/proto/openapi/banyandb/database/v1/rpc.swagger.json
index 729a332..f301bd6 100644
--- a/api/proto/openapi/banyandb/database/v1/rpc.swagger.json
+++ b/api/proto/openapi/banyandb/database/v1/rpc.swagger.json
@@ -653,6 +653,10 @@
                       "type": "string",
                       "format": "date-time",
                       "title": "updated_at indicates when the IndexRule is 
updated"
+                    },
+                    "analyzer": {
+                      "$ref": "#/definitions/IndexRuleAnalyzer",
+                      "description": "analyzer analyzes tag value to support 
the full-text searching for TYPE_INVERTED indices."
                     }
                   },
                   "description": "IndexRule defines how to generate indices 
based on tags and the index type\nIndexRule should bind to a subject through an 
IndexRuleBinding to generate proper indices."
@@ -1356,10 +1360,22 @@
         "BINARY_OP_HAVING",
         "BINARY_OP_NOT_HAVING",
         "BINARY_OP_IN",
-        "BINARY_OP_NOT_IN"
+        "BINARY_OP_NOT_IN",
+        "BINARY_OP_MATCH"
       ],
       "default": "BINARY_OP_UNSPECIFIED",
-      "title": "BinaryOp specifies the operation imposed to the given query 
condition\nFor EQ, NE, LT, GT, LE and GE, only one operand should be given, 
i.e. one-to-one relationship.\nHAVING and NOT_HAVING allow multi-value to be 
the operand such as array/vector, i.e. one-to-many relationship.\nFor example, 
\"keyA\" contains \"valueA\" **and** \"valueB\""
+      "description": "BinaryOp specifies the operation imposed to the given 
query condition\nFor EQ, NE, LT, GT, LE and GE, only one operand should be 
given, i.e. one-to-one relationship.\nHAVING and NOT_HAVING allow multi-value 
to be the operand such as array/vector, i.e. one-to-many relationship.\nFor 
example, \"keyA\" contains \"valueA\" **and** \"valueB\"\nMATCH performances a 
full-text search if the tag is analyzed.\nThe string value applies to the same 
analyzer as the tag, but stri [...]
+    },
+    "IndexRuleAnalyzer": {
+      "type": "string",
+      "enum": [
+        "ANALYZER_UNSPECIFIED",
+        "ANALYZER_KEYWORD",
+        "ANALYZER_STANDARD",
+        "ANALYZER_SIMPLE"
+      ],
+      "default": "ANALYZER_UNSPECIFIED",
+      "description": " - ANALYZER_KEYWORD: Keyword analyzer is a “noop” 
analyzer which returns the entire input string as a single token.\n - 
ANALYZER_STANDARD: Standard analyzer provides grammar based tokenization\n - 
ANALYZER_SIMPLE: Simple analyzer breaks text into tokens at any non-letter 
character, \nsuch as numbers, spaces, hyphens and apostrophes, discards 
non-letter characters, \nand changes uppercase to lowercase."
     },
     "protobufAny": {
       "type": "object",
@@ -1592,6 +1608,10 @@
           "type": "string",
           "format": "date-time",
           "title": "updated_at indicates when the IndexRule is updated"
+        },
+        "analyzer": {
+          "$ref": "#/definitions/IndexRuleAnalyzer",
+          "description": "analyzer analyzes tag value to support the full-text 
searching for TYPE_INVERTED indices."
         }
       },
       "description": "IndexRule defines how to generate indices based on tags 
and the index type\nIndexRule should bind to a subject through an 
IndexRuleBinding to generate proper indices."
diff --git a/api/proto/openapi/banyandb/measure/v1/rpc.swagger.json 
b/api/proto/openapi/banyandb/measure/v1/rpc.swagger.json
index fc0d786..843f477 100644
--- a/api/proto/openapi/banyandb/measure/v1/rpc.swagger.json
+++ b/api/proto/openapi/banyandb/measure/v1/rpc.swagger.json
@@ -64,10 +64,11 @@
         "BINARY_OP_HAVING",
         "BINARY_OP_NOT_HAVING",
         "BINARY_OP_IN",
-        "BINARY_OP_NOT_IN"
+        "BINARY_OP_NOT_IN",
+        "BINARY_OP_MATCH"
       ],
       "default": "BINARY_OP_UNSPECIFIED",
-      "title": "BinaryOp specifies the operation imposed to the given query 
condition\nFor EQ, NE, LT, GT, LE and GE, only one operand should be given, 
i.e. one-to-one relationship.\nHAVING and NOT_HAVING allow multi-value to be 
the operand such as array/vector, i.e. one-to-many relationship.\nFor example, 
\"keyA\" contains \"valueA\" **and** \"valueB\""
+      "description": "BinaryOp specifies the operation imposed to the given 
query condition\nFor EQ, NE, LT, GT, LE and GE, only one operand should be 
given, i.e. one-to-one relationship.\nHAVING and NOT_HAVING allow multi-value 
to be the operand such as array/vector, i.e. one-to-many relationship.\nFor 
example, \"keyA\" contains \"valueA\" **and** \"valueB\"\nMATCH performances a 
full-text search if the tag is analyzed.\nThe string value applies to the same 
analyzer as the tag, but stri [...]
     },
     "DataPointField": {
       "type": "object",
diff --git a/api/proto/openapi/banyandb/stream/v1/rpc.swagger.json 
b/api/proto/openapi/banyandb/stream/v1/rpc.swagger.json
index 6b5b264..71710d8 100644
--- a/api/proto/openapi/banyandb/stream/v1/rpc.swagger.json
+++ b/api/proto/openapi/banyandb/stream/v1/rpc.swagger.json
@@ -64,10 +64,11 @@
         "BINARY_OP_HAVING",
         "BINARY_OP_NOT_HAVING",
         "BINARY_OP_IN",
-        "BINARY_OP_NOT_IN"
+        "BINARY_OP_NOT_IN",
+        "BINARY_OP_MATCH"
       ],
       "default": "BINARY_OP_UNSPECIFIED",
-      "title": "BinaryOp specifies the operation imposed to the given query 
condition\nFor EQ, NE, LT, GT, LE and GE, only one operand should be given, 
i.e. one-to-one relationship.\nHAVING and NOT_HAVING allow multi-value to be 
the operand such as array/vector, i.e. one-to-many relationship.\nFor example, 
\"keyA\" contains \"valueA\" **and** \"valueB\""
+      "description": "BinaryOp specifies the operation imposed to the given 
query condition\nFor EQ, NE, LT, GT, LE and GE, only one operand should be 
given, i.e. one-to-one relationship.\nHAVING and NOT_HAVING allow multi-value 
to be the operand such as array/vector, i.e. one-to-many relationship.\nFor 
example, \"keyA\" contains \"valueA\" **and** \"valueB\"\nMATCH performances a 
full-text search if the tag is analyzed.\nThe string value applies to the same 
analyzer as the tag, but stri [...]
     },
     "modelv1Tag": {
       "type": "object",
diff --git a/banyand/tsdb/index/writer.go b/banyand/tsdb/index/writer.go
index 4204b0f..a7b72f4 100644
--- a/banyand/tsdb/index/writer.go
+++ b/banyand/tsdb/index/writer.go
@@ -164,6 +164,7 @@ func (s *Writer) writeGlobalIndex(scope tsdb.Entry, ref 
tsdb.GlobalItemID, value
                                rr = append(rr, index.Field{
                                        Key: index.FieldKey{
                                                IndexRuleID: 
rule.GetMetadata().GetId(),
+                                               Analyzer:    rule.Analyzer,
                                        },
                                        Term: val,
                                })
@@ -203,7 +204,7 @@ func (s *Writer) writeGlobalIndex(scope tsdb.Entry, ref 
tsdb.GlobalItemID, value
 
 func (s *Writer) writeLocalIndex(writer tsdb.Writer, value Value) (err error) {
        collect := func(ruleIndexes []*partition.IndexRuleLocator, fn 
func(fields []index.Field) error) error {
-               var fields []index.Field
+               fields := make([]index.Field, 0)
                for _, ruleIndex := range ruleIndexes {
                        values, _, err := getIndexValue(ruleIndex, value)
                        if err != nil {
@@ -217,6 +218,7 @@ func (s *Writer) writeLocalIndex(writer tsdb.Writer, value 
Value) (err error) {
                                fields = append(fields, index.Field{
                                        Key: index.FieldKey{
                                                IndexRuleID: 
rule.GetMetadata().GetId(),
+                                               Analyzer:    rule.Analyzer,
                                        },
                                        Term: val,
                                })
diff --git a/banyand/tsdb/series_seek.go b/banyand/tsdb/series_seek.go
index 9051ed4..dca7791 100644
--- a/banyand/tsdb/series_seek.go
+++ b/banyand/tsdb/series_seek.go
@@ -56,9 +56,10 @@ type seekerBuilder struct {
        seriesSpan *seriesSpan
 
        conditions []struct {
-               indexRuleType databasev1.IndexRule_Type
-               indexRuleID   uint32
-               condition     Condition
+               indexRuleType     databasev1.IndexRule_Type
+               indexRuleID       uint32
+               indexRuleAnalyzer databasev1.IndexRule_Analyzer
+               condition         Condition
        }
        order               modelv1.Sort
        indexRuleForSorting *databasev1.IndexRule
diff --git a/banyand/tsdb/series_seek_filter.go 
b/banyand/tsdb/series_seek_filter.go
index 6a63293..2f14489 100644
--- a/banyand/tsdb/series_seek_filter.go
+++ b/banyand/tsdb/series_seek_filter.go
@@ -31,13 +31,15 @@ type Condition map[string][]index.ConditionValue
 
 func (s *seekerBuilder) Filter(indexRule *databasev1.IndexRule, condition 
Condition) SeekerBuilder {
        s.conditions = append(s.conditions, struct {
-               indexRuleType databasev1.IndexRule_Type
-               indexRuleID   uint32
-               condition     Condition
+               indexRuleType     databasev1.IndexRule_Type
+               indexRuleID       uint32
+               indexRuleAnalyzer databasev1.IndexRule_Analyzer
+               condition         Condition
        }{
-               indexRuleType: indexRule.GetType(),
-               indexRuleID:   indexRule.GetMetadata().GetId(),
-               condition:     condition,
+               indexRuleType:     indexRule.GetType(),
+               indexRuleID:       indexRule.GetMetadata().GetId(),
+               indexRuleAnalyzer: indexRule.Analyzer,
+               condition:         condition,
        })
        return s
 }
@@ -61,6 +63,7 @@ func (s *seekerBuilder) buildConditions() ([]condWithIRT, 
error) {
                term := index.FieldKey{
                        SeriesID:    s.seriesSpan.seriesID,
                        IndexRuleID: condition.indexRuleID,
+                       Analyzer:    condition.indexRuleAnalyzer,
                }
                for _, c := range condition.condition {
                        cond[term] = c
diff --git a/docs/api-reference.md b/docs/api-reference.md
index 3c55cd7..d871d63 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -61,6 +61,7 @@
     - [CompressionMethod](#banyandb-database-v1-CompressionMethod)
     - [EncodingMethod](#banyandb-database-v1-EncodingMethod)
     - [FieldType](#banyandb-database-v1-FieldType)
+    - [IndexRule.Analyzer](#banyandb-database-v1-IndexRule-Analyzer)
     - [IndexRule.Location](#banyandb-database-v1-IndexRule-Location)
     - [IndexRule.Type](#banyandb-database-v1-IndexRule-Type)
     - [TagType](#banyandb-database-v1-TagType)
@@ -725,6 +726,9 @@ BinaryOp specifies the operation imposed to the given query 
condition
 For EQ, NE, LT, GT, LE and GE, only one operand should be given, i.e. 
one-to-one relationship.
 HAVING and NOT_HAVING allow multi-value to be the operand such as 
array/vector, i.e. one-to-many relationship.
 For example, &#34;keyA&#34; contains &#34;valueA&#34; **and** &#34;valueB&#34;
+MATCH performances a full-text search if the tag is analyzed.
+The string value applies to the same analyzer as the tag, but string array 
value does not.
+Each item in a string array is seen as a token instead of a query expression.
 
 | Name | Number | Description |
 | ---- | ------ | ----------- |
@@ -739,6 +743,7 @@ For example, &#34;keyA&#34; contains &#34;valueA&#34; 
**and** &#34;valueB&#34;
 | BINARY_OP_NOT_HAVING | 8 |  |
 | BINARY_OP_IN | 9 |  |
 | BINARY_OP_NOT_IN | 10 |  |
+| BINARY_OP_MATCH | 11 |  |
 
 
 
@@ -816,6 +821,7 @@ IndexRule should bind to a subject through an 
IndexRuleBinding to generate prope
 | type | [IndexRule.Type](#banyandb-database-v1-IndexRule-Type) |  | type is 
the IndexType of this IndexObject. |
 | location | [IndexRule.Location](#banyandb-database-v1-IndexRule-Location) |  
| location indicates where to store index. |
 | updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) |  | 
updated_at indicates when the IndexRule is updated |
+| analyzer | [IndexRule.Analyzer](#banyandb-database-v1-IndexRule-Analyzer) |  
| analyzer analyzes tag value to support the full-text searching for 
TYPE_INVERTED indices. |
 
 
 
@@ -992,6 +998,20 @@ TopNAggregation generates offline TopN statistics for a 
measure&#39;s TopN appro
 
 
 
+<a name="banyandb-database-v1-IndexRule-Analyzer"></a>
+
+### IndexRule.Analyzer
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| ANALYZER_UNSPECIFIED | 0 |  |
+| ANALYZER_KEYWORD | 1 | Keyword analyzer is a “noop” analyzer which returns 
the entire input string as a single token. |
+| ANALYZER_STANDARD | 2 | Standard analyzer provides grammar based 
tokenization |
+| ANALYZER_SIMPLE | 3 | Simple analyzer breaks text into tokens at any 
non-letter character, such as numbers, spaces, hyphens and apostrophes, 
discards non-letter characters, and changes uppercase to lowercase. |
+
+
+
 <a name="banyandb-database-v1-IndexRule-Location"></a>
 
 ### IndexRule.Location
diff --git a/pkg/index/index.go b/pkg/index/index.go
index b9b332d..ad53ba5 100644
--- a/pkg/index/index.go
+++ b/pkg/index/index.go
@@ -24,6 +24,7 @@ import (
        "github.com/pkg/errors"
 
        "github.com/apache/skywalking-banyandb/api/common"
+       databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
        modelv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
        "github.com/apache/skywalking-banyandb/banyand/observability"
        "github.com/apache/skywalking-banyandb/pkg/convert"
@@ -36,6 +37,7 @@ type FieldKey struct {
        SeriesID    common.SeriesID
        IndexRuleID uint32
        EncodeTerm  bool
+       Analyzer    databasev1.IndexRule_Analyzer
 }
 
 func (f FieldKey) Marshal() []byte {
@@ -153,6 +155,7 @@ type FieldIterable interface {
 
 type Searcher interface {
        FieldIterable
+       Match(fieldKey FieldKey, match []string) (list posting.List, err error)
        MatchField(fieldKey FieldKey) (list posting.List, err error)
        MatchTerms(field Field) (list posting.List, err error)
        Range(fieldKey FieldKey, opts RangeOpts) (list posting.List, err error)
diff --git a/pkg/index/inverted/inverted.go b/pkg/index/inverted/inverted.go
index d3c7e75..8d1e27c 100644
--- a/pkg/index/inverted/inverted.go
+++ b/pkg/index/inverted/inverted.go
@@ -25,12 +25,14 @@ import (
        "math"
 
        "github.com/blugelabs/bluge"
+       "github.com/blugelabs/bluge/analysis"
        "github.com/blugelabs/bluge/analysis/analyzer"
        "github.com/blugelabs/bluge/search"
        "github.com/dgraph-io/badger/v3/y"
        "go.uber.org/multierr"
 
        "github.com/apache/skywalking-banyandb/api/common"
+       databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
        modelv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
        "github.com/apache/skywalking-banyandb/banyand/observability"
        "github.com/apache/skywalking-banyandb/pkg/convert"
@@ -42,7 +44,15 @@ import (
 
 const docID = "_id"
 
-var defaultAnalyzer = analyzer.NewKeywordAnalyzer()
+var analyzers map[databasev1.IndexRule_Analyzer]*analysis.Analyzer
+
+func init() {
+       analyzers = map[databasev1.IndexRule_Analyzer]*analysis.Analyzer{
+               databasev1.IndexRule_ANALYZER_KEYWORD:  
analyzer.NewKeywordAnalyzer(),
+               databasev1.IndexRule_ANALYZER_SIMPLE:   
analyzer.NewSimpleAnalyzer(),
+               databasev1.IndexRule_ANALYZER_STANDARD: 
analyzer.NewStandardAnalyzer(),
+       }
+}
 
 var _ index.Store = (*store)(nil)
 
@@ -57,7 +67,7 @@ type store struct {
 
 func NewStore(opts StoreOpts) (index.Store, error) {
        config := bluge.DefaultConfig(opts.Path)
-       config.DefaultSearchAnalyzer = defaultAnalyzer
+       config.DefaultSearchAnalyzer = 
analyzers[databasev1.IndexRule_ANALYZER_KEYWORD]
        config.Logger = log.New(opts.Logger, opts.Logger.Module(), 0)
        w, err := bluge.OpenWriter(config)
        if err != nil {
@@ -79,7 +89,11 @@ func (s *store) Close() error {
 func (s *store) Write(fields []index.Field, itemID common.ItemID) error {
        doc := bluge.NewDocument(string(convert.Uint64ToBytes(uint64(itemID))))
        for _, f := range fields {
-               doc.AddField(bluge.NewKeywordFieldBytes(f.Key.MarshalToStr(), 
f.Term).StoreValue().Sortable())
+               field := bluge.NewKeywordFieldBytes(f.Key.MarshalToStr(), 
f.Term).StoreValue().Sortable()
+               if f.Key.Analyzer != databasev1.IndexRule_ANALYZER_UNSPECIFIED {
+                       field.WithAnalyzer(analyzers[f.Key.Analyzer])
+               }
+               doc.AddField(field)
        }
        return s.writer.Insert(doc)
 }
@@ -139,6 +153,45 @@ func (s *store) MatchTerms(field index.Field) (list 
posting.List, err error) {
        return iter.Val().Value, nil
 }
 
+func (s *store) Match(fieldKey index.FieldKey, matches []string) 
(posting.List, error) {
+       if len(matches) == 0 {
+               return roaring.EmptyPostingList, nil
+       }
+       reader, err := s.writer.Reader()
+       if err != nil {
+               return nil, err
+       }
+       fk := fieldKey.MarshalToStr()
+       var query bluge.Query
+       getMatchQuery := func(match string) bluge.Query {
+               q := bluge.NewMatchQuery(match).SetField(fk)
+               if fieldKey.Analyzer != 
databasev1.IndexRule_ANALYZER_UNSPECIFIED {
+                       q.SetAnalyzer(analyzers[fieldKey.Analyzer])
+               }
+               return q
+       }
+       if len(matches) == 1 {
+               query = getMatchQuery(matches[0])
+       } else {
+               bq := bluge.NewBooleanQuery()
+               for _, m := range matches {
+                       bq.AddMust(getMatchQuery(m))
+               }
+               query = bq
+       }
+       documentMatchIterator, err := reader.Search(context.Background(), 
bluge.NewAllMatches(query))
+       if err != nil {
+               return nil, err
+       }
+       iter := newBlugeMatchIterator(documentMatchIterator, fk)
+       list := roaring.NewPostingList()
+       for iter.Next() {
+               err = multierr.Append(err, list.Union(iter.Val().Value))
+       }
+       err = multierr.Append(err, iter.Close())
+       return list, err
+}
+
 func (s *store) Range(fieldKey index.FieldKey, opts index.RangeOpts) (list 
posting.List, err error) {
        iter, err := s.Iterator(fieldKey, opts, modelv1.Sort_SORT_ASC)
        if err != nil {
diff --git a/pkg/index/inverted/inverted_test.go 
b/pkg/index/inverted/inverted_test.go
index b329322..cfa1dcb 100644
--- a/pkg/index/inverted/inverted_test.go
+++ b/pkg/index/inverted/inverted_test.go
@@ -18,16 +18,127 @@
 package inverted
 
 import (
+       "strings"
        "testing"
 
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
 
+       "github.com/apache/skywalking-banyandb/api/common"
+       databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+       "github.com/apache/skywalking-banyandb/pkg/index"
+       "github.com/apache/skywalking-banyandb/pkg/index/posting"
+       "github.com/apache/skywalking-banyandb/pkg/index/posting/roaring"
        "github.com/apache/skywalking-banyandb/pkg/index/testcases"
        "github.com/apache/skywalking-banyandb/pkg/logger"
        "github.com/apache/skywalking-banyandb/pkg/test"
 )
 
+var serviceName = index.FieldKey{
+       // http_method
+       IndexRuleID: 6,
+       EncodeTerm:  false,
+       Analyzer:    databasev1.IndexRule_ANALYZER_SIMPLE,
+}
+
+func TestStore_Match(t *testing.T) {
+       tester := assert.New(t)
+       path, fn := setUp(require.New(t))
+       s, err := NewStore(StoreOpts{
+               Path:   path,
+               Logger: logger.GetLogger("test"),
+       })
+       defer func() {
+               tester.NoError(s.Close())
+               fn()
+       }()
+       tester.NoError(err)
+       tester.NoError(s.Write([]index.Field{{
+               Key:  serviceName,
+               Term: []byte("GET::/product/order"),
+       }}, common.ItemID(1)))
+       tester.NoError(s.Write([]index.Field{{
+               Key:  serviceName,
+               Term: []byte("GET::/root/product"),
+       }}, common.ItemID(2)))
+       tester.NoError(s.Write([]index.Field{{
+               Key:  serviceName,
+               Term: 
[]byte("org.apache.skywalking.examples.OrderService.order"),
+       }}, common.ItemID(3)))
+
+       tests := []struct {
+               matches []string
+               want    posting.List
+               wantErr bool
+       }{
+               {
+                       matches: []string{"root"},
+                       want:    roaring.NewPostingListWithInitialData(2),
+               },
+               {
+                       matches: []string{"product"},
+                       want:    roaring.NewPostingListWithInitialData(1, 2),
+               },
+               {
+                       matches: []string{"order"},
+                       want:    roaring.NewPostingListWithInitialData(1, 3),
+               },
+               {
+                       matches: []string{"/root/product"},
+                       want:    roaring.NewPostingListWithInitialData(1, 2),
+               },
+               {
+                       matches: []string{"/product/order"},
+                       want:    roaring.NewPostingListWithInitialData(1, 2, 3),
+               },
+               {
+                       matches: []string{"GET"},
+                       want:    roaring.NewPostingListWithInitialData(1, 2),
+               },
+               {
+                       matches: []string{"GET::/root"},
+                       want:    roaring.NewPostingListWithInitialData(1, 2),
+               },
+               {
+                       matches: []string{"org"},
+                       want:    roaring.NewPostingListWithInitialData(3),
+               },
+               {
+                       matches: []string{"org.apache"},
+                       want:    roaring.NewPostingListWithInitialData(3),
+               },
+               {
+                       matches: []string{"org.apache....OrderService"},
+                       want:    roaring.NewPostingListWithInitialData(3),
+               },
+               {
+                       matches: []string{"OrderService.order"},
+                       want:    roaring.NewPostingListWithInitialData(1, 3),
+               },
+               {
+                       matches: []string{"root", "product"},
+                       want:    roaring.NewPostingListWithInitialData(2),
+               },
+               {
+                       matches: []string{"OrderService", "order"},
+                       want:    roaring.NewPostingListWithInitialData(3),
+               },
+       }
+       for _, tt := range tests {
+               name := strings.Join(tt.matches, " and ")
+               t.Run(name, func(t *testing.T) {
+                       list, err := s.Match(serviceName, tt.matches)
+                       if tt.wantErr {
+                               tester.Error(err)
+                               return
+                       }
+                       tester.NoError(err, name)
+                       tester.NotNil(list, name)
+                       tester.Equal(tt.want, list, name)
+               })
+       }
+}
+
 func TestStore_MatchTerm(t *testing.T) {
        tester := assert.New(t)
        path, fn := setUp(require.New(t))
diff --git a/pkg/index/lsm/search.go b/pkg/index/lsm/search.go
index 5e877fa..251dfdd 100644
--- a/pkg/index/lsm/search.go
+++ b/pkg/index/lsm/search.go
@@ -32,6 +32,8 @@ import (
        "github.com/apache/skywalking-banyandb/pkg/index/posting/roaring"
 )
 
+var ErrUnsupportedOperation = errors.New("unsupported operation")
+
 func (s *store) MatchField(fieldKey index.FieldKey) (list posting.List, err 
error) {
        return s.Range(fieldKey, index.RangeOpts{})
 }
@@ -89,3 +91,7 @@ func (s *store) Iterator(fieldKey index.FieldKey, termRange 
index.RangeOpts, ord
                        return pv, nil
                })
 }
+
+func (s *store) Match(_ index.FieldKey, _ []string) (posting.List, error) {
+       return nil, errors.WithMessage(ErrUnsupportedOperation, "LSM-Tree index 
doesn't support full-text searching")
+}
diff --git a/pkg/index/testcases/duration.go b/pkg/index/testcases/duration.go
index c065d96..df79010 100644
--- a/pkg/index/testcases/duration.go
+++ b/pkg/index/testcases/duration.go
@@ -292,7 +292,7 @@ func RunDuration(t *testing.T, data map[int]posting.List, 
store SimpleStore) {
                                        items: toArray(data[w]),
                                })
                        }
-                       tester.Equal(wants, got)
+                       tester.Equal(wants, got, tt.name)
                })
        }
 }
diff --git a/pkg/index/tree.go b/pkg/index/tree.go
index dd39f64..7718643 100644
--- a/pkg/index/tree.go
+++ b/pkg/index/tree.go
@@ -85,6 +85,8 @@ func BuildTree(searcher Searcher, condMap Condition) (Tree, 
error) {
                        switch cond.Op {
                        case modelv1.Condition_BINARY_OP_EQ:
                                root.addEq(key, cond.Values)
+                       case modelv1.Condition_BINARY_OP_MATCH:
+                               root.addMatch(key, cond.Values)
                        case modelv1.Condition_BINARY_OP_NE:
                                root.addNot(key, root.newEq(key, cond.Values))
                        case modelv1.Condition_BINARY_OP_HAVING:
@@ -136,6 +138,20 @@ func (n *node) newEq(key FieldKey, values [][]byte) *eq {
        }
 }
 
+func (n *node) newMatch(key FieldKey, values [][]byte) *match {
+       return &match{
+               leaf: &leaf{
+                       Key:      key,
+                       Values:   values,
+                       searcher: n.searcher,
+               },
+       }
+}
+
+func (n *node) addMatch(key FieldKey, values [][]byte) {
+       n.SubNodes = append(n.SubNodes, n.newMatch(key, values))
+}
+
 func (n *node) addEq(key FieldKey, values [][]byte) {
        n.SubNodes = append(n.SubNodes, n.newEq(key, values))
 }
@@ -295,6 +311,27 @@ func (eq *eq) MarshalJSON() ([]byte, error) {
        return json.Marshal(data)
 }
 
+type match struct {
+       *leaf
+}
+
+func (match *match) Execute() (posting.List, error) {
+       matches := make([]string, len(match.Values))
+       for i, v := range match.Values {
+               matches[i] = string(v)
+       }
+       return match.searcher.Match(
+               match.Key,
+               matches,
+       )
+}
+
+func (match *match) MarshalJSON() ([]byte, error) {
+       data := make(map[string]interface{}, 1)
+       data["match"] = match.leaf
+       return json.Marshal(data)
+}
+
 type rangeOp struct {
        *leaf
        Opts *RangeOpts
diff --git a/pkg/pb/v1/query.go b/pkg/pb/v1/query.go
index b3e085b..981dd68 100644
--- a/pkg/pb/v1/query.go
+++ b/pkg/pb/v1/query.go
@@ -38,6 +38,7 @@ var binaryOpsMap = map[string]modelv1.Condition_BinaryOp{
        "<=":         modelv1.Condition_BINARY_OP_LE,
        "having":     modelv1.Condition_BINARY_OP_HAVING,
        "not having": modelv1.Condition_BINARY_OP_NOT_HAVING,
+       "match":      modelv1.Condition_BINARY_OP_MATCH,
 }
 
 type StreamQueryRequestBuilder struct {
diff --git a/pkg/query/logical/common_test.go b/pkg/query/logical/common_test.go
index 0bc72fb..fac02c1 100644
--- a/pkg/query/logical/common_test.go
+++ b/pkg/query/logical/common_test.go
@@ -144,7 +144,7 @@ func setup(t *require.Assertions) (stream.Stream, 
metadata.Service, func()) {
        }
 }
 
-func setupMeasure(t *require.Assertions) (measure.Measure, metadata.Service, 
func()) {
+func setupMeasure(t *require.Assertions, name string) (measure.Measure, 
metadata.Service, func()) {
        t.NoError(logger.Init(logger.Logging{
                Env:   "dev",
                Level: "warn",
@@ -188,7 +188,7 @@ func setupMeasure(t *require.Assertions) (measure.Measure, 
metadata.Service, fun
        t.NoError(err)
 
        m, err := measureSvc.Measure(&commonv1.Metadata{
-               Name:  "service_cpm_minute",
+               Name:  name,
                Group: "sw_metric",
        })
        t.NoError(err)
diff --git a/pkg/query/logical/expr.go b/pkg/query/logical/expr.go
index dccbd45..e17e228 100644
--- a/pkg/query/logical/expr.go
+++ b/pkg/query/logical/expr.go
@@ -34,6 +34,7 @@ var binaryOpFactory = map[modelv1.Condition_BinaryOp]func(l, 
r Expr) Expr{
        modelv1.Condition_BINARY_OP_GE:         Ge,
        modelv1.Condition_BINARY_OP_HAVING:     Having,
        modelv1.Condition_BINARY_OP_NOT_HAVING: NotHaving,
+       modelv1.Condition_BINARY_OP_MATCH:      Match,
 }
 
 var _ ResolvableExpr = (*TagRef)(nil)
@@ -187,6 +188,14 @@ func Eq(l, r Expr) Expr {
        }
 }
 
+func Match(l, r Expr) Expr {
+       return &binaryExpr{
+               op: modelv1.Condition_BINARY_OP_MATCH,
+               l:  l,
+               r:  r,
+       }
+}
+
 func Ne(l, r Expr) Expr {
        return &binaryExpr{
                op: modelv1.Condition_BINARY_OP_NE,
diff --git a/pkg/query/logical/measure_analyzer_test.go 
b/pkg/query/logical/measure_analyzer_test.go
index f3a547f..a06bf2f 100644
--- a/pkg/query/logical/measure_analyzer_test.go
+++ b/pkg/query/logical/measure_analyzer_test.go
@@ -343,3 +343,45 @@ func TestMeasureAnalyzer_Fields_IndexNotDefined(t 
*testing.T) {
        _, err = ana.Analyze(context.TODO(), criteria, metadata, schema)
        assert.ErrorIs(err, logical.ErrIndexNotDefined)
 }
+
+func TestMeasureAnalyzer_Searchable(t *testing.T) {
+       assert := require.New(t)
+
+       ana, stopFunc, err := setUpMeasureAnalyzer()
+       assert.NoError(err)
+       assert.NotNil(ana)
+       defer stopFunc()
+
+       sT, eT := time.Now().Add(-3*time.Hour), time.Now()
+
+       criteria := pb.NewMeasureQueryRequestBuilder().
+               Metadata("sw_metric", "service_instance_traffic").
+               TagProjection("default", "id", "name").
+               TagsInTagFamily("default", "name", "match", "abc").
+               TimeRange(sT, eT).
+               Build()
+
+       metadata := criteria.GetMetadata()
+
+       schema, err := ana.BuildMeasureSchema(context.TODO(), metadata)
+       assert.NoError(err)
+
+       plan, err := ana.Analyze(context.TODO(), criteria, metadata, schema)
+       assert.NoError(err)
+       assert.NotNil(plan)
+
+       correctPlan, err := logical.MeasureLimit(
+               logical.MeasureIndexScan(sT, eT, metadata,
+                       []logical.Expr{
+                               logical.Match(logical.NewTagRef("default", 
"name"), logical.Str("abc")),
+                       },
+                       tsdb.Entity{nil},
+                       [][]*logical.Tag{logical.NewTags("default", "id", 
"name")},
+                       nil,
+                       false,
+                       nil,
+               ), 0, logical.DefaultLimit).Analyze(schema)
+       assert.NoError(err)
+       assert.NotNil(correctPlan)
+       assert.True(cmp.Equal(plan, correctPlan), "plan is not equal to correct 
plan")
+}
diff --git a/pkg/query/logical/measure_plan_execution_test.go 
b/pkg/query/logical/measure_plan_execution_test.go
index 117382f..cda0665 100644
--- a/pkg/query/logical/measure_plan_execution_test.go
+++ b/pkg/query/logical/measure_plan_execution_test.go
@@ -34,7 +34,7 @@ import (
 
 func TestMeasurePlanExecution_IndexScan(t *testing.T) {
        tester := require.New(t)
-       measureSvc, metaService, deferFunc := setupMeasure(tester)
+       measureSvc, metaService, deferFunc := setupMeasure(tester, 
"service_cpm_minute")
        defer deferFunc()
        baseTs := setupMeasureQueryData(t, "measure_query_data.json", 
measureSvc)
 
@@ -127,7 +127,7 @@ func TestMeasurePlanExecution_IndexScan(t *testing.T) {
 
 func TestMeasurePlanExecution_GroupByAndIndexScan(t *testing.T) {
        tester := require.New(t)
-       measureSvc, metaService, deferFunc := setupMeasure(tester)
+       measureSvc, metaService, deferFunc := setupMeasure(tester, 
"service_cpm_minute")
        defer deferFunc()
        baseTs := setupMeasureQueryData(t, "measure_query_data.json", 
measureSvc)
 
@@ -218,7 +218,7 @@ func TestMeasurePlanExecution_GroupByAndIndexScan(t 
*testing.T) {
 
 func TestMeasurePlanExecution_Cursor(t *testing.T) {
        tester := require.New(t)
-       measureSvc, metaService, deferFunc := setupMeasure(tester)
+       measureSvc, metaService, deferFunc := setupMeasure(tester, 
"service_cpm_minute")
        defer deferFunc()
        baseTs := setupMeasureQueryData(t, "measure_top_data.json", measureSvc)
 
@@ -359,3 +359,85 @@ func TestMeasurePlanExecution_Cursor(t *testing.T) {
                })
        }
 }
+
+func TestMeasurePlanExecution_Searchable(t *testing.T) {
+       tester := require.New(t)
+       measureSvc, metaService, deferFunc := setupMeasure(tester, 
"service_instance_traffic")
+       defer deferFunc()
+       baseTs := setupMeasureQueryData(t, "measure_search_data.json", 
measureSvc)
+
+       metadata := &commonv1.Metadata{
+               Name:  "service_instance_traffic",
+               Group: "sw_metric",
+       }
+
+       sT, eT := baseTs, baseTs.Add(1*time.Hour)
+
+       analyzer, err := 
logical.CreateMeasureAnalyzerFromMetaService(metaService)
+       tester.NoError(err)
+       tester.NotNil(analyzer)
+
+       tests := []struct {
+               name           string
+               unresolvedPlan logical.UnresolvedPlan
+               wantLength     int
+               tagLength      []int
+               fieldLength    int
+       }{
+               {
+                       name: "search node a",
+                       unresolvedPlan: logical.MeasureIndexScan(sT, eT, 
metadata, []logical.Expr{
+                               logical.Match(logical.NewTagRef("default", 
"name"), logical.Str("nodea")),
+                       }, tsdb.Entity{tsdb.AnyEntry}, nil, nil, false, nil),
+                       wantLength: 2,
+               },
+               {
+                       name: "search nodes in us",
+                       unresolvedPlan: logical.MeasureIndexScan(sT, eT, 
metadata, []logical.Expr{
+                               logical.Match(logical.NewTagRef("default", 
"name"), logical.Str("us")),
+                       }, tsdb.Entity{tsdb.AnyEntry}, nil, nil, false, nil),
+                       wantLength: 2,
+               },
+               {
+                       name: "search nodes in cn",
+                       unresolvedPlan: logical.MeasureIndexScan(sT, eT, 
metadata, []logical.Expr{
+                               logical.Match(logical.NewTagRef("default", 
"name"), logical.Str("cn")),
+                       }, tsdb.Entity{tsdb.AnyEntry}, nil, nil, false, nil),
+                       wantLength: 1,
+               },
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       tester := require.New(t)
+                       schema, err := 
analyzer.BuildMeasureSchema(context.TODO(), metadata)
+                       tester.NoError(err)
+
+                       plan, err := tt.unresolvedPlan.Analyze(schema)
+                       tester.NoError(err)
+                       tester.NotNil(plan)
+
+                       iter, err := 
plan.(executor.MeasureExecutable).Execute(measureSvc)
+                       tester.NoError(err)
+                       defer func() {
+                               err = iter.Close()
+                               tester.NoError(err)
+                       }()
+                       dataSize := 0
+                       for iter.Next() {
+                               dataPoints := iter.Current()
+                               if len(dataPoints) < 1 {
+                                       continue
+                               }
+                               dp := dataPoints[0]
+                               dataSize++
+                               tester.Len(dp.GetFields(), tt.fieldLength)
+                               tester.Len(dp.GetTagFamilies(), 
len(tt.tagLength))
+                               for tagFamilyIdx, tagFamily := range 
dp.GetTagFamilies() {
+                                       tester.Len(tagFamily.GetTags(), 
tt.tagLength[tagFamilyIdx])
+                               }
+                       }
+                       tester.Equal(dataSize, tt.wantLength)
+               })
+       }
+}
diff --git a/pkg/query/logical/stream_analyzer_test.go 
b/pkg/query/logical/stream_analyzer_test.go
index 91b2b8e..9876468 100644
--- a/pkg/query/logical/stream_analyzer_test.go
+++ b/pkg/query/logical/stream_analyzer_test.go
@@ -144,7 +144,7 @@ func TestStreamAnalyzer_ComplexQuery(t *testing.T) {
                OrderBy("duration", modelv1.Sort_SORT_DESC).
                Metadata("default", "sw").
                Projection("searchable", "http.method", "service_id", 
"duration").
-               TagsInTagFamily("searchable", "service_id", "=", "my_app", 
"http.method", "=", "GET", "mq.topic", "=", "event_topic").
+               TagsInTagFamily("searchable", "service_id", "=", "my_app", 
"http.method", "match", "GET", "mq.topic", "=", "event_topic").
                TimeRange(sT, eT).
                Build()
 
@@ -162,7 +162,7 @@ func TestStreamAnalyzer_ComplexQuery(t *testing.T) {
                        logical.TagFilter(sT, eT, metadata,
                                []logical.Expr{
                                        
logical.Eq(logical.NewSearchableTagRef("mq.topic"), logical.Str("event_topic")),
-                                       
logical.Eq(logical.NewSearchableTagRef("http.method"), logical.Str("GET")),
+                                       
logical.Match(logical.NewSearchableTagRef("http.method"), logical.Str("GET")),
                                }, tsdb.Entity{tsdb.Entry("my_app"), 
tsdb.AnyEntry, tsdb.AnyEntry},
                                logical.OrderBy("duration", 
modelv1.Sort_SORT_DESC),
                                logical.NewTags("searchable", "http.method", 
"service_id", "duration")),
diff --git a/pkg/query/logical/stream_plan_execution_test.go 
b/pkg/query/logical/stream_plan_execution_test.go
index d336e02..740855e 100644
--- a/pkg/query/logical/stream_plan_execution_test.go
+++ b/pkg/query/logical/stream_plan_execution_test.go
@@ -293,6 +293,13 @@ func TestPlanExecution_IndexScan(t *testing.T) {
                        }, tsdb.Entity{tsdb.AnyEntry, tsdb.AnyEntry, 
tsdb.AnyEntry}, nil),
                        wantLength: 0,
                },
+               {
+                       name: "Full text searching",
+                       unresolvedPlan: logical.TagFilter(sT, eT, metadata, 
[]logical.Expr{
+                               logical.Match(logical.NewTagRef("searchable", 
"db.instance"), logical.Str("mysql")),
+                       }, tsdb.Entity{tsdb.AnyEntry, tsdb.AnyEntry, 
tsdb.AnyEntry}, nil),
+                       wantLength: 2,
+               },
        }
 
        for _, tt := range tests {
diff --git a/pkg/query/logical/testdata/measure_search_data.json 
b/pkg/query/logical/testdata/measure_search_data.json
new file mode 100644
index 0000000..03fbafa
--- /dev/null
+++ b/pkg/query/logical/testdata/measure_search_data.json
@@ -0,0 +1,102 @@
+[
+  {
+    "tag_families": [
+      {
+        "tags": [
+          {
+            "id": {
+              "value": "1"
+            }
+          },
+          {
+            "str": {
+              "value": "svc_1"
+            }
+          },
+          {
+            "str": {
+              "value": "nodea@west-us"
+            }
+          },
+          {
+            "int": {
+              "value": 100
+            }
+          },
+          {
+            "int": {
+              "value": 0
+            }
+          }
+        ]
+      }
+    ]
+  },
+  {
+    "tag_families": [
+      {
+        "tags": [
+          {
+            "id": {
+              "value": "2"
+            }
+          },
+          {
+            "str": {
+              "value": "svc_1"
+            }
+          },
+          {
+            "str": {
+              "value": "nodeb@west-us"
+            }
+          },
+          {
+            "int": {
+              "value": 100
+            }
+          },
+          {
+            "int": {
+              "value": 0
+            }
+          }
+        ]
+      }
+    ]
+  },
+  {
+    "tag_families": [
+      {
+        "tags": [
+          {
+            "id": {
+              "value": "3"
+            }
+          },
+          {
+            "str": {
+              "value": "svc_1"
+            }
+          },
+          {
+            "str": {
+              "value": "nodea@east-cn"
+            }
+          },
+          {
+            "int": {
+              "value": 100
+            }
+          },
+          {
+            "int": {
+              "value": 0
+            }
+          }
+        ]
+      }
+    ]
+  }
+  
+]
diff --git a/pkg/query/logical/testdata/multiple_shards.json 
b/pkg/query/logical/testdata/multiple_shards.json
index 402964d..c211129 100644
--- a/pkg/query/logical/testdata/multiple_shards.json
+++ b/pkg/query/logical/testdata/multiple_shards.json
@@ -18,7 +18,12 @@
       {"str":{"value": "10.0.0.3_id"}},
       {"str":{"value": "/product_id"}},
       {"int":{"value": 500}},
-      {"int":{"value": 1622933202000000000}}
+      {"int":{"value": 1622933202000000000}},
+      {"null":0},
+      {"null":0},
+      {"null":0},
+      {"str":{"value":"mysql"}},
+      {"str":{"value":"jdbc:mysql://localhost:3306/bar"}}
     ]
   },
   {
@@ -31,7 +36,10 @@
       {"int":{"value": 30}},
       {"int":{"value": 1622933202000000000}},
       {"str":{"value": "GET"}},
-      {"int":{"value": 500}}
+      {"int":{"value": 500}},
+      {"null":0},
+      {"str":{"value":"mysql"}},
+      {"str":{"value":"jdbc:mysql://test:3306/bar"}}
     ]
   },
   {
@@ -44,7 +52,10 @@
       {"int":{"value": 60}},
       {"int":{"value": 1622933202000000000}},
       {"str":{"value": "GET"}},
-      {"int":{"value": 400}}
+      {"int":{"value": 400}},
+      {"null":0},
+      {"str":{"value":"postgresql"}},
+      {"str":{"value":"jdbc:postgresql://test:5432/bar"}}
     ]
   },
   {
diff --git 
a/pkg/test/measure/testdata/index_rule_bindings/service_instance_traffic.json 
b/pkg/test/measure/testdata/index_rule_bindings/service_instance_traffic.json
index 35b49eb..249c1f8 100644
--- 
a/pkg/test/measure/testdata/index_rule_bindings/service_instance_traffic.json
+++ 
b/pkg/test/measure/testdata/index_rule_bindings/service_instance_traffic.json
@@ -4,7 +4,8 @@
     "group": "sw_metric"
   },
   "rules": [
-    "service_id"
+    "service_id",
+    "searchable_name"
   ],
   "subject":{
     "catalog": "CATALOG_MEASURE",
diff --git a/pkg/test/measure/testdata/index_rules/searchable_name.json 
b/pkg/test/measure/testdata/index_rules/searchable_name.json
new file mode 100644
index 0000000..80229bf
--- /dev/null
+++ b/pkg/test/measure/testdata/index_rules/searchable_name.json
@@ -0,0 +1,14 @@
+{
+       "metadata": {
+               "id": 2,
+               "name": "searchable_name",
+               "group": "sw_metric"
+       },
+       "tags": [
+               "name"
+       ],
+       "type": "TYPE_INVERTED",
+       "location": "LOCATION_SERIES",
+  "analyzer": "ANALYZER_SIMPLE",
+       "updated_at": "2021-04-15T01:30:15.01Z"
+}
\ No newline at end of file
diff --git a/pkg/test/stream/testdata/index_rules/db.instance.json 
b/pkg/test/stream/testdata/index_rules/db.instance.json
index d6bcbb1..830408e 100644
--- a/pkg/test/stream/testdata/index_rules/db.instance.json
+++ b/pkg/test/stream/testdata/index_rules/db.instance.json
@@ -9,5 +9,6 @@
   ],
   "type": "TYPE_INVERTED",
   "location": "LOCATION_SERIES",
+  "analyzer": "ANALYZER_SIMPLE",
   "updated_at": "2021-04-15T01:30:15.01Z"
 }

Reply via email to