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

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

commit 7edb0a42845ce1065313c688d23d9491dd736f29
Author: Gao Hongtao <[email protected]>
AuthorDate: Wed Oct 5 07:30:17 2022 +0000

    Refactor Property
    
    * Merge Create and Update to Apply
    * Add tag selector to Get
    * Add id and tag selector to List
    
    Signed-off-by: Gao Hongtao <[email protected]>
---
 api/proto/banyandb/property/v1/rpc.pb.go           | 555 +++++++++++----------
 api/proto/banyandb/property/v1/rpc.pb.gw.go        | 219 ++++----
 api/proto/banyandb/property/v1/rpc.pb.validate.go  | 347 +++----------
 api/proto/banyandb/property/v1/rpc.proto           |  42 +-
 api/proto/banyandb/property/v1/rpc_grpc.pb.go      |  66 +--
 .../openapi/banyandb/property/v1/rpc.swagger.json  | 119 +++--
 banyand/liaison/grpc/property.go                   |  21 +-
 banyand/metadata/schema/property.go                | 123 ++++-
 banyand/metadata/schema/schema.go                  |   9 +-
 docs/api-reference.md                              |  59 ++-
 test/integration/other/property_test.go            | 146 ++++++
 11 files changed, 846 insertions(+), 860 deletions(-)

diff --git a/api/proto/banyandb/property/v1/rpc.pb.go 
b/api/proto/banyandb/property/v1/rpc.pb.go
index b33ea55..5a330e9 100644
--- a/api/proto/banyandb/property/v1/rpc.pb.go
+++ b/api/proto/banyandb/property/v1/rpc.pb.go
@@ -41,116 +41,82 @@ const (
        _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
-type CreateRequest struct {
-       state         protoimpl.MessageState
-       sizeCache     protoimpl.SizeCache
-       unknownFields protoimpl.UnknownFields
+type ApplyRequest_Strategy int32
 
-       Property *Property `protobuf:"bytes,1,opt,name=property,proto3" 
json:"property,omitempty"`
-}
+const (
+       ApplyRequest_STRATEGY_UNSPECIFIED ApplyRequest_Strategy = 0
+       ApplyRequest_STRATEGY_MERGE       ApplyRequest_Strategy = 1
+       ApplyRequest_STRATEGY_REPLACE     ApplyRequest_Strategy = 2
+)
 
-func (x *CreateRequest) Reset() {
-       *x = CreateRequest{}
-       if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[0]
-               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-               ms.StoreMessageInfo(mi)
+// Enum value maps for ApplyRequest_Strategy.
+var (
+       ApplyRequest_Strategy_name = map[int32]string{
+               0: "STRATEGY_UNSPECIFIED",
+               1: "STRATEGY_MERGE",
+               2: "STRATEGY_REPLACE",
        }
-}
-
-func (x *CreateRequest) String() string {
-       return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CreateRequest) ProtoMessage() {}
-
-func (x *CreateRequest) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[0]
-       if protoimpl.UnsafeEnabled && x != nil {
-               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-               if ms.LoadMessageInfo() == nil {
-                       ms.StoreMessageInfo(mi)
-               }
-               return ms
+       ApplyRequest_Strategy_value = map[string]int32{
+               "STRATEGY_UNSPECIFIED": 0,
+               "STRATEGY_MERGE":       1,
+               "STRATEGY_REPLACE":     2,
        }
-       return mi.MessageOf(x)
-}
+)
 
-// Deprecated: Use CreateRequest.ProtoReflect.Descriptor instead.
-func (*CreateRequest) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{0}
+func (x ApplyRequest_Strategy) Enum() *ApplyRequest_Strategy {
+       p := new(ApplyRequest_Strategy)
+       *p = x
+       return p
 }
 
-func (x *CreateRequest) GetProperty() *Property {
-       if x != nil {
-               return x.Property
-       }
-       return nil
-}
-
-type CreateResponse struct {
-       state         protoimpl.MessageState
-       sizeCache     protoimpl.SizeCache
-       unknownFields protoimpl.UnknownFields
+func (x ApplyRequest_Strategy) String() string {
+       return protoimpl.X.EnumStringOf(x.Descriptor(), 
protoreflect.EnumNumber(x))
 }
 
-func (x *CreateResponse) Reset() {
-       *x = CreateResponse{}
-       if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[1]
-               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-               ms.StoreMessageInfo(mi)
-       }
+func (ApplyRequest_Strategy) Descriptor() protoreflect.EnumDescriptor {
+       return file_banyandb_property_v1_rpc_proto_enumTypes[0].Descriptor()
 }
 
-func (x *CreateResponse) String() string {
-       return protoimpl.X.MessageStringOf(x)
+func (ApplyRequest_Strategy) Type() protoreflect.EnumType {
+       return &file_banyandb_property_v1_rpc_proto_enumTypes[0]
 }
 
-func (*CreateResponse) ProtoMessage() {}
-
-func (x *CreateResponse) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[1]
-       if protoimpl.UnsafeEnabled && x != nil {
-               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-               if ms.LoadMessageInfo() == nil {
-                       ms.StoreMessageInfo(mi)
-               }
-               return ms
-       }
-       return mi.MessageOf(x)
+func (x ApplyRequest_Strategy) Number() protoreflect.EnumNumber {
+       return protoreflect.EnumNumber(x)
 }
 
-// Deprecated: Use CreateResponse.ProtoReflect.Descriptor instead.
-func (*CreateResponse) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{1}
+// Deprecated: Use ApplyRequest_Strategy.Descriptor instead.
+func (ApplyRequest_Strategy) EnumDescriptor() ([]byte, []int) {
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{0, 0}
 }
 
-type UpdateRequest struct {
+type ApplyRequest struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
 
        Property *Property `protobuf:"bytes,1,opt,name=property,proto3" 
json:"property,omitempty"`
+       // strategy indicates how to update a property. It defaults to 
STRATEGY_MERGE
+       Strategy ApplyRequest_Strategy 
`protobuf:"varint,2,opt,name=strategy,proto3,enum=banyandb.property.v1.ApplyRequest_Strategy"
 json:"strategy,omitempty"`
 }
 
-func (x *UpdateRequest) Reset() {
-       *x = UpdateRequest{}
+func (x *ApplyRequest) Reset() {
+       *x = ApplyRequest{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[2]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[0]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
 }
 
-func (x *UpdateRequest) String() string {
+func (x *ApplyRequest) String() string {
        return protoimpl.X.MessageStringOf(x)
 }
 
-func (*UpdateRequest) ProtoMessage() {}
+func (*ApplyRequest) ProtoMessage() {}
 
-func (x *UpdateRequest) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[2]
+func (x *ApplyRequest) ProtoReflect() protoreflect.Message {
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[0]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -161,41 +127,53 @@ func (x *UpdateRequest) ProtoReflect() 
protoreflect.Message {
        return mi.MessageOf(x)
 }
 
-// Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead.
-func (*UpdateRequest) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{2}
+// Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead.
+func (*ApplyRequest) Descriptor() ([]byte, []int) {
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{0}
 }
 
-func (x *UpdateRequest) GetProperty() *Property {
+func (x *ApplyRequest) GetProperty() *Property {
        if x != nil {
                return x.Property
        }
        return nil
 }
 
-type UpdateResponse struct {
+func (x *ApplyRequest) GetStrategy() ApplyRequest_Strategy {
+       if x != nil {
+               return x.Strategy
+       }
+       return ApplyRequest_STRATEGY_UNSPECIFIED
+}
+
+type ApplyResponse struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
+
+       // created indicates whether the property existed.
+       // True: the property is absent. False: the property existed.
+       Created bool   `protobuf:"varint,1,opt,name=created,proto3" 
json:"created,omitempty"`
+       TagsNum uint32 
`protobuf:"varint,2,opt,name=tags_num,json=tagsNum,proto3" 
json:"tags_num,omitempty"`
 }
 
-func (x *UpdateResponse) Reset() {
-       *x = UpdateResponse{}
+func (x *ApplyResponse) Reset() {
+       *x = ApplyResponse{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[3]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[1]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
 }
 
-func (x *UpdateResponse) String() string {
+func (x *ApplyResponse) String() string {
        return protoimpl.X.MessageStringOf(x)
 }
 
-func (*UpdateResponse) ProtoMessage() {}
+func (*ApplyResponse) ProtoMessage() {}
 
-func (x *UpdateResponse) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[3]
+func (x *ApplyResponse) ProtoReflect() protoreflect.Message {
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[1]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -206,9 +184,23 @@ func (x *UpdateResponse) ProtoReflect() 
protoreflect.Message {
        return mi.MessageOf(x)
 }
 
-// Deprecated: Use UpdateResponse.ProtoReflect.Descriptor instead.
-func (*UpdateResponse) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{3}
+// Deprecated: Use ApplyResponse.ProtoReflect.Descriptor instead.
+func (*ApplyResponse) Descriptor() ([]byte, []int) {
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ApplyResponse) GetCreated() bool {
+       if x != nil {
+               return x.Created
+       }
+       return false
+}
+
+func (x *ApplyResponse) GetTagsNum() uint32 {
+       if x != nil {
+               return x.TagsNum
+       }
+       return 0
 }
 
 type DeleteRequest struct {
@@ -217,12 +209,13 @@ type DeleteRequest struct {
        unknownFields protoimpl.UnknownFields
 
        Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" 
json:"metadata,omitempty"`
+       Tags     []string  `protobuf:"bytes,2,rep,name=tags,proto3" 
json:"tags,omitempty"`
 }
 
 func (x *DeleteRequest) Reset() {
        *x = DeleteRequest{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[4]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[2]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
@@ -235,7 +228,7 @@ func (x *DeleteRequest) String() string {
 func (*DeleteRequest) ProtoMessage() {}
 
 func (x *DeleteRequest) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[4]
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[2]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -248,7 +241,7 @@ func (x *DeleteRequest) ProtoReflect() protoreflect.Message 
{
 
 // Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead.
 func (*DeleteRequest) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{4}
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *DeleteRequest) GetMetadata() *Metadata {
@@ -258,18 +251,26 @@ func (x *DeleteRequest) GetMetadata() *Metadata {
        return nil
 }
 
+func (x *DeleteRequest) GetTags() []string {
+       if x != nil {
+               return x.Tags
+       }
+       return nil
+}
+
 type DeleteResponse struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
 
-       Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" 
json:"deleted,omitempty"`
+       Deleted bool   `protobuf:"varint,1,opt,name=deleted,proto3" 
json:"deleted,omitempty"`
+       TagsNum uint32 
`protobuf:"varint,2,opt,name=tags_num,json=tagsNum,proto3" 
json:"tags_num,omitempty"`
 }
 
 func (x *DeleteResponse) Reset() {
        *x = DeleteResponse{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[5]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[3]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
@@ -282,7 +283,7 @@ func (x *DeleteResponse) String() string {
 func (*DeleteResponse) ProtoMessage() {}
 
 func (x *DeleteResponse) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[5]
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[3]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -295,7 +296,7 @@ func (x *DeleteResponse) ProtoReflect() 
protoreflect.Message {
 
 // Deprecated: Use DeleteResponse.ProtoReflect.Descriptor instead.
 func (*DeleteResponse) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{5}
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *DeleteResponse) GetDeleted() bool {
@@ -305,18 +306,26 @@ func (x *DeleteResponse) GetDeleted() bool {
        return false
 }
 
+func (x *DeleteResponse) GetTagsNum() uint32 {
+       if x != nil {
+               return x.TagsNum
+       }
+       return 0
+}
+
 type GetRequest struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
        unknownFields protoimpl.UnknownFields
 
        Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" 
json:"metadata,omitempty"`
+       Tags     []string  `protobuf:"bytes,2,rep,name=tags,proto3" 
json:"tags,omitempty"`
 }
 
 func (x *GetRequest) Reset() {
        *x = GetRequest{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[6]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[4]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
@@ -329,7 +338,7 @@ func (x *GetRequest) String() string {
 func (*GetRequest) ProtoMessage() {}
 
 func (x *GetRequest) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[6]
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[4]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -342,7 +351,7 @@ func (x *GetRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetRequest.ProtoReflect.Descriptor instead.
 func (*GetRequest) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{6}
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *GetRequest) GetMetadata() *Metadata {
@@ -352,6 +361,13 @@ func (x *GetRequest) GetMetadata() *Metadata {
        return nil
 }
 
+func (x *GetRequest) GetTags() []string {
+       if x != nil {
+               return x.Tags
+       }
+       return nil
+}
+
 type GetResponse struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
@@ -363,7 +379,7 @@ type GetResponse struct {
 func (x *GetResponse) Reset() {
        *x = GetResponse{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[7]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[5]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
@@ -376,7 +392,7 @@ func (x *GetResponse) String() string {
 func (*GetResponse) ProtoMessage() {}
 
 func (x *GetResponse) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[7]
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[5]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -389,7 +405,7 @@ func (x *GetResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetResponse.ProtoReflect.Descriptor instead.
 func (*GetResponse) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{7}
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *GetResponse) GetProperty() *Property {
@@ -405,12 +421,14 @@ type ListRequest struct {
        unknownFields protoimpl.UnknownFields
 
        Container *v1.Metadata `protobuf:"bytes,1,opt,name=container,proto3" 
json:"container,omitempty"`
+       Ids       []string     `protobuf:"bytes,2,rep,name=ids,proto3" 
json:"ids,omitempty"`
+       Tags      []string     `protobuf:"bytes,3,rep,name=tags,proto3" 
json:"tags,omitempty"`
 }
 
 func (x *ListRequest) Reset() {
        *x = ListRequest{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[8]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[6]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
@@ -423,7 +441,7 @@ func (x *ListRequest) String() string {
 func (*ListRequest) ProtoMessage() {}
 
 func (x *ListRequest) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[8]
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[6]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -436,7 +454,7 @@ func (x *ListRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListRequest.ProtoReflect.Descriptor instead.
 func (*ListRequest) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{8}
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *ListRequest) GetContainer() *v1.Metadata {
@@ -446,6 +464,20 @@ func (x *ListRequest) GetContainer() *v1.Metadata {
        return nil
 }
 
+func (x *ListRequest) GetIds() []string {
+       if x != nil {
+               return x.Ids
+       }
+       return nil
+}
+
+func (x *ListRequest) GetTags() []string {
+       if x != nil {
+               return x.Tags
+       }
+       return nil
+}
+
 type ListResponse struct {
        state         protoimpl.MessageState
        sizeCache     protoimpl.SizeCache
@@ -457,7 +489,7 @@ type ListResponse struct {
 func (x *ListResponse) Reset() {
        *x = ListResponse{}
        if protoimpl.UnsafeEnabled {
-               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[9]
+               mi := &file_banyandb_property_v1_rpc_proto_msgTypes[7]
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                ms.StoreMessageInfo(mi)
        }
@@ -470,7 +502,7 @@ func (x *ListResponse) String() string {
 func (*ListResponse) ProtoMessage() {}
 
 func (x *ListResponse) ProtoReflect() protoreflect.Message {
-       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[9]
+       mi := &file_banyandb_property_v1_rpc_proto_msgTypes[7]
        if protoimpl.UnsafeEnabled && x != nil {
                ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
                if ms.LoadMessageInfo() == nil {
@@ -483,7 +515,7 @@ func (x *ListResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListResponse.ProtoReflect.Descriptor instead.
 func (*ListResponse) Descriptor() ([]byte, []int) {
-       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{9}
+       return file_banyandb_property_v1_rpc_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *ListResponse) GetProperty() []*Property {
@@ -510,107 +542,115 @@ var file_banyandb_property_v1_rpc_proto_rawDesc = 
[]byte{
        0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 
0x67, 0x65, 0x6e, 0x2d,
        0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 
0x74, 0x69, 0x6f, 0x6e,
        0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 
0x73, 0x2e, 0x70, 0x72,
-       0x6f, 0x74, 0x6f, 0x22, 0x55, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 
0x65, 0x52, 0x65, 0x71,
+       0x6f, 0x74, 0x6f, 0x22, 0xed, 0x01, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 
0x79, 0x52, 0x65, 0x71,
        0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 
0x65, 0x72, 0x74, 0x79,
        0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
        0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 
0x31, 0x2e, 0x50, 0x72,
        0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 
0x01, 0x02, 0x10, 0x01,
-       0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x22, 0x10, 
0x0a, 0x0e, 0x43, 0x72,
-       0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 
0x22, 0x55, 0x0a, 0x0d,
-       0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 
0x74, 0x12, 0x44, 0x0a,
-       0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 
0x01, 0x28, 0x0b, 0x32,
-       0x1e, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 
0x72, 0x6f, 0x70, 0x65,
-       0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 
0x72, 0x74, 0x79, 0x42,
-       0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 
0x72, 0x6f, 0x70, 0x65,
-       0x72, 0x74, 0x79, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 
0x65, 0x52, 0x65, 0x73,
-       0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x55, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 
0x65, 0x74, 0x65, 0x52,
-       0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x6d, 0x65, 
0x74, 0x61, 0x64, 0x61,
-       0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x62, 
0x61, 0x6e, 0x79, 0x61,
-       0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2e, 0x76, 0x31, 0x2e,
-       0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 
0x05, 0x8a, 0x01, 0x02,
-       0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 
0x22, 0x2a, 0x0a, 0x0e,
-       0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 
0x73, 0x65, 0x12, 0x18,
-       0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 
0x01, 0x28, 0x08, 0x52,
-       0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0x52, 0x0a, 0x0a, 
0x47, 0x65, 0x74, 0x52,
-       0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x6d, 0x65, 
0x74, 0x61, 0x64, 0x61,
-       0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x62, 
0x61, 0x6e, 0x79, 0x61,
-       0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2e, 0x76, 0x31, 0x2e,
-       0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x08, 0xfa, 0x42, 
0x05, 0x8a, 0x01, 0x02,
-       0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 
0x22, 0x49, 0x0a, 0x0b,
-       0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 
0x3a, 0x0a, 0x08, 0x70,
-       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 
0x0b, 0x32, 0x1e, 0x2e,
-       0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 
0x70, 0x65, 0x72, 0x74,
-       0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 
0x79, 0x52, 0x08, 0x70,
-       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x22, 0x53, 0x0a, 0x0b, 0x4c, 
0x69, 0x73, 0x74, 0x52,
-       0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x09, 0x63, 0x6f, 
0x6e, 0x74, 0x61, 0x69,
-       0x6e, 0x65, 0x72, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 
0x8a, 0x01, 0x02, 0x10,
-       0x01, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 
0x22, 0x4a, 0x0a, 0x0c,
-       0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 
0x12, 0x3a, 0x0a, 0x08,
-       0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 
0x28, 0x0b, 0x32, 0x1e,
-       0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 
0x6f, 0x70, 0x65, 0x72,
-       0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 
0x74, 0x79, 0x52, 0x08,
-       0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x32, 0xb0, 0x06, 0x0a, 
0x0f, 0x50, 0x72, 0x6f,
-       0x70, 0x65, 0x72, 0x74, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 
0x12, 0x6c, 0x0a, 0x06,
-       0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x62, 0x61, 0x6e, 
0x79, 0x61, 0x6e, 0x64,
-       0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 
0x31, 0x2e, 0x43, 0x72,
-       0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 
0x24, 0x2e, 0x62, 0x61,
+       0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x12, 0x47, 
0x0a, 0x08, 0x73, 0x74,
+       0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 
0x32, 0x2b, 0x2e, 0x62,
+       0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 
0x65, 0x72, 0x74, 0x79,
+       0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 
0x75, 0x65, 0x73, 0x74,
+       0x2e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x08, 0x73, 
0x74, 0x72, 0x61, 0x74,
+       0x65, 0x67, 0x79, 0x22, 0x4e, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x61, 0x74, 
0x65, 0x67, 0x79, 0x12,
+       0x18, 0x0a, 0x14, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 
0x55, 0x4e, 0x53, 0x50,
+       0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 
0x0e, 0x53, 0x54, 0x52,
+       0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4d, 0x45, 0x52, 0x47, 0x45, 0x10, 
0x01, 0x12, 0x14, 0x0a,
+       0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, 0x45, 
0x50, 0x4c, 0x41, 0x43,
+       0x45, 0x10, 0x02, 0x22, 0x44, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 
0x52, 0x65, 0x73, 0x70,
+       0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 
0x74, 0x65, 0x64, 0x18,
+       0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 
0x65, 0x64, 0x12, 0x19,
+       0x0a, 0x08, 0x74, 0x61, 0x67, 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 
0x20, 0x01, 0x28, 0x0d,
+       0x52, 0x07, 0x74, 0x61, 0x67, 0x73, 0x4e, 0x75, 0x6d, 0x22, 0x69, 0x0a, 
0x0d, 0x44, 0x65, 0x6c,
+       0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 
0x0a, 0x08, 0x6d, 0x65,
+       0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 
0x32, 0x1e, 0x2e, 0x62,
+       0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 
0x65, 0x72, 0x74, 0x79,
+       0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 
0x42, 0x08, 0xfa, 0x42,
+       0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 
0x64, 0x61, 0x74, 0x61,
+       0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 
0x28, 0x09, 0x52, 0x04,
+       0x74, 0x61, 0x67, 0x73, 0x22, 0x45, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 
0x74, 0x65, 0x52, 0x65,
+       0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 
0x6c, 0x65, 0x74, 0x65,
+       0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 
0x65, 0x74, 0x65, 0x64,
+       0x12, 0x19, 0x0a, 0x08, 0x74, 0x61, 0x67, 0x73, 0x5f, 0x6e, 0x75, 0x6d, 
0x18, 0x02, 0x20, 0x01,
+       0x28, 0x0d, 0x52, 0x07, 0x74, 0x61, 0x67, 0x73, 0x4e, 0x75, 0x6d, 0x22, 
0x66, 0x0a, 0x0a, 0x47,
+       0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 
0x08, 0x6d, 0x65, 0x74,
+       0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 
0x1e, 0x2e, 0x62, 0x61,
+       0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 
0x72, 0x74, 0x79, 0x2e,
+       0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 
0x08, 0xfa, 0x42, 0x05,
+       0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 
0x61, 0x74, 0x61, 0x12,
+       0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 
0x09, 0x52, 0x04, 0x74,
+       0x61, 0x67, 0x73, 0x22, 0x49, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 
0x73, 0x70, 0x6f, 0x6e,
+       0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 
0x74, 0x79, 0x18, 0x01,
+       0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 
0x6e, 0x64, 0x62, 0x2e,
+       0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 
0x50, 0x72, 0x6f, 0x70,
+       0x65, 0x72, 0x74, 0x79, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 
0x74, 0x79, 0x22, 0x79,
+       0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 
0x74, 0x12, 0x44, 0x0a,
+       0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 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, 0x42, 0x08,
+       0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x63, 0x6f, 
0x6e, 0x74, 0x61, 0x69,
+       0x6e, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 
0x20, 0x03, 0x28, 0x09,
+       0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 
0x73, 0x18, 0x03, 0x20,
+       0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x4a, 0x0a, 
0x0c, 0x4c, 0x69, 0x73,
+       0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 
0x08, 0x70, 0x72, 0x6f,
+       0x70, 0x65, 0x72, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 
0x1e, 0x2e, 0x62, 0x61,
        0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 
0x72, 0x74, 0x79, 0x2e,
-       0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 
0x70, 0x6f, 0x6e, 0x73,
-       0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 
0x76, 0x31, 0x2f, 0x70,
-       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0xca, 
0x01, 0x0a, 0x06, 0x55,
-       0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x62, 0x61, 0x6e, 0x79, 
0x61, 0x6e, 0x64, 0x62,
-       0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 
0x2e, 0x55, 0x70, 0x64,
-       0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 
0x2e, 0x62, 0x61, 0x6e,
-       0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 
0x74, 0x79, 0x2e, 0x76,
-       0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 
0x6f, 0x6e, 0x73, 0x65,
-       0x22, 0x75, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x6f, 0x1a, 0x6a, 0x2f, 0x76, 
0x31, 0x2f, 0x70, 0x72,
-       0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x70, 
0x65, 0x72, 0x74, 0x79,
+       0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x52, 
0x08, 0x70, 0x72, 0x6f,
+       0x70, 0x65, 0x72, 0x74, 0x79, 0x32, 0xda, 0x05, 0x0a, 0x0f, 0x50, 0x72, 
0x6f, 0x70, 0x65, 0x72,
+       0x74, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xc7, 0x01, 
0x0a, 0x05, 0x41, 0x70,
+       0x70, 0x6c, 0x79, 0x12, 0x22, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 
0x64, 0x62, 0x2e, 0x70,
+       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x41, 
0x70, 0x70, 0x6c, 0x79,
+       0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x62, 0x61, 
0x6e, 0x79, 0x61, 0x6e,
+       0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 
0x76, 0x31, 0x2e, 0x41,
+       0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 
0x22, 0x75, 0x82, 0xd3,
+       0xe4, 0x93, 0x02, 0x6f, 0x1a, 0x6a, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 
0x6f, 0x70, 0x65, 0x72,
+       0x74, 0x79, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2e, 0x6d, 0x65, 0x74,
+       0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 
0x6e, 0x65, 0x72, 0x2e,
+       0x67, 0x72, 0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x70, 
0x65, 0x72, 0x74, 0x79,
        0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 
0x6e, 0x74, 0x61, 0x69,
-       0x6e, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 
0x70, 0x72, 0x6f, 0x70,
-       0x65, 0x72, 0x74, 0x79, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 
0x61, 0x2e, 0x63, 0x6f,
-       0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 
0x7d, 0x2f, 0x7b, 0x70,
-       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x6d, 0x65, 0x74, 0x61, 
0x64, 0x61, 0x74, 0x61,
-       0x2e, 0x69, 0x64, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0xac, 0x01, 0x0a, 0x06, 
0x44, 0x65, 0x6c, 0x65,
-       0x74, 0x65, 0x12, 0x23, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 
0x62, 0x2e, 0x70, 0x72,
-       0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 
0x6c, 0x65, 0x74, 0x65,
-       0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x62, 0x61, 
0x6e, 0x79, 0x61, 0x6e,
-       0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 
0x76, 0x31, 0x2e, 0x44,
-       0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 
0x65, 0x22, 0x57, 0x82,
-       0xd3, 0xe4, 0x93, 0x02, 0x51, 0x2a, 0x4f, 0x2f, 0x76, 0x31, 0x2f, 0x70, 
0x72, 0x6f, 0x70, 0x65,
-       0x72, 0x74, 0x79, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 
0x61, 0x2e, 0x63, 0x6f,
-       0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 
0x70, 0x7d, 0x2f, 0x7b,
-       0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 
0x74, 0x61, 0x69, 0x6e,
-       0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x7b, 0x6d, 0x65, 
0x74, 0x61, 0x64, 0x61,
-       0x74, 0x61, 0x2e, 0x69, 0x64, 0x7d, 0x12, 0xa3, 0x01, 0x0a, 0x03, 0x47, 
0x65, 0x74, 0x12, 0x20,
+       0x6e, 0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x7b, 0x70, 
0x72, 0x6f, 0x70, 0x65,
+       0x72, 0x74, 0x79, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 
0x2e, 0x69, 0x64, 0x7d,
+       0x3a, 0x01, 0x2a, 0x12, 0xb3, 0x01, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 
0x74, 0x65, 0x12, 0x23,
        0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 
0x6f, 0x70, 0x65, 0x72,
-       0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 
0x75, 0x65, 0x73, 0x74,
-       0x1a, 0x21, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 
0x70, 0x72, 0x6f, 0x70,
-       0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 
0x65, 0x73, 0x70, 0x6f,
-       0x6e, 0x73, 0x65, 0x22, 0x57, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x51, 0x12, 
0x4f, 0x2f, 0x76, 0x31,
-       0x2f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2f, 0x7b, 0x6d, 
0x65, 0x74, 0x61, 0x64,
-       0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 
0x72, 0x2e, 0x67, 0x72,
-       0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 
0x74, 0x61, 0x2e, 0x63,
-       0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 
0x65, 0x7d, 0x2f, 0x7b,
-       0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x69, 0x64, 0x7d, 
0x12, 0x8c, 0x01, 0x0a,
-       0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x21, 0x2e, 0x62, 0x61, 0x6e, 0x79, 
0x61, 0x6e, 0x64, 0x62,
-       0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 
0x2e, 0x4c, 0x69, 0x73,
-       0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x62, 
0x61, 0x6e, 0x79, 0x61,
-       0x6e, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2e, 0x76, 0x31, 0x2e,
-       0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 
0x22, 0x3d, 0x82, 0xd3,
-       0xe4, 0x93, 0x02, 0x37, 0x12, 0x35, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 
0x6f, 0x70, 0x65, 0x72,
-       0x74, 0x79, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 
0x6e, 0x74, 0x61, 0x69,
-       0x6e, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 
0x63, 0x6f, 0x6e, 0x74,
-       0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x42, 
0x7b, 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, 0x70, 0x72,
-       0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 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, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2f, 0x76, 0x31, 0x92,
-       0x41, 0x06, 0x22, 0x04, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 
0x6f, 0x74, 0x6f, 0x33,
+       0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 
0x52, 0x65, 0x71, 0x75,
+       0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 
0x64, 0x62, 0x2e, 0x70,
+       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 
0x65, 0x6c, 0x65, 0x74,
+       0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x82, 
0xd3, 0xe4, 0x93, 0x02,
+       0x58, 0x2a, 0x56, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x65, 
0x72, 0x74, 0x79, 0x2f,
+       0x7b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 
0x6e, 0x74, 0x61, 0x69,
+       0x6e, 0x65, 0x72, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 
0x6d, 0x65, 0x74, 0x61,
+       0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 
0x65, 0x72, 0x2e, 0x6e,
+       0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 
0x74, 0x61, 0x2e, 0x69,
+       0x64, 0x7d, 0x2f, 0x7b, 0x74, 0x61, 0x67, 0x73, 0x7d, 0x12, 0xaa, 0x01, 
0x0a, 0x03, 0x47, 0x65,
+       0x74, 0x12, 0x20, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 
0x2e, 0x70, 0x72, 0x6f,
+       0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 
0x52, 0x65, 0x71, 0x75,
+       0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 
0x64, 0x62, 0x2e, 0x70,
+       0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 
0x65, 0x74, 0x52, 0x65,
+       0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5e, 0x82, 0xd3, 0xe4, 0x93, 
0x02, 0x58, 0x12, 0x56,
+       0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2f, 0x7b, 0x6d, 0x65,
+       0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 
0x69, 0x6e, 0x65, 0x72,
+       0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 
0x61, 0x64, 0x61, 0x74,
+       0x61, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 
0x6e, 0x61, 0x6d, 0x65,
+       0x7d, 0x2f, 0x7b, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 
0x69, 0x64, 0x7d, 0x2f,
+       0x7b, 0x74, 0x61, 0x67, 0x73, 0x7d, 0x12, 0x99, 0x01, 0x0a, 0x04, 0x4c, 
0x69, 0x73, 0x74, 0x12,
+       0x21, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 0x62, 0x2e, 0x70, 
0x72, 0x6f, 0x70, 0x65,
+       0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 
0x65, 0x71, 0x75, 0x65,
+       0x73, 0x74, 0x1a, 0x22, 0x2e, 0x62, 0x61, 0x6e, 0x79, 0x61, 0x6e, 0x64, 
0x62, 0x2e, 0x70, 0x72,
+       0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 
0x73, 0x74, 0x52, 0x65,
+       0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4a, 0x82, 0xd3, 0xe4, 0x93, 
0x02, 0x44, 0x12, 0x42,
+       0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 
0x2f, 0x6c, 0x69, 0x73,
+       0x74, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 
0x72, 0x2e, 0x67, 0x72,
+       0x6f, 0x75, 0x70, 0x7d, 0x2f, 0x7b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 
0x6e, 0x65, 0x72, 0x2e,
+       0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x7b, 0x69, 0x64, 0x73, 0x7d, 0x2f, 
0x7b, 0x74, 0x61, 0x67,
+       0x73, 0x7d, 0x42, 0x7b, 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, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 
0x79, 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, 0x70, 
0x72, 0x6f, 0x70, 0x65,
+       0x72, 0x74, 0x79, 0x2f, 0x76, 0x31, 0x92, 0x41, 0x06, 0x22, 0x04, 0x2f, 
0x61, 0x70, 0x69, 0x62,
+       0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -625,42 +665,40 @@ func file_banyandb_property_v1_rpc_proto_rawDescGZIP() 
[]byte {
        return file_banyandb_property_v1_rpc_proto_rawDescData
 }
 
-var file_banyandb_property_v1_rpc_proto_msgTypes = 
make([]protoimpl.MessageInfo, 10)
+var file_banyandb_property_v1_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 
1)
+var file_banyandb_property_v1_rpc_proto_msgTypes = 
make([]protoimpl.MessageInfo, 8)
 var file_banyandb_property_v1_rpc_proto_goTypes = []interface{}{
-       (*CreateRequest)(nil),  // 0: banyandb.property.v1.CreateRequest
-       (*CreateResponse)(nil), // 1: banyandb.property.v1.CreateResponse
-       (*UpdateRequest)(nil),  // 2: banyandb.property.v1.UpdateRequest
-       (*UpdateResponse)(nil), // 3: banyandb.property.v1.UpdateResponse
-       (*DeleteRequest)(nil),  // 4: banyandb.property.v1.DeleteRequest
-       (*DeleteResponse)(nil), // 5: banyandb.property.v1.DeleteResponse
-       (*GetRequest)(nil),     // 6: banyandb.property.v1.GetRequest
-       (*GetResponse)(nil),    // 7: banyandb.property.v1.GetResponse
-       (*ListRequest)(nil),    // 8: banyandb.property.v1.ListRequest
-       (*ListResponse)(nil),   // 9: banyandb.property.v1.ListResponse
-       (*Property)(nil),       // 10: banyandb.property.v1.Property
-       (*Metadata)(nil),       // 11: banyandb.property.v1.Metadata
-       (*v1.Metadata)(nil),    // 12: banyandb.common.v1.Metadata
+       (ApplyRequest_Strategy)(0), // 0: 
banyandb.property.v1.ApplyRequest.Strategy
+       (*ApplyRequest)(nil),       // 1: banyandb.property.v1.ApplyRequest
+       (*ApplyResponse)(nil),      // 2: banyandb.property.v1.ApplyResponse
+       (*DeleteRequest)(nil),      // 3: banyandb.property.v1.DeleteRequest
+       (*DeleteResponse)(nil),     // 4: banyandb.property.v1.DeleteResponse
+       (*GetRequest)(nil),         // 5: banyandb.property.v1.GetRequest
+       (*GetResponse)(nil),        // 6: banyandb.property.v1.GetResponse
+       (*ListRequest)(nil),        // 7: banyandb.property.v1.ListRequest
+       (*ListResponse)(nil),       // 8: banyandb.property.v1.ListResponse
+       (*Property)(nil),           // 9: banyandb.property.v1.Property
+       (*Metadata)(nil),           // 10: banyandb.property.v1.Metadata
+       (*v1.Metadata)(nil),        // 11: banyandb.common.v1.Metadata
 }
 var file_banyandb_property_v1_rpc_proto_depIdxs = []int32{
-       10, // 0: banyandb.property.v1.CreateRequest.property:type_name -> 
banyandb.property.v1.Property
-       10, // 1: banyandb.property.v1.UpdateRequest.property:type_name -> 
banyandb.property.v1.Property
-       11, // 2: banyandb.property.v1.DeleteRequest.metadata:type_name -> 
banyandb.property.v1.Metadata
-       11, // 3: banyandb.property.v1.GetRequest.metadata:type_name -> 
banyandb.property.v1.Metadata
-       10, // 4: banyandb.property.v1.GetResponse.property:type_name -> 
banyandb.property.v1.Property
-       12, // 5: banyandb.property.v1.ListRequest.container:type_name -> 
banyandb.common.v1.Metadata
-       10, // 6: banyandb.property.v1.ListResponse.property:type_name -> 
banyandb.property.v1.Property
-       0,  // 7: banyandb.property.v1.PropertyService.Create:input_type -> 
banyandb.property.v1.CreateRequest
-       2,  // 8: banyandb.property.v1.PropertyService.Update:input_type -> 
banyandb.property.v1.UpdateRequest
-       4,  // 9: banyandb.property.v1.PropertyService.Delete:input_type -> 
banyandb.property.v1.DeleteRequest
-       6,  // 10: banyandb.property.v1.PropertyService.Get:input_type -> 
banyandb.property.v1.GetRequest
-       8,  // 11: banyandb.property.v1.PropertyService.List:input_type -> 
banyandb.property.v1.ListRequest
-       1,  // 12: banyandb.property.v1.PropertyService.Create:output_type -> 
banyandb.property.v1.CreateResponse
-       3,  // 13: banyandb.property.v1.PropertyService.Update:output_type -> 
banyandb.property.v1.UpdateResponse
-       5,  // 14: banyandb.property.v1.PropertyService.Delete:output_type -> 
banyandb.property.v1.DeleteResponse
-       7,  // 15: banyandb.property.v1.PropertyService.Get:output_type -> 
banyandb.property.v1.GetResponse
-       9,  // 16: banyandb.property.v1.PropertyService.List:output_type -> 
banyandb.property.v1.ListResponse
-       12, // [12:17] is the sub-list for method output_type
-       7,  // [7:12] is the sub-list for method input_type
+       9,  // 0: banyandb.property.v1.ApplyRequest.property:type_name -> 
banyandb.property.v1.Property
+       0,  // 1: banyandb.property.v1.ApplyRequest.strategy:type_name -> 
banyandb.property.v1.ApplyRequest.Strategy
+       10, // 2: banyandb.property.v1.DeleteRequest.metadata:type_name -> 
banyandb.property.v1.Metadata
+       10, // 3: banyandb.property.v1.GetRequest.metadata:type_name -> 
banyandb.property.v1.Metadata
+       9,  // 4: banyandb.property.v1.GetResponse.property:type_name -> 
banyandb.property.v1.Property
+       11, // 5: banyandb.property.v1.ListRequest.container:type_name -> 
banyandb.common.v1.Metadata
+       9,  // 6: banyandb.property.v1.ListResponse.property:type_name -> 
banyandb.property.v1.Property
+       1,  // 7: banyandb.property.v1.PropertyService.Apply:input_type -> 
banyandb.property.v1.ApplyRequest
+       3,  // 8: banyandb.property.v1.PropertyService.Delete:input_type -> 
banyandb.property.v1.DeleteRequest
+       5,  // 9: banyandb.property.v1.PropertyService.Get:input_type -> 
banyandb.property.v1.GetRequest
+       7,  // 10: banyandb.property.v1.PropertyService.List:input_type -> 
banyandb.property.v1.ListRequest
+       2,  // 11: banyandb.property.v1.PropertyService.Apply:output_type -> 
banyandb.property.v1.ApplyResponse
+       4,  // 12: banyandb.property.v1.PropertyService.Delete:output_type -> 
banyandb.property.v1.DeleteResponse
+       6,  // 13: banyandb.property.v1.PropertyService.Get:output_type -> 
banyandb.property.v1.GetResponse
+       8,  // 14: banyandb.property.v1.PropertyService.List:output_type -> 
banyandb.property.v1.ListResponse
+       11, // [11:15] is the sub-list for method output_type
+       7,  // [7:11] is the sub-list for method input_type
        7,  // [7:7] is the sub-list for extension type_name
        7,  // [7:7] is the sub-list for extension extendee
        0,  // [0:7] is the sub-list for field type_name
@@ -674,7 +712,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
        file_banyandb_property_v1_property_proto_init()
        if !protoimpl.UnsafeEnabled {
                file_banyandb_property_v1_rpc_proto_msgTypes[0].Exporter = 
func(v interface{}, i int) interface{} {
-                       switch v := v.(*CreateRequest); i {
+                       switch v := v.(*ApplyRequest); i {
                        case 0:
                                return &v.state
                        case 1:
@@ -686,7 +724,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
                        }
                }
                file_banyandb_property_v1_rpc_proto_msgTypes[1].Exporter = 
func(v interface{}, i int) interface{} {
-                       switch v := v.(*CreateResponse); i {
+                       switch v := v.(*ApplyResponse); i {
                        case 0:
                                return &v.state
                        case 1:
@@ -698,30 +736,6 @@ func file_banyandb_property_v1_rpc_proto_init() {
                        }
                }
                file_banyandb_property_v1_rpc_proto_msgTypes[2].Exporter = 
func(v interface{}, i int) interface{} {
-                       switch v := v.(*UpdateRequest); i {
-                       case 0:
-                               return &v.state
-                       case 1:
-                               return &v.sizeCache
-                       case 2:
-                               return &v.unknownFields
-                       default:
-                               return nil
-                       }
-               }
-               file_banyandb_property_v1_rpc_proto_msgTypes[3].Exporter = 
func(v interface{}, i int) interface{} {
-                       switch v := v.(*UpdateResponse); i {
-                       case 0:
-                               return &v.state
-                       case 1:
-                               return &v.sizeCache
-                       case 2:
-                               return &v.unknownFields
-                       default:
-                               return nil
-                       }
-               }
-               file_banyandb_property_v1_rpc_proto_msgTypes[4].Exporter = 
func(v interface{}, i int) interface{} {
                        switch v := v.(*DeleteRequest); i {
                        case 0:
                                return &v.state
@@ -733,7 +747,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
                                return nil
                        }
                }
-               file_banyandb_property_v1_rpc_proto_msgTypes[5].Exporter = 
func(v interface{}, i int) interface{} {
+               file_banyandb_property_v1_rpc_proto_msgTypes[3].Exporter = 
func(v interface{}, i int) interface{} {
                        switch v := v.(*DeleteResponse); i {
                        case 0:
                                return &v.state
@@ -745,7 +759,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
                                return nil
                        }
                }
-               file_banyandb_property_v1_rpc_proto_msgTypes[6].Exporter = 
func(v interface{}, i int) interface{} {
+               file_banyandb_property_v1_rpc_proto_msgTypes[4].Exporter = 
func(v interface{}, i int) interface{} {
                        switch v := v.(*GetRequest); i {
                        case 0:
                                return &v.state
@@ -757,7 +771,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
                                return nil
                        }
                }
-               file_banyandb_property_v1_rpc_proto_msgTypes[7].Exporter = 
func(v interface{}, i int) interface{} {
+               file_banyandb_property_v1_rpc_proto_msgTypes[5].Exporter = 
func(v interface{}, i int) interface{} {
                        switch v := v.(*GetResponse); i {
                        case 0:
                                return &v.state
@@ -769,7 +783,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
                                return nil
                        }
                }
-               file_banyandb_property_v1_rpc_proto_msgTypes[8].Exporter = 
func(v interface{}, i int) interface{} {
+               file_banyandb_property_v1_rpc_proto_msgTypes[6].Exporter = 
func(v interface{}, i int) interface{} {
                        switch v := v.(*ListRequest); i {
                        case 0:
                                return &v.state
@@ -781,7 +795,7 @@ func file_banyandb_property_v1_rpc_proto_init() {
                                return nil
                        }
                }
-               file_banyandb_property_v1_rpc_proto_msgTypes[9].Exporter = 
func(v interface{}, i int) interface{} {
+               file_banyandb_property_v1_rpc_proto_msgTypes[7].Exporter = 
func(v interface{}, i int) interface{} {
                        switch v := v.(*ListResponse); i {
                        case 0:
                                return &v.state
@@ -799,13 +813,14 @@ func file_banyandb_property_v1_rpc_proto_init() {
                File: protoimpl.DescBuilder{
                        GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
                        RawDescriptor: 
file_banyandb_property_v1_rpc_proto_rawDesc,
-                       NumEnums:      0,
-                       NumMessages:   10,
+                       NumEnums:      1,
+                       NumMessages:   8,
                        NumExtensions: 0,
                        NumServices:   1,
                },
                GoTypes:           file_banyandb_property_v1_rpc_proto_goTypes,
                DependencyIndexes: file_banyandb_property_v1_rpc_proto_depIdxs,
+               EnumInfos:         
file_banyandb_property_v1_rpc_proto_enumTypes,
                MessageInfos:      file_banyandb_property_v1_rpc_proto_msgTypes,
        }.Build()
        File_banyandb_property_v1_rpc_proto = out.File
diff --git a/api/proto/banyandb/property/v1/rpc.pb.gw.go 
b/api/proto/banyandb/property/v1/rpc.pb.gw.go
index b8f6a6a..2e0a59e 100644
--- a/api/proto/banyandb/property/v1/rpc.pb.gw.go
+++ b/api/proto/banyandb/property/v1/rpc.pb.gw.go
@@ -31,42 +31,8 @@ var _ = runtime.String
 var _ = utilities.NewDoubleArray
 var _ = metadata.Join
 
-func request_PropertyService_Create_0(ctx context.Context, marshaler 
runtime.Marshaler, client PropertyServiceClient, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-       var protoReq CreateRequest
-       var metadata runtime.ServerMetadata
-
-       newReader, berr := utilities.IOReaderFactory(req.Body)
-       if berr != nil {
-               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", berr)
-       }
-       if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != 
nil && err != io.EOF {
-               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
-       }
-
-       msg, err := client.Create(ctx, &protoReq, 
grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
-       return msg, metadata, err
-
-}
-
-func local_request_PropertyService_Create_0(ctx context.Context, marshaler 
runtime.Marshaler, server PropertyServiceServer, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-       var protoReq CreateRequest
-       var metadata runtime.ServerMetadata
-
-       newReader, berr := utilities.IOReaderFactory(req.Body)
-       if berr != nil {
-               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", berr)
-       }
-       if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != 
nil && err != io.EOF {
-               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
-       }
-
-       msg, err := server.Create(ctx, &protoReq)
-       return msg, metadata, err
-
-}
-
-func request_PropertyService_Update_0(ctx context.Context, marshaler 
runtime.Marshaler, client PropertyServiceClient, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-       var protoReq UpdateRequest
+func request_PropertyService_Apply_0(ctx context.Context, marshaler 
runtime.Marshaler, client PropertyServiceClient, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+       var protoReq ApplyRequest
        var metadata runtime.ServerMetadata
 
        newReader, berr := utilities.IOReaderFactory(req.Body)
@@ -114,13 +80,13 @@ func request_PropertyService_Update_0(ctx context.Context, 
marshaler runtime.Mar
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "property.metadata.id", err)
        }
 
-       msg, err := client.Update(ctx, &protoReq, 
grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+       msg, err := client.Apply(ctx, &protoReq, 
grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
        return msg, metadata, err
 
 }
 
-func local_request_PropertyService_Update_0(ctx context.Context, marshaler 
runtime.Marshaler, server PropertyServiceServer, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
-       var protoReq UpdateRequest
+func local_request_PropertyService_Apply_0(ctx context.Context, marshaler 
runtime.Marshaler, server PropertyServiceServer, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+       var protoReq ApplyRequest
        var metadata runtime.ServerMetadata
 
        newReader, berr := utilities.IOReaderFactory(req.Body)
@@ -168,13 +134,13 @@ func local_request_PropertyService_Update_0(ctx 
context.Context, marshaler runti
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "property.metadata.id", err)
        }
 
-       msg, err := server.Update(ctx, &protoReq)
+       msg, err := server.Apply(ctx, &protoReq)
        return msg, metadata, err
 
 }
 
 var (
-       filter_PropertyService_Delete_0 = &utilities.DoubleArray{Encoding: 
map[string]int{"metadata": 0, "container": 1, "group": 2, "name": 3, "id": 4}, 
Base: []int{1, 4, 1, 1, 2, 2, 0, 0, 4, 0}, Check: []int{0, 1, 2, 3, 2, 5, 4, 6, 
2, 9}}
+       filter_PropertyService_Delete_0 = &utilities.DoubleArray{Encoding: 
map[string]int{"metadata": 0, "container": 1, "group": 2, "name": 3, "id": 4, 
"tags": 5}, Base: []int{1, 5, 1, 1, 2, 2, 5, 0, 0, 4, 0, 0}, Check: []int{0, 1, 
2, 3, 2, 5, 1, 4, 6, 2, 10, 7}}
 )
 
 func request_PropertyService_Delete_0(ctx context.Context, marshaler 
runtime.Marshaler, client PropertyServiceClient, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -218,6 +184,16 @@ func request_PropertyService_Delete_0(ctx context.Context, 
marshaler runtime.Mar
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "metadata.id", err)
        }
 
+       val, ok = pathParams["tags"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "tags")
+       }
+
+       protoReq.Tags, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "tags", err)
+       }
+
        if err := req.ParseForm(); err != nil {
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
        }
@@ -271,6 +247,16 @@ func local_request_PropertyService_Delete_0(ctx 
context.Context, marshaler runti
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "metadata.id", err)
        }
 
+       val, ok = pathParams["tags"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "tags")
+       }
+
+       protoReq.Tags, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "tags", err)
+       }
+
        if err := req.ParseForm(); err != nil {
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
        }
@@ -284,7 +270,7 @@ func local_request_PropertyService_Delete_0(ctx 
context.Context, marshaler runti
 }
 
 var (
-       filter_PropertyService_Get_0 = &utilities.DoubleArray{Encoding: 
map[string]int{"metadata": 0, "container": 1, "group": 2, "name": 3, "id": 4}, 
Base: []int{1, 4, 1, 1, 2, 2, 0, 0, 4, 0}, Check: []int{0, 1, 2, 3, 2, 5, 4, 6, 
2, 9}}
+       filter_PropertyService_Get_0 = &utilities.DoubleArray{Encoding: 
map[string]int{"metadata": 0, "container": 1, "group": 2, "name": 3, "id": 4, 
"tags": 5}, Base: []int{1, 5, 1, 1, 2, 2, 5, 0, 0, 4, 0, 0}, Check: []int{0, 1, 
2, 3, 2, 5, 1, 4, 6, 2, 10, 7}}
 )
 
 func request_PropertyService_Get_0(ctx context.Context, marshaler 
runtime.Marshaler, client PropertyServiceClient, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -328,6 +314,16 @@ func request_PropertyService_Get_0(ctx context.Context, 
marshaler runtime.Marsha
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "metadata.id", err)
        }
 
+       val, ok = pathParams["tags"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "tags")
+       }
+
+       protoReq.Tags, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "tags", err)
+       }
+
        if err := req.ParseForm(); err != nil {
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
        }
@@ -381,6 +377,16 @@ func local_request_PropertyService_Get_0(ctx 
context.Context, marshaler runtime.
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "metadata.id", err)
        }
 
+       val, ok = pathParams["tags"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "tags")
+       }
+
+       protoReq.Tags, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "tags", err)
+       }
+
        if err := req.ParseForm(); err != nil {
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
        }
@@ -394,7 +400,7 @@ func local_request_PropertyService_Get_0(ctx 
context.Context, marshaler runtime.
 }
 
 var (
-       filter_PropertyService_List_0 = &utilities.DoubleArray{Encoding: 
map[string]int{"container": 0, "group": 1, "name": 2}, Base: []int{1, 1, 1, 2, 
0, 0}, Check: []int{0, 1, 2, 2, 3, 4}}
+       filter_PropertyService_List_0 = &utilities.DoubleArray{Encoding: 
map[string]int{"container": 0, "group": 1, "name": 2, "ids": 3, "tags": 4}, 
Base: []int{1, 1, 1, 2, 3, 4, 0, 0, 0, 0}, Check: []int{0, 1, 2, 2, 1, 1, 3, 4, 
5, 6}}
 )
 
 func request_PropertyService_List_0(ctx context.Context, marshaler 
runtime.Marshaler, client PropertyServiceClient, req *http.Request, pathParams 
map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -428,6 +434,26 @@ func request_PropertyService_List_0(ctx context.Context, 
marshaler runtime.Marsh
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "container.name", err)
        }
 
+       val, ok = pathParams["ids"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "ids")
+       }
+
+       protoReq.Ids, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "ids", err)
+       }
+
+       val, ok = pathParams["tags"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "tags")
+       }
+
+       protoReq.Tags, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "tags", err)
+       }
+
        if err := req.ParseForm(); err != nil {
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
        }
@@ -471,6 +497,26 @@ func local_request_PropertyService_List_0(ctx 
context.Context, marshaler runtime
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "container.name", err)
        }
 
+       val, ok = pathParams["ids"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "ids")
+       }
+
+       protoReq.Ids, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "ids", err)
+       }
+
+       val, ok = pathParams["tags"]
+       if !ok {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"missing parameter %s", "tags")
+       }
+
+       protoReq.Tags, err = runtime.StringSlice(val, ",")
+       if err != nil {
+               return nil, metadata, status.Errorf(codes.InvalidArgument, 
"type mismatch, parameter: %s, error: %v", "tags", err)
+       }
+
        if err := req.ParseForm(); err != nil {
                return nil, metadata, status.Errorf(codes.InvalidArgument, 
"%v", err)
        }
@@ -489,19 +535,19 @@ func local_request_PropertyService_List_0(ctx 
context.Context, marshaler runtime
 // Note that using this registration option will cause many gRPC library 
features to stop working. Consider using 
RegisterPropertyServiceHandlerFromEndpoint instead.
 func RegisterPropertyServiceHandlerServer(ctx context.Context, mux 
*runtime.ServeMux, server PropertyServiceServer) error {
 
-       mux.Handle("POST", pattern_PropertyService_Create_0, func(w 
http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+       mux.Handle("PUT", pattern_PropertyService_Apply_0, func(w 
http.ResponseWriter, req *http.Request, pathParams map[string]string) {
                ctx, cancel := context.WithCancel(req.Context())
                defer cancel()
                var stream runtime.ServerTransportStream
                ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Create", 
runtime.WithHTTPPathPattern("/v1/property"))
+               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Apply", 
runtime.WithHTTPPathPattern("/v1/property/{property.metadata.container.group}/{property.metadata.container.name}/{property.metadata.id}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
                }
-               resp, md, err := local_request_PropertyService_Create_0(ctx, 
inboundMarshaler, server, req, pathParams)
+               resp, md, err := local_request_PropertyService_Apply_0(ctx, 
inboundMarshaler, server, req, pathParams)
                md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, 
stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
                ctx = runtime.NewServerMetadataContext(ctx, md)
                if err != nil {
@@ -509,31 +555,7 @@ func RegisterPropertyServiceHandlerServer(ctx 
context.Context, mux *runtime.Serv
                        return
                }
 
-               forward_PropertyService_Create_0(ctx, mux, outboundMarshaler, 
w, req, resp, mux.GetForwardResponseOptions()...)
-
-       })
-
-       mux.Handle("PUT", pattern_PropertyService_Update_0, func(w 
http.ResponseWriter, req *http.Request, pathParams map[string]string) {
-               ctx, cancel := context.WithCancel(req.Context())
-               defer cancel()
-               var stream runtime.ServerTransportStream
-               ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
-               inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
-               var err error
-               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Update", 
runtime.WithHTTPPathPattern("/v1/property/{property.metadata.container.group}/{property.metadata.container.name}/{property.metadata.id}"))
-               if err != nil {
-                       runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
-                       return
-               }
-               resp, md, err := local_request_PropertyService_Update_0(ctx, 
inboundMarshaler, server, req, pathParams)
-               md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, 
stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
-               ctx = runtime.NewServerMetadataContext(ctx, md)
-               if err != nil {
-                       runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
-                       return
-               }
-
-               forward_PropertyService_Update_0(ctx, mux, outboundMarshaler, 
w, req, resp, mux.GetForwardResponseOptions()...)
+               forward_PropertyService_Apply_0(ctx, mux, outboundMarshaler, w, 
req, resp, mux.GetForwardResponseOptions()...)
 
        })
 
@@ -544,7 +566,7 @@ func RegisterPropertyServiceHandlerServer(ctx 
context.Context, mux *runtime.Serv
                ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Delete", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}"))
+               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Delete", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
@@ -568,7 +590,7 @@ func RegisterPropertyServiceHandlerServer(ctx 
context.Context, mux *runtime.Serv
                ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Get", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}"))
+               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Get", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
@@ -592,7 +614,7 @@ func RegisterPropertyServiceHandlerServer(ctx 
context.Context, mux *runtime.Serv
                ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/List", 
runtime.WithHTTPPathPattern("/v1/property/lists/{container.group}/{container.name}"))
+               ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/List", 
runtime.WithHTTPPathPattern("/v1/property/lists/{container.group}/{container.name}/{ids}/{tags}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
@@ -650,45 +672,24 @@ func RegisterPropertyServiceHandler(ctx context.Context, 
mux *runtime.ServeMux,
 // "PropertyServiceClient" to call the correct interceptors.
 func RegisterPropertyServiceHandlerClient(ctx context.Context, mux 
*runtime.ServeMux, client PropertyServiceClient) error {
 
-       mux.Handle("POST", pattern_PropertyService_Create_0, func(w 
http.ResponseWriter, req *http.Request, pathParams map[string]string) {
-               ctx, cancel := context.WithCancel(req.Context())
-               defer cancel()
-               inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
-               var err error
-               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Create", 
runtime.WithHTTPPathPattern("/v1/property"))
-               if err != nil {
-                       runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
-                       return
-               }
-               resp, md, err := request_PropertyService_Create_0(ctx, 
inboundMarshaler, client, req, pathParams)
-               ctx = runtime.NewServerMetadataContext(ctx, md)
-               if err != nil {
-                       runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
-                       return
-               }
-
-               forward_PropertyService_Create_0(ctx, mux, outboundMarshaler, 
w, req, resp, mux.GetForwardResponseOptions()...)
-
-       })
-
-       mux.Handle("PUT", pattern_PropertyService_Update_0, func(w 
http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+       mux.Handle("PUT", pattern_PropertyService_Apply_0, func(w 
http.ResponseWriter, req *http.Request, pathParams map[string]string) {
                ctx, cancel := context.WithCancel(req.Context())
                defer cancel()
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Update", 
runtime.WithHTTPPathPattern("/v1/property/{property.metadata.container.group}/{property.metadata.container.name}/{property.metadata.id}"))
+               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Apply", 
runtime.WithHTTPPathPattern("/v1/property/{property.metadata.container.group}/{property.metadata.container.name}/{property.metadata.id}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
                }
-               resp, md, err := request_PropertyService_Update_0(ctx, 
inboundMarshaler, client, req, pathParams)
+               resp, md, err := request_PropertyService_Apply_0(ctx, 
inboundMarshaler, client, req, pathParams)
                ctx = runtime.NewServerMetadataContext(ctx, md)
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
                }
 
-               forward_PropertyService_Update_0(ctx, mux, outboundMarshaler, 
w, req, resp, mux.GetForwardResponseOptions()...)
+               forward_PropertyService_Apply_0(ctx, mux, outboundMarshaler, w, 
req, resp, mux.GetForwardResponseOptions()...)
 
        })
 
@@ -697,7 +698,7 @@ func RegisterPropertyServiceHandlerClient(ctx 
context.Context, mux *runtime.Serv
                defer cancel()
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Delete", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}"))
+               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Delete", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
@@ -718,7 +719,7 @@ func RegisterPropertyServiceHandlerClient(ctx 
context.Context, mux *runtime.Serv
                defer cancel()
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Get", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}"))
+               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/Get", 
runtime.WithHTTPPathPattern("/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
@@ -739,7 +740,7 @@ func RegisterPropertyServiceHandlerClient(ctx 
context.Context, mux *runtime.Serv
                defer cancel()
                inboundMarshaler, outboundMarshaler := 
runtime.MarshalerForRequest(mux, req)
                var err error
-               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/List", 
runtime.WithHTTPPathPattern("/v1/property/lists/{container.group}/{container.name}"))
+               ctx, err = runtime.AnnotateContext(ctx, mux, req, 
"/banyandb.property.v1.PropertyService/List", 
runtime.WithHTTPPathPattern("/v1/property/lists/{container.group}/{container.name}/{ids}/{tags}"))
                if err != nil {
                        runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, 
err)
                        return
@@ -759,21 +760,17 @@ func RegisterPropertyServiceHandlerClient(ctx 
context.Context, mux *runtime.Serv
 }
 
 var (
-       pattern_PropertyService_Create_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", 
"property"}, ""))
-
-       pattern_PropertyService_Update_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 
1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "property", 
"property.metadata.container.group", "property.metadata.container.name", 
"property.metadata.id"}, ""))
+       pattern_PropertyService_Apply_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 
1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "property", 
"property.metadata.container.group", "property.metadata.container.name", 
"property.metadata.id"}, ""))
 
-       pattern_PropertyService_Delete_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 
1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "property", 
"metadata.container.group", "metadata.container.name", "metadata.id"}, ""))
+       pattern_PropertyService_Delete_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 
1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"v1", 
"property", "metadata.container.group", "metadata.container.name", 
"metadata.id", "tags"}, ""))
 
-       pattern_PropertyService_Get_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 
1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "property", 
"metadata.container.group", "metadata.container.name", "metadata.id"}, ""))
+       pattern_PropertyService_Get_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 
1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"v1", 
"property", "metadata.container.group", "metadata.container.name", 
"metadata.id", "tags"}, ""))
 
-       pattern_PropertyService_List_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 
5, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "property", "lists", "container.group", 
"container.name"}, ""))
+       pattern_PropertyService_List_0 = 
runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 
5, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6}, []string{"v1", 
"property", "lists", "container.group", "container.name", "ids", "tags"}, ""))
 )
 
 var (
-       forward_PropertyService_Create_0 = runtime.ForwardResponseMessage
-
-       forward_PropertyService_Update_0 = runtime.ForwardResponseMessage
+       forward_PropertyService_Apply_0 = runtime.ForwardResponseMessage
 
        forward_PropertyService_Delete_0 = runtime.ForwardResponseMessage
 
diff --git a/api/proto/banyandb/property/v1/rpc.pb.validate.go 
b/api/proto/banyandb/property/v1/rpc.pb.validate.go
index 802002b..35e5a2f 100644
--- a/api/proto/banyandb/property/v1/rpc.pb.validate.go
+++ b/api/proto/banyandb/property/v1/rpc.pb.validate.go
@@ -35,22 +35,22 @@ var (
        _ = sort.Sort
 )
 
-// Validate checks the field values on CreateRequest with the rules defined in
+// Validate checks the field values on ApplyRequest with the rules defined in
 // the proto definition for this message. If any rules are violated, the first
 // error encountered is returned, or nil if there are no violations.
-func (m *CreateRequest) Validate() error {
+func (m *ApplyRequest) Validate() error {
        return m.validate(false)
 }
 
-// ValidateAll checks the field values on CreateRequest with the rules defined
+// ValidateAll checks the field values on ApplyRequest with the rules defined
 // in the proto definition for this message. If any rules are violated, the
-// result is a list of violation errors wrapped in CreateRequestMultiError, or
+// result is a list of violation errors wrapped in ApplyRequestMultiError, or
 // nil if none found.
-func (m *CreateRequest) ValidateAll() error {
+func (m *ApplyRequest) ValidateAll() error {
        return m.validate(true)
 }
 
-func (m *CreateRequest) validate(all bool) error {
+func (m *ApplyRequest) validate(all bool) error {
        if m == nil {
                return nil
        }
@@ -58,7 +58,7 @@ func (m *CreateRequest) validate(all bool) error {
        var errors []error
 
        if m.GetProperty() == nil {
-               err := CreateRequestValidationError{
+               err := ApplyRequestValidationError{
                        field:  "Property",
                        reason: "value is required",
                }
@@ -72,7 +72,7 @@ func (m *CreateRequest) validate(all bool) error {
                switch v := interface{}(m.GetProperty()).(type) {
                case interface{ ValidateAll() error }:
                        if err := v.ValidateAll(); err != nil {
-                               errors = append(errors, 
CreateRequestValidationError{
+                               errors = append(errors, 
ApplyRequestValidationError{
                                        field:  "Property",
                                        reason: "embedded message failed 
validation",
                                        cause:  err,
@@ -80,7 +80,7 @@ func (m *CreateRequest) validate(all bool) error {
                        }
                case interface{ Validate() error }:
                        if err := v.Validate(); err != nil {
-                               errors = append(errors, 
CreateRequestValidationError{
+                               errors = append(errors, 
ApplyRequestValidationError{
                                        field:  "Property",
                                        reason: "embedded message failed 
validation",
                                        cause:  err,
@@ -89,7 +89,7 @@ func (m *CreateRequest) validate(all bool) error {
                }
        } else if v, ok := interface{}(m.GetProperty()).(interface{ Validate() 
error }); ok {
                if err := v.Validate(); err != nil {
-                       return CreateRequestValidationError{
+                       return ApplyRequestValidationError{
                                field:  "Property",
                                reason: "embedded message failed validation",
                                cause:  err,
@@ -97,120 +97,21 @@ func (m *CreateRequest) validate(all bool) error {
                }
        }
 
-       if len(errors) > 0 {
-               return CreateRequestMultiError(errors)
-       }
-
-       return nil
-}
-
-// CreateRequestMultiError is an error wrapping multiple validation errors
-// returned by CreateRequest.ValidateAll() if the designated constraints
-// aren't met.
-type CreateRequestMultiError []error
-
-// Error returns a concatenation of all the error messages it wraps.
-func (m CreateRequestMultiError) Error() string {
-       var msgs []string
-       for _, err := range m {
-               msgs = append(msgs, err.Error())
-       }
-       return strings.Join(msgs, "; ")
-}
-
-// AllErrors returns a list of validation violation errors.
-func (m CreateRequestMultiError) AllErrors() []error { return m }
-
-// CreateRequestValidationError is the validation error returned by
-// CreateRequest.Validate if the designated constraints aren't met.
-type CreateRequestValidationError struct {
-       field  string
-       reason string
-       cause  error
-       key    bool
-}
-
-// Field function returns field value.
-func (e CreateRequestValidationError) Field() string { return e.field }
-
-// Reason function returns reason value.
-func (e CreateRequestValidationError) Reason() string { return e.reason }
-
-// Cause function returns cause value.
-func (e CreateRequestValidationError) Cause() error { return e.cause }
-
-// Key function returns key value.
-func (e CreateRequestValidationError) Key() bool { return e.key }
-
-// ErrorName returns error name.
-func (e CreateRequestValidationError) ErrorName() string { return 
"CreateRequestValidationError" }
-
-// Error satisfies the builtin error interface
-func (e CreateRequestValidationError) Error() string {
-       cause := ""
-       if e.cause != nil {
-               cause = fmt.Sprintf(" | caused by: %v", e.cause)
-       }
-
-       key := ""
-       if e.key {
-               key = "key for "
-       }
-
-       return fmt.Sprintf(
-               "invalid %sCreateRequest.%s: %s%s",
-               key,
-               e.field,
-               e.reason,
-               cause)
-}
-
-var _ error = CreateRequestValidationError{}
-
-var _ interface {
-       Field() string
-       Reason() string
-       Key() bool
-       Cause() error
-       ErrorName() string
-} = CreateRequestValidationError{}
-
-// Validate checks the field values on CreateResponse with the rules defined in
-// the proto definition for this message. If any rules are violated, the first
-// error encountered is returned, or nil if there are no violations.
-func (m *CreateResponse) Validate() error {
-       return m.validate(false)
-}
-
-// ValidateAll checks the field values on CreateResponse with the rules defined
-// in the proto definition for this message. If any rules are violated, the
-// result is a list of violation errors wrapped in CreateResponseMultiError,
-// or nil if none found.
-func (m *CreateResponse) ValidateAll() error {
-       return m.validate(true)
-}
-
-func (m *CreateResponse) validate(all bool) error {
-       if m == nil {
-               return nil
-       }
-
-       var errors []error
+       // no validation rules for Strategy
 
        if len(errors) > 0 {
-               return CreateResponseMultiError(errors)
+               return ApplyRequestMultiError(errors)
        }
 
        return nil
 }
 
-// CreateResponseMultiError is an error wrapping multiple validation errors
-// returned by CreateResponse.ValidateAll() if the designated constraints
-// aren't met.
-type CreateResponseMultiError []error
+// ApplyRequestMultiError is an error wrapping multiple validation errors
+// returned by ApplyRequest.ValidateAll() if the designated constraints aren't 
met.
+type ApplyRequestMultiError []error
 
 // Error returns a concatenation of all the error messages it wraps.
-func (m CreateResponseMultiError) Error() string {
+func (m ApplyRequestMultiError) Error() string {
        var msgs []string
        for _, err := range m {
                msgs = append(msgs, err.Error())
@@ -219,11 +120,11 @@ func (m CreateResponseMultiError) Error() string {
 }
 
 // AllErrors returns a list of validation violation errors.
-func (m CreateResponseMultiError) AllErrors() []error { return m }
+func (m ApplyRequestMultiError) AllErrors() []error { return m }
 
-// CreateResponseValidationError is the validation error returned by
-// CreateResponse.Validate if the designated constraints aren't met.
-type CreateResponseValidationError struct {
+// ApplyRequestValidationError is the validation error returned by
+// ApplyRequest.Validate if the designated constraints aren't met.
+type ApplyRequestValidationError struct {
        field  string
        reason string
        cause  error
@@ -231,22 +132,22 @@ type CreateResponseValidationError struct {
 }
 
 // Field function returns field value.
-func (e CreateResponseValidationError) Field() string { return e.field }
+func (e ApplyRequestValidationError) Field() string { return e.field }
 
 // Reason function returns reason value.
-func (e CreateResponseValidationError) Reason() string { return e.reason }
+func (e ApplyRequestValidationError) Reason() string { return e.reason }
 
 // Cause function returns cause value.
-func (e CreateResponseValidationError) Cause() error { return e.cause }
+func (e ApplyRequestValidationError) Cause() error { return e.cause }
 
 // Key function returns key value.
-func (e CreateResponseValidationError) Key() bool { return e.key }
+func (e ApplyRequestValidationError) Key() bool { return e.key }
 
 // ErrorName returns error name.
-func (e CreateResponseValidationError) ErrorName() string { return 
"CreateResponseValidationError" }
+func (e ApplyRequestValidationError) ErrorName() string { return 
"ApplyRequestValidationError" }
 
 // Error satisfies the builtin error interface
-func (e CreateResponseValidationError) Error() string {
+func (e ApplyRequestValidationError) Error() string {
        cause := ""
        if e.cause != nil {
                cause = fmt.Sprintf(" | caused by: %v", e.cause)
@@ -258,14 +159,14 @@ func (e CreateResponseValidationError) Error() string {
        }
 
        return fmt.Sprintf(
-               "invalid %sCreateResponse.%s: %s%s",
+               "invalid %sApplyRequest.%s: %s%s",
                key,
                e.field,
                e.reason,
                cause)
 }
 
-var _ error = CreateResponseValidationError{}
+var _ error = ApplyRequestValidationError{}
 
 var _ interface {
        Field() string
@@ -273,84 +174,48 @@ var _ interface {
        Key() bool
        Cause() error
        ErrorName() string
-} = CreateResponseValidationError{}
+} = ApplyRequestValidationError{}
 
-// Validate checks the field values on UpdateRequest with the rules defined in
+// Validate checks the field values on ApplyResponse with the rules defined in
 // the proto definition for this message. If any rules are violated, the first
 // error encountered is returned, or nil if there are no violations.
-func (m *UpdateRequest) Validate() error {
+func (m *ApplyResponse) Validate() error {
        return m.validate(false)
 }
 
-// ValidateAll checks the field values on UpdateRequest with the rules defined
+// ValidateAll checks the field values on ApplyResponse with the rules defined
 // in the proto definition for this message. If any rules are violated, the
-// result is a list of violation errors wrapped in UpdateRequestMultiError, or
+// result is a list of violation errors wrapped in ApplyResponseMultiError, or
 // nil if none found.
-func (m *UpdateRequest) ValidateAll() error {
+func (m *ApplyResponse) ValidateAll() error {
        return m.validate(true)
 }
 
-func (m *UpdateRequest) validate(all bool) error {
+func (m *ApplyResponse) validate(all bool) error {
        if m == nil {
                return nil
        }
 
        var errors []error
 
-       if m.GetProperty() == nil {
-               err := UpdateRequestValidationError{
-                       field:  "Property",
-                       reason: "value is required",
-               }
-               if !all {
-                       return err
-               }
-               errors = append(errors, err)
-       }
+       // no validation rules for Created
 
-       if all {
-               switch v := interface{}(m.GetProperty()).(type) {
-               case interface{ ValidateAll() error }:
-                       if err := v.ValidateAll(); err != nil {
-                               errors = append(errors, 
UpdateRequestValidationError{
-                                       field:  "Property",
-                                       reason: "embedded message failed 
validation",
-                                       cause:  err,
-                               })
-                       }
-               case interface{ Validate() error }:
-                       if err := v.Validate(); err != nil {
-                               errors = append(errors, 
UpdateRequestValidationError{
-                                       field:  "Property",
-                                       reason: "embedded message failed 
validation",
-                                       cause:  err,
-                               })
-                       }
-               }
-       } else if v, ok := interface{}(m.GetProperty()).(interface{ Validate() 
error }); ok {
-               if err := v.Validate(); err != nil {
-                       return UpdateRequestValidationError{
-                               field:  "Property",
-                               reason: "embedded message failed validation",
-                               cause:  err,
-                       }
-               }
-       }
+       // no validation rules for TagsNum
 
        if len(errors) > 0 {
-               return UpdateRequestMultiError(errors)
+               return ApplyResponseMultiError(errors)
        }
 
        return nil
 }
 
-// UpdateRequestMultiError is an error wrapping multiple validation errors
-// returned by UpdateRequest.ValidateAll() if the designated constraints
+// ApplyResponseMultiError is an error wrapping multiple validation errors
+// returned by ApplyResponse.ValidateAll() if the designated constraints
 // aren't met.
-type UpdateRequestMultiError []error
+type ApplyResponseMultiError []error
 
 // Error returns a concatenation of all the error messages it wraps.
-func (m UpdateRequestMultiError) Error() string {
+func (m ApplyResponseMultiError) Error() string {
        var msgs []string
        for _, err := range m {
                msgs = append(msgs, err.Error())
@@ -359,11 +224,11 @@ func (m UpdateRequestMultiError) Error() string {
 }
 
 // AllErrors returns a list of validation violation errors.
-func (m UpdateRequestMultiError) AllErrors() []error { return m }
+func (m ApplyResponseMultiError) AllErrors() []error { return m }
 
-// UpdateRequestValidationError is the validation error returned by
-// UpdateRequest.Validate if the designated constraints aren't met.
-type UpdateRequestValidationError struct {
+// ApplyResponseValidationError is the validation error returned by
+// ApplyResponse.Validate if the designated constraints aren't met.
+type ApplyResponseValidationError struct {
        field  string
        reason string
        cause  error
@@ -371,22 +236,22 @@ type UpdateRequestValidationError struct {
 }
 
 // Field function returns field value.
-func (e UpdateRequestValidationError) Field() string { return e.field }
+func (e ApplyResponseValidationError) Field() string { return e.field }
 
 // Reason function returns reason value.
-func (e UpdateRequestValidationError) Reason() string { return e.reason }
+func (e ApplyResponseValidationError) Reason() string { return e.reason }
 
 // Cause function returns cause value.
-func (e UpdateRequestValidationError) Cause() error { return e.cause }
+func (e ApplyResponseValidationError) Cause() error { return e.cause }
 
 // Key function returns key value.
-func (e UpdateRequestValidationError) Key() bool { return e.key }
+func (e ApplyResponseValidationError) Key() bool { return e.key }
 
 // ErrorName returns error name.
-func (e UpdateRequestValidationError) ErrorName() string { return 
"UpdateRequestValidationError" }
+func (e ApplyResponseValidationError) ErrorName() string { return 
"ApplyResponseValidationError" }
 
 // Error satisfies the builtin error interface
-func (e UpdateRequestValidationError) Error() string {
+func (e ApplyResponseValidationError) Error() string {
        cause := ""
        if e.cause != nil {
                cause = fmt.Sprintf(" | caused by: %v", e.cause)
@@ -398,14 +263,14 @@ func (e UpdateRequestValidationError) Error() string {
        }
 
        return fmt.Sprintf(
-               "invalid %sUpdateRequest.%s: %s%s",
+               "invalid %sApplyResponse.%s: %s%s",
                key,
                e.field,
                e.reason,
                cause)
 }
 
-var _ error = UpdateRequestValidationError{}
+var _ error = ApplyResponseValidationError{}
 
 var _ interface {
        Field() string
@@ -413,107 +278,7 @@ var _ interface {
        Key() bool
        Cause() error
        ErrorName() string
-} = UpdateRequestValidationError{}
-
-// Validate checks the field values on UpdateResponse with the rules defined in
-// the proto definition for this message. If any rules are violated, the first
-// error encountered is returned, or nil if there are no violations.
-func (m *UpdateResponse) Validate() error {
-       return m.validate(false)
-}
-
-// ValidateAll checks the field values on UpdateResponse with the rules defined
-// in the proto definition for this message. If any rules are violated, the
-// result is a list of violation errors wrapped in UpdateResponseMultiError,
-// or nil if none found.
-func (m *UpdateResponse) ValidateAll() error {
-       return m.validate(true)
-}
-
-func (m *UpdateResponse) validate(all bool) error {
-       if m == nil {
-               return nil
-       }
-
-       var errors []error
-
-       if len(errors) > 0 {
-               return UpdateResponseMultiError(errors)
-       }
-
-       return nil
-}
-
-// UpdateResponseMultiError is an error wrapping multiple validation errors
-// returned by UpdateResponse.ValidateAll() if the designated constraints
-// aren't met.
-type UpdateResponseMultiError []error
-
-// Error returns a concatenation of all the error messages it wraps.
-func (m UpdateResponseMultiError) Error() string {
-       var msgs []string
-       for _, err := range m {
-               msgs = append(msgs, err.Error())
-       }
-       return strings.Join(msgs, "; ")
-}
-
-// AllErrors returns a list of validation violation errors.
-func (m UpdateResponseMultiError) AllErrors() []error { return m }
-
-// UpdateResponseValidationError is the validation error returned by
-// UpdateResponse.Validate if the designated constraints aren't met.
-type UpdateResponseValidationError struct {
-       field  string
-       reason string
-       cause  error
-       key    bool
-}
-
-// Field function returns field value.
-func (e UpdateResponseValidationError) Field() string { return e.field }
-
-// Reason function returns reason value.
-func (e UpdateResponseValidationError) Reason() string { return e.reason }
-
-// Cause function returns cause value.
-func (e UpdateResponseValidationError) Cause() error { return e.cause }
-
-// Key function returns key value.
-func (e UpdateResponseValidationError) Key() bool { return e.key }
-
-// ErrorName returns error name.
-func (e UpdateResponseValidationError) ErrorName() string { return 
"UpdateResponseValidationError" }
-
-// Error satisfies the builtin error interface
-func (e UpdateResponseValidationError) Error() string {
-       cause := ""
-       if e.cause != nil {
-               cause = fmt.Sprintf(" | caused by: %v", e.cause)
-       }
-
-       key := ""
-       if e.key {
-               key = "key for "
-       }
-
-       return fmt.Sprintf(
-               "invalid %sUpdateResponse.%s: %s%s",
-               key,
-               e.field,
-               e.reason,
-               cause)
-}
-
-var _ error = UpdateResponseValidationError{}
-
-var _ interface {
-       Field() string
-       Reason() string
-       Key() bool
-       Cause() error
-       ErrorName() string
-} = UpdateResponseValidationError{}
+} = ApplyResponseValidationError{}
 
 // Validate checks the field values on DeleteRequest with the rules defined in
 // the proto definition for this message. If any rules are violated, the first
@@ -679,6 +444,8 @@ func (m *DeleteResponse) validate(all bool) error {
 
        // no validation rules for Deleted
 
+       // no validation rules for TagsNum
+
        if len(errors) > 0 {
                return DeleteResponseMultiError(errors)
        }
diff --git a/api/proto/banyandb/property/v1/rpc.proto 
b/api/proto/banyandb/property/v1/rpc.proto
index 99d7e13..8b90ae6 100644
--- a/api/proto/banyandb/property/v1/rpc.proto
+++ b/api/proto/banyandb/property/v1/rpc.proto
@@ -32,30 +32,37 @@ option 
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
     base_path:"/api"
 };
 
-message CreateRequest {
+message ApplyRequest {
     banyandb.property.v1.Property property = 1 
[(validate.rules).message.required = true];
+    enum Strategy {
+        STRATEGY_UNSPECIFIED=0;
+        STRATEGY_MERGE=1;
+        STRATEGY_REPLACE=2;
+    }
+    // strategy indicates how to update a property. It defaults to 
STRATEGY_MERGE
+    Strategy strategy = 2;
 }
 
-message CreateResponse {
-}
-
-message UpdateRequest {
-    banyandb.property.v1.Property property = 1 
[(validate.rules).message.required = true];
-}
-
-message UpdateResponse {
+message ApplyResponse {
+    // created indicates whether the property existed.
+    // True: the property is absent. False: the property existed.
+    bool created = 1;
+    uint32 tags_num = 2;
 }
 
 message DeleteRequest {
     banyandb.property.v1.Metadata metadata = 1 
[(validate.rules).message.required = true];
+    repeated string tags = 2;
 }
 
 message DeleteResponse {
     bool deleted = 1;
+    uint32 tags_num = 2;
 }
 
 message GetRequest {
     banyandb.property.v1.Metadata metadata = 1 
[(validate.rules).message.required = true];
+    repeated string tags = 2;
 }
 
 message GetResponse {
@@ -64,6 +71,8 @@ message GetResponse {
 
 message ListRequest {
     banyandb.common.v1.Metadata container = 1 
[(validate.rules).message.required = true];
+    repeated string ids = 2;
+    repeated string tags = 3;
 }
 
 message ListResponse {
@@ -71,13 +80,8 @@ message ListResponse {
 }
 
 service PropertyService {
-    rpc Create(CreateRequest) returns (CreateResponse){
-        option(google.api.http) = {
-            post:"/v1/property"
-            body:"*"
-        };
-    };
-    rpc Update(UpdateRequest) returns (UpdateResponse){
+    // Apply creates a property if it's absent, or update a existed one based 
on a strategy.
+    rpc Apply(ApplyRequest) returns (ApplyResponse){
         option(google.api.http) = {
             
put:"/v1/property/{property.metadata.container.group}/{property.metadata.container.name}/{property.metadata.id}"
             body:"*"
@@ -85,17 +89,17 @@ service PropertyService {
     };
     rpc Delete(DeleteRequest) returns (DeleteResponse){
         option(google.api.http) = {
-            
delete:"/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}"
+            
delete:"/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}"
         };
     };
     rpc Get(GetRequest) returns (GetResponse){
         option(google.api.http) = {
-            
get:"/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}"
+            
get:"/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}"
         };
     };
     rpc List(ListRequest) returns (ListResponse){
         option(google.api.http) = {
-            get:"/v1/property/lists/{container.group}/{container.name}"
+            
get:"/v1/property/lists/{container.group}/{container.name}/{ids}/{tags}"
         };
     };
 }
diff --git a/api/proto/banyandb/property/v1/rpc_grpc.pb.go 
b/api/proto/banyandb/property/v1/rpc_grpc.pb.go
index 62004b8..7ec3995 100644
--- a/api/proto/banyandb/property/v1/rpc_grpc.pb.go
+++ b/api/proto/banyandb/property/v1/rpc_grpc.pb.go
@@ -22,8 +22,8 @@ const _ = grpc.SupportPackageIsVersion7
 //
 // For semantics around ctx use and closing/ending streaming RPCs, please 
refer to 
https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
 type PropertyServiceClient interface {
-       Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) 
(*CreateResponse, error)
-       Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) 
(*UpdateResponse, error)
+       // Apply creates a property if it's absent, or update a existed one 
based on a strategy.
+       Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) 
(*ApplyResponse, error)
        Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) 
(*DeleteResponse, error)
        Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) 
(*GetResponse, error)
        List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) 
(*ListResponse, error)
@@ -37,18 +37,9 @@ func NewPropertyServiceClient(cc grpc.ClientConnInterface) 
PropertyServiceClient
        return &propertyServiceClient{cc}
 }
 
-func (c *propertyServiceClient) Create(ctx context.Context, in *CreateRequest, 
opts ...grpc.CallOption) (*CreateResponse, error) {
-       out := new(CreateResponse)
-       err := c.cc.Invoke(ctx, "/banyandb.property.v1.PropertyService/Create", 
in, out, opts...)
-       if err != nil {
-               return nil, err
-       }
-       return out, nil
-}
-
-func (c *propertyServiceClient) Update(ctx context.Context, in *UpdateRequest, 
opts ...grpc.CallOption) (*UpdateResponse, error) {
-       out := new(UpdateResponse)
-       err := c.cc.Invoke(ctx, "/banyandb.property.v1.PropertyService/Update", 
in, out, opts...)
+func (c *propertyServiceClient) Apply(ctx context.Context, in *ApplyRequest, 
opts ...grpc.CallOption) (*ApplyResponse, error) {
+       out := new(ApplyResponse)
+       err := c.cc.Invoke(ctx, "/banyandb.property.v1.PropertyService/Apply", 
in, out, opts...)
        if err != nil {
                return nil, err
        }
@@ -86,8 +77,8 @@ func (c *propertyServiceClient) List(ctx context.Context, in 
*ListRequest, opts
 // All implementations must embed UnimplementedPropertyServiceServer
 // for forward compatibility
 type PropertyServiceServer interface {
-       Create(context.Context, *CreateRequest) (*CreateResponse, error)
-       Update(context.Context, *UpdateRequest) (*UpdateResponse, error)
+       // Apply creates a property if it's absent, or update a existed one 
based on a strategy.
+       Apply(context.Context, *ApplyRequest) (*ApplyResponse, error)
        Delete(context.Context, *DeleteRequest) (*DeleteResponse, error)
        Get(context.Context, *GetRequest) (*GetResponse, error)
        List(context.Context, *ListRequest) (*ListResponse, error)
@@ -98,11 +89,8 @@ type PropertyServiceServer interface {
 type UnimplementedPropertyServiceServer struct {
 }
 
-func (UnimplementedPropertyServiceServer) Create(context.Context, 
*CreateRequest) (*CreateResponse, error) {
-       return nil, status.Errorf(codes.Unimplemented, "method Create not 
implemented")
-}
-func (UnimplementedPropertyServiceServer) Update(context.Context, 
*UpdateRequest) (*UpdateResponse, error) {
-       return nil, status.Errorf(codes.Unimplemented, "method Update not 
implemented")
+func (UnimplementedPropertyServiceServer) Apply(context.Context, 
*ApplyRequest) (*ApplyResponse, error) {
+       return nil, status.Errorf(codes.Unimplemented, "method Apply not 
implemented")
 }
 func (UnimplementedPropertyServiceServer) Delete(context.Context, 
*DeleteRequest) (*DeleteResponse, error) {
        return nil, status.Errorf(codes.Unimplemented, "method Delete not 
implemented")
@@ -126,38 +114,20 @@ func RegisterPropertyServiceServer(s 
grpc.ServiceRegistrar, srv PropertyServiceS
        s.RegisterService(&PropertyService_ServiceDesc, srv)
 }
 
-func _PropertyService_Create_Handler(srv interface{}, ctx context.Context, dec 
func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, 
error) {
-       in := new(CreateRequest)
-       if err := dec(in); err != nil {
-               return nil, err
-       }
-       if interceptor == nil {
-               return srv.(PropertyServiceServer).Create(ctx, in)
-       }
-       info := &grpc.UnaryServerInfo{
-               Server:     srv,
-               FullMethod: "/banyandb.property.v1.PropertyService/Create",
-       }
-       handler := func(ctx context.Context, req interface{}) (interface{}, 
error) {
-               return srv.(PropertyServiceServer).Create(ctx, 
req.(*CreateRequest))
-       }
-       return interceptor(ctx, in, info, handler)
-}
-
-func _PropertyService_Update_Handler(srv interface{}, ctx context.Context, dec 
func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, 
error) {
-       in := new(UpdateRequest)
+func _PropertyService_Apply_Handler(srv interface{}, ctx context.Context, dec 
func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, 
error) {
+       in := new(ApplyRequest)
        if err := dec(in); err != nil {
                return nil, err
        }
        if interceptor == nil {
-               return srv.(PropertyServiceServer).Update(ctx, in)
+               return srv.(PropertyServiceServer).Apply(ctx, in)
        }
        info := &grpc.UnaryServerInfo{
                Server:     srv,
-               FullMethod: "/banyandb.property.v1.PropertyService/Update",
+               FullMethod: "/banyandb.property.v1.PropertyService/Apply",
        }
        handler := func(ctx context.Context, req interface{}) (interface{}, 
error) {
-               return srv.(PropertyServiceServer).Update(ctx, 
req.(*UpdateRequest))
+               return srv.(PropertyServiceServer).Apply(ctx, 
req.(*ApplyRequest))
        }
        return interceptor(ctx, in, info, handler)
 }
@@ -224,12 +194,8 @@ var PropertyService_ServiceDesc = grpc.ServiceDesc{
        HandlerType: (*PropertyServiceServer)(nil),
        Methods: []grpc.MethodDesc{
                {
-                       MethodName: "Create",
-                       Handler:    _PropertyService_Create_Handler,
-               },
-               {
-                       MethodName: "Update",
-                       Handler:    _PropertyService_Update_Handler,
+                       MethodName: "Apply",
+                       Handler:    _PropertyService_Apply_Handler,
                },
                {
                        MethodName: "Delete",
diff --git a/api/proto/openapi/banyandb/property/v1/rpc.swagger.json 
b/api/proto/openapi/banyandb/property/v1/rpc.swagger.json
index 984151f..b8435b8 100644
--- a/api/proto/openapi/banyandb/property/v1/rpc.swagger.json
+++ b/api/proto/openapi/banyandb/property/v1/rpc.swagger.json
@@ -17,39 +17,7 @@
     "application/json"
   ],
   "paths": {
-    "/v1/property": {
-      "post": {
-        "operationId": "PropertyService_Create",
-        "responses": {
-          "200": {
-            "description": "A successful response.",
-            "schema": {
-              "$ref": "#/definitions/v1CreateResponse"
-            }
-          },
-          "default": {
-            "description": "An unexpected error response.",
-            "schema": {
-              "$ref": "#/definitions/rpcStatus"
-            }
-          }
-        },
-        "parameters": [
-          {
-            "name": "body",
-            "in": "body",
-            "required": true,
-            "schema": {
-              "$ref": "#/definitions/v1CreateRequest"
-            }
-          }
-        ],
-        "tags": [
-          "PropertyService"
-        ]
-      }
-    },
-    "/v1/property/lists/{container.group}/{container.name}": {
+    "/v1/property/lists/{container.group}/{container.name}/{ids}/{tags}": {
       "get": {
         "operationId": "PropertyService_List",
         "responses": {
@@ -81,6 +49,28 @@
             "required": true,
             "type": "string"
           },
+          {
+            "name": "ids",
+            "in": "path",
+            "required": true,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "csv",
+            "minItems": 1
+          },
+          {
+            "name": "tags",
+            "in": "path",
+            "required": true,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "csv",
+            "minItems": 1
+          },
           {
             "name": "container.id",
             "in": "query",
@@ -110,7 +100,7 @@
         ]
       }
     },
-    
"/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}":
 {
+    
"/v1/property/{metadata.container.group}/{metadata.container.name}/{metadata.id}/{tags}":
 {
       "get": {
         "operationId": "PropertyService_Get",
         "responses": {
@@ -149,6 +139,17 @@
             "required": true,
             "type": "string"
           },
+          {
+            "name": "tags",
+            "in": "path",
+            "required": true,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "csv",
+            "minItems": 1
+          },
           {
             "name": "metadata.container.id",
             "in": "query",
@@ -215,6 +216,17 @@
             "required": true,
             "type": "string"
           },
+          {
+            "name": "tags",
+            "in": "path",
+            "required": true,
+            "type": "array",
+            "items": {
+              "type": "string"
+            },
+            "collectionFormat": "csv",
+            "minItems": 1
+          },
           {
             "name": "metadata.container.id",
             "in": "query",
@@ -246,12 +258,13 @@
     },
     
"/v1/property/{property.metadata.container.group}/{property.metadata.container.name}/{property.metadata.id}":
 {
       "put": {
-        "operationId": "PropertyService_Update",
+        "summary": "Apply creates a property if it's absent, or update a 
existed one based on a strategy.",
+        "operationId": "PropertyService_Apply",
         "responses": {
           "200": {
             "description": "A successful response.",
             "schema": {
-              "$ref": "#/definitions/v1UpdateResponse"
+              "$ref": "#/definitions/v1ApplyResponse"
             }
           },
           "default": {
@@ -333,6 +346,10 @@
                     }
                   },
                   "title": "Property stores the user defined data"
+                },
+                "strategy": {
+                  "$ref": "#/definitions/ApplyRequestStrategy",
+                  "title": "strategy indicates how to update a property. It 
defaults to STRATEGY_MERGE"
                 }
               }
             }
@@ -345,6 +362,15 @@
     }
   },
   "definitions": {
+    "ApplyRequestStrategy": {
+      "type": "string",
+      "enum": [
+        "STRATEGY_UNSPECIFIED",
+        "STRATEGY_MERGE",
+        "STRATEGY_REPLACE"
+      ],
+      "default": "STRATEGY_UNSPECIFIED"
+    },
     "banyandbcommonv1Metadata": {
       "type": "object",
       "properties": {
@@ -434,22 +460,28 @@
         }
       }
     },
-    "v1CreateRequest": {
+    "v1ApplyResponse": {
       "type": "object",
       "properties": {
-        "property": {
-          "$ref": "#/definitions/v1Property"
+        "created": {
+          "type": "boolean",
+          "description": "created indicates whether the property 
existed.\nTrue: the property is absent. False: the property existed."
+        },
+        "tagsNum": {
+          "type": "integer",
+          "format": "int64"
         }
       }
     },
-    "v1CreateResponse": {
-      "type": "object"
-    },
     "v1DeleteResponse": {
       "type": "object",
       "properties": {
         "deleted": {
           "type": "boolean"
+        },
+        "tagsNum": {
+          "type": "integer",
+          "format": "int64"
         }
       }
     },
@@ -568,9 +600,6 @@
           "$ref": "#/definitions/v1ID"
         }
       }
-    },
-    "v1UpdateResponse": {
-      "type": "object"
     }
   }
 }
diff --git a/banyand/liaison/grpc/property.go b/banyand/liaison/grpc/property.go
index e2e27b5..d262797 100644
--- a/banyand/liaison/grpc/property.go
+++ b/banyand/liaison/grpc/property.go
@@ -29,32 +29,27 @@ type propertyServer struct {
        propertyv1.UnimplementedPropertyServiceServer
 }
 
-func (ps *propertyServer) Create(ctx context.Context, req 
*propertyv1.CreateRequest) (*propertyv1.CreateResponse, error) {
-       if err := ps.schemaRegistry.PropertyRegistry().CreateProperty(ctx, 
req.GetProperty()); err != nil {
-               return nil, err
-       }
-       return &propertyv1.CreateResponse{}, nil
-}
-
-func (ps *propertyServer) Update(ctx context.Context, req 
*propertyv1.UpdateRequest) (*propertyv1.UpdateResponse, error) {
-       if err := ps.schemaRegistry.PropertyRegistry().UpdateProperty(ctx, 
req.GetProperty()); err != nil {
+func (ps *propertyServer) Apply(ctx context.Context, req 
*propertyv1.ApplyRequest) (*propertyv1.ApplyResponse, error) {
+       created, tagsNum, err := 
ps.schemaRegistry.PropertyRegistry().ApplyProperty(ctx, req.Property, 
req.Strategy)
+       if err != nil {
                return nil, err
        }
-       return &propertyv1.UpdateResponse{}, nil
+       return &propertyv1.ApplyResponse{Created: created, TagsNum: tagsNum}, 
nil
 }
 
 func (ps *propertyServer) Delete(ctx context.Context, req 
*propertyv1.DeleteRequest) (*propertyv1.DeleteResponse, error) {
-       ok, err := ps.schemaRegistry.PropertyRegistry().DeleteProperty(ctx, 
req.GetMetadata())
+       ok, tagsNum, err := 
ps.schemaRegistry.PropertyRegistry().DeleteProperty(ctx, req.GetMetadata(), 
req.Tags)
        if err != nil {
                return nil, err
        }
        return &propertyv1.DeleteResponse{
                Deleted: ok,
+               TagsNum: tagsNum,
        }, nil
 }
 
 func (ps *propertyServer) Get(ctx context.Context, req *propertyv1.GetRequest) 
(*propertyv1.GetResponse, error) {
-       entity, err := ps.schemaRegistry.PropertyRegistry().GetProperty(ctx, 
req.GetMetadata())
+       entity, err := ps.schemaRegistry.PropertyRegistry().GetProperty(ctx, 
req.GetMetadata(), req.GetTags())
        if err != nil {
                return nil, err
        }
@@ -64,7 +59,7 @@ func (ps *propertyServer) Get(ctx context.Context, req 
*propertyv1.GetRequest) (
 }
 
 func (ps *propertyServer) List(ctx context.Context, req 
*propertyv1.ListRequest) (*propertyv1.ListResponse, error) {
-       entities, err := ps.schemaRegistry.PropertyRegistry().ListProperty(ctx, 
req.GetContainer())
+       entities, err := ps.schemaRegistry.PropertyRegistry().ListProperty(ctx, 
req.GetContainer(), req.Ids, req.Tags)
        if err != nil {
                return nil, err
        }
diff --git a/banyand/metadata/schema/property.go 
b/banyand/metadata/schema/property.go
index d873014..e8c79ff 100644
--- a/banyand/metadata/schema/property.go
+++ b/banyand/metadata/schema/property.go
@@ -19,6 +19,7 @@ package schema
 
 import (
        "context"
+       "errors"
 
        "google.golang.org/protobuf/proto"
 
@@ -26,17 +27,38 @@ import (
        propertyv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/property/v1"
 )
 
+const all = "*"
+
 var PropertyKeyPrefix = "/properties/"
 
-func (e *etcdSchemaRegistry) GetProperty(ctx context.Context, metadata 
*propertyv1.Metadata) (*propertyv1.Property, error) {
+func (e *etcdSchemaRegistry) GetProperty(ctx context.Context, metadata 
*propertyv1.Metadata, tags []string) (*propertyv1.Property, error) {
        var entity propertyv1.Property
        if err := e.get(ctx, formatPropertyKey(transformKey(metadata)), 
&entity); err != nil {
                return nil, err
        }
-       return &entity, nil
+       return filterTags(&entity, tags), nil
 }
 
-func (e *etcdSchemaRegistry) ListProperty(ctx context.Context, container 
*commonv1.Metadata) ([]*propertyv1.Property, error) {
+func filterTags(property *propertyv1.Property, tags []string) 
*propertyv1.Property {
+       if len(tags) == 0 || tags[0] == all {
+               return property
+       }
+       filtered := &propertyv1.Property{
+               Metadata:  property.Metadata,
+               UpdatedAt: property.UpdatedAt,
+       }
+
+       for _, expectedTag := range tags {
+               for _, t := range property.Tags {
+                       if t.Key == expectedTag {
+                               filtered.Tags = append(filtered.Tags, t)
+                       }
+               }
+       }
+       return filtered
+}
+
+func (e *etcdSchemaRegistry) ListProperty(ctx context.Context, container 
*commonv1.Metadata, ids []string, tags []string) ([]*propertyv1.Property, 
error) {
        if container.Group == "" {
                return nil, BadRequest("container.group", "group should not be 
empty")
        }
@@ -48,44 +70,91 @@ func (e *etcdSchemaRegistry) ListProperty(ctx 
context.Context, container *common
        }
        entities := make([]*propertyv1.Property, 0, len(messages))
        for _, message := range messages {
-               entities = append(entities, message.(*propertyv1.Property))
+               p := message.(*propertyv1.Property)
+               if len(ids) < 1 || ids[0] == all {
+                       entities = append(entities, filterTags(p, tags))
+                       continue
+               }
+               for _, id := range ids {
+                       if p.Metadata.Id == id {
+                               entities = append(entities, filterTags(p, tags))
+                       }
+               }
        }
        return entities, nil
 }
 
-func (e *etcdSchemaRegistry) CreateProperty(ctx context.Context, property 
*propertyv1.Property) error {
+func (e *etcdSchemaRegistry) ApplyProperty(ctx context.Context, property 
*propertyv1.Property, strategy propertyv1.ApplyRequest_Strategy) (bool, uint32, 
error) {
        m := transformKey(property.GetMetadata())
-       return e.create(ctx, Metadata{
+       md := Metadata{
                TypeMeta: TypeMeta{
                        Kind:  KindProperty,
                        Group: m.GetGroup(),
                        Name:  m.GetName(),
                },
                Spec: property,
-       })
+       }
+       tagsNum := uint32(len(property.Tags))
+       err := e.create(ctx, md)
+       if err == nil {
+               return true, tagsNum, nil
+       }
+       if !errors.Is(err, ErrGRPCAlreadyExists) {
+               return false, 0, err
+       }
+       if strategy != propertyv1.ApplyRequest_STRATEGY_REPLACE {
+               existed, errGet := e.GetProperty(ctx, property.Metadata, nil)
+               if errGet != nil {
+                       return false, 0, errGet
+               }
+               for i := 0; i < int(tagsNum); i++ {
+                       t := property.Tags[0]
+                       property.Tags = property.Tags[1:]
+                       for _, et := range existed.Tags {
+                               if et.Key == t.Key {
+                                       et.Value = t.Value
+                               }
+                       }
+               }
+               existed.Tags = append(existed.Tags, property.Tags...)
+               md.Spec = existed
+       }
+       if err = e.update(ctx, md); err != nil {
+               return false, 0, err
+       }
+       return false, tagsNum, nil
 }
 
-func (e *etcdSchemaRegistry) UpdateProperty(ctx context.Context, property 
*propertyv1.Property) error {
-       m := transformKey(property.GetMetadata())
-       return e.update(ctx, Metadata{
-               TypeMeta: TypeMeta{
-                       Kind:  KindProperty,
-                       Group: m.GetGroup(),
-                       Name:  m.GetName(),
-               },
-               Spec: property,
-       })
-}
+func (e *etcdSchemaRegistry) DeleteProperty(ctx context.Context, metadata 
*propertyv1.Metadata, tags []string) (bool, uint32, error) {
+       if len(tags) == 0 || tags[0] == all {
+               m := transformKey(metadata)
+               deleted, err := e.delete(ctx, Metadata{
+                       TypeMeta: TypeMeta{
+                               Kind:  KindProperty,
+                               Group: m.GetGroup(),
+                               Name:  m.GetName(),
+                       },
+               })
+               return deleted, 0, err
+       }
+       property, err := e.GetProperty(ctx, metadata, nil)
+       if err != nil {
+               return false, 0, err
+       }
+       filtered := &propertyv1.Property{
+               Metadata:  property.Metadata,
+               UpdatedAt: property.UpdatedAt,
+       }
 
-func (e *etcdSchemaRegistry) DeleteProperty(ctx context.Context, metadata 
*propertyv1.Metadata) (bool, error) {
-       m := transformKey(metadata)
-       return e.delete(ctx, Metadata{
-               TypeMeta: TypeMeta{
-                       Kind:  KindProperty,
-                       Group: m.GetGroup(),
-                       Name:  m.GetName(),
-               },
-       })
+       for _, expectedTag := range tags {
+               for _, t := range property.Tags {
+                       if t.Key != expectedTag {
+                               filtered.Tags = append(filtered.Tags, t)
+                       }
+               }
+       }
+       _, num, err := e.ApplyProperty(ctx, filtered, 
propertyv1.ApplyRequest_STRATEGY_REPLACE)
+       return true, num, err
 }
 
 func transformKey(metadata *propertyv1.Metadata) *commonv1.Metadata {
diff --git a/banyand/metadata/schema/schema.go 
b/banyand/metadata/schema/schema.go
index 31898d5..ec52e4d 100644
--- a/banyand/metadata/schema/schema.go
+++ b/banyand/metadata/schema/schema.go
@@ -203,9 +203,8 @@ type TopNAggregation interface {
 }
 
 type Property interface {
-       GetProperty(ctx context.Context, metadata *propertyv1.Metadata) 
(*propertyv1.Property, error)
-       ListProperty(ctx context.Context, container *commonv1.Metadata) 
([]*propertyv1.Property, error)
-       CreateProperty(ctx context.Context, property *propertyv1.Property) error
-       UpdateProperty(ctx context.Context, property *propertyv1.Property) error
-       DeleteProperty(ctx context.Context, metadata *propertyv1.Metadata) 
(bool, error)
+       GetProperty(ctx context.Context, metadata *propertyv1.Metadata, tags 
[]string) (*propertyv1.Property, error)
+       ListProperty(ctx context.Context, container *commonv1.Metadata, ids 
[]string, tags []string) ([]*propertyv1.Property, error)
+       ApplyProperty(ctx context.Context, property *propertyv1.Property, 
strategy propertyv1.ApplyRequest_Strategy) (bool, uint32, error)
+       DeleteProperty(ctx context.Context, metadata *propertyv1.Metadata, tags 
[]string) (bool, uint32, error)
 }
diff --git a/docs/api-reference.md b/docs/api-reference.md
index 10843bb..409876d 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -169,16 +169,16 @@
     - [Property](#banyandb-property-v1-Property)
   
 - [banyandb/property/v1/rpc.proto](#banyandb_property_v1_rpc-proto)
-    - [CreateRequest](#banyandb-property-v1-CreateRequest)
-    - [CreateResponse](#banyandb-property-v1-CreateResponse)
+    - [ApplyRequest](#banyandb-property-v1-ApplyRequest)
+    - [ApplyResponse](#banyandb-property-v1-ApplyResponse)
     - [DeleteRequest](#banyandb-property-v1-DeleteRequest)
     - [DeleteResponse](#banyandb-property-v1-DeleteResponse)
     - [GetRequest](#banyandb-property-v1-GetRequest)
     - [GetResponse](#banyandb-property-v1-GetResponse)
     - [ListRequest](#banyandb-property-v1-ListRequest)
     - [ListResponse](#banyandb-property-v1-ListResponse)
-    - [UpdateRequest](#banyandb-property-v1-UpdateRequest)
-    - [UpdateResponse](#banyandb-property-v1-UpdateResponse)
+  
+    - [ApplyRequest.Strategy](#banyandb-property-v1-ApplyRequest-Strategy)
   
     - [PropertyService](#banyandb-property-v1-PropertyService)
   
@@ -2457,26 +2457,33 @@ Property stores the user defined data
 
 
 
-<a name="banyandb-property-v1-CreateRequest"></a>
+<a name="banyandb-property-v1-ApplyRequest"></a>
 
-### CreateRequest
+### ApplyRequest
 
 
 
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
 | property | [Property](#banyandb-property-v1-Property) |  |  |
+| strategy | 
[ApplyRequest.Strategy](#banyandb-property-v1-ApplyRequest-Strategy) |  | 
strategy indicates how to update a property. It defaults to STRATEGY_MERGE |
+
 
 
 
 
 
+<a name="banyandb-property-v1-ApplyResponse"></a>
 
-<a name="banyandb-property-v1-CreateResponse"></a>
+### ApplyResponse
 
-### CreateResponse
 
 
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| created | [bool](#bool) |  | created indicates whether the property existed. 
True: the property is absent. False: the property existed. |
+| tags_num | [uint32](#uint32) |  |  |
+
 
 
 
@@ -2491,6 +2498,7 @@ Property stores the user defined data
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
 | metadata | [Metadata](#banyandb-property-v1-Metadata) |  |  |
+| tags | [string](#string) | repeated |  |
 
 
 
@@ -2506,6 +2514,7 @@ Property stores the user defined data
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
 | deleted | [bool](#bool) |  |  |
+| tags_num | [uint32](#uint32) |  |  |
 
 
 
@@ -2521,6 +2530,7 @@ Property stores the user defined data
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
 | metadata | [Metadata](#banyandb-property-v1-Metadata) |  |  |
+| tags | [string](#string) | repeated |  |
 
 
 
@@ -2551,6 +2561,8 @@ Property stores the user defined data
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
 | container | [banyandb.common.v1.Metadata](#banyandb-common-v1-Metadata) |  | 
 |
+| ids | [string](#string) | repeated |  |
+| tags | [string](#string) | repeated |  |
 
 
 
@@ -2571,32 +2583,20 @@ Property stores the user defined data
 
 
 
-
-<a name="banyandb-property-v1-UpdateRequest"></a>
-
-### UpdateRequest
-
-
-
-| Field | Type | Label | Description |
-| ----- | ---- | ----- | ----------- |
-| property | [Property](#banyandb-property-v1-Property) |  |  |
-
-
-
-
-
-
-<a name="banyandb-property-v1-UpdateResponse"></a>
-
-### UpdateResponse
+ 
 
 
+<a name="banyandb-property-v1-ApplyRequest-Strategy"></a>
 
+### ApplyRequest.Strategy
 
 
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| STRATEGY_UNSPECIFIED | 0 |  |
+| STRATEGY_MERGE | 1 |  |
+| STRATEGY_REPLACE | 2 |  |
 
- 
 
  
 
@@ -2610,8 +2610,7 @@ Property stores the user defined data
 
 | Method Name | Request Type | Response Type | Description |
 | ----------- | ------------ | ------------- | ------------|
-| Create | [CreateRequest](#banyandb-property-v1-CreateRequest) | 
[CreateResponse](#banyandb-property-v1-CreateResponse) |  |
-| Update | [UpdateRequest](#banyandb-property-v1-UpdateRequest) | 
[UpdateResponse](#banyandb-property-v1-UpdateResponse) |  |
+| Apply | [ApplyRequest](#banyandb-property-v1-ApplyRequest) | 
[ApplyResponse](#banyandb-property-v1-ApplyResponse) | Apply creates a property 
if it&#39;s absent, or update a existed one based on a strategy. |
 | Delete | [DeleteRequest](#banyandb-property-v1-DeleteRequest) | 
[DeleteResponse](#banyandb-property-v1-DeleteResponse) |  |
 | Get | [GetRequest](#banyandb-property-v1-GetRequest) | 
[GetResponse](#banyandb-property-v1-GetResponse) |  |
 | List | [ListRequest](#banyandb-property-v1-ListRequest) | 
[ListResponse](#banyandb-property-v1-ListResponse) |  |
diff --git a/test/integration/other/property_test.go 
b/test/integration/other/property_test.go
new file mode 100644
index 0000000..07dbdfb
--- /dev/null
+++ b/test/integration/other/property_test.go
@@ -0,0 +1,146 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package integration_other_test
+
+import (
+       "context"
+
+       "github.com/apache/skywalking-banyandb/pkg/test/setup"
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+       grpclib "google.golang.org/grpc"
+       "google.golang.org/grpc/credentials/insecure"
+
+       common_v1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
+       model_v1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
+       property_v1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/property/v1"
+)
+
+var _ = Describe("Property application", func() {
+       var deferFn func()
+       var conn *grpclib.ClientConn
+       var client property_v1.PropertyServiceClient
+
+       BeforeEach(func() {
+               var addr string
+               addr, deferFn = setup.SetUp()
+               var err error
+               conn, err = grpclib.Dial(
+                       addr,
+                       
grpclib.WithTransportCredentials(insecure.NewCredentials()),
+               )
+               Expect(err).NotTo(HaveOccurred())
+               client = property_v1.NewPropertyServiceClient(conn)
+       })
+       AfterEach(func() {
+               Expect(conn.Close()).To(Succeed())
+               deferFn()
+       })
+       It("applies properties", func() {
+               md := &property_v1.Metadata{
+                       Container: &common_v1.Metadata{
+                               Name:  "p",
+                               Group: "g",
+                       },
+                       Id: "1",
+               }
+               resp, err := client.Apply(context.Background(), 
&property_v1.ApplyRequest{Property: &property_v1.Property{
+                       Metadata: md,
+                       Tags: []*model_v1.Tag{
+                               {Key: "t1", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v1"}}}},
+                               {Key: "t2", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v2"}}}},
+                       },
+               }})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(resp.Created).To(BeTrue())
+               Expect(resp.TagsNum).To(Equal(uint32(2)))
+               got, err := client.Get(context.Background(), 
&property_v1.GetRequest{Metadata: md})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(got.Property.Tags).To(Equal([]*model_v1.Tag{
+                       {Key: "t1", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v1"}}}},
+                       {Key: "t2", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v2"}}}},
+               }))
+               resp, err = client.Apply(context.Background(), 
&property_v1.ApplyRequest{Property: &property_v1.Property{
+                       Metadata: md,
+                       Tags: []*model_v1.Tag{
+                               {Key: "t2", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v22"}}}},
+                       },
+               }})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(resp.Created).To(BeFalse())
+               Expect(resp.TagsNum).To(Equal(uint32(1)))
+               got, err = client.Get(context.Background(), 
&property_v1.GetRequest{Metadata: md})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(got.Property.Tags).To(Equal([]*model_v1.Tag{
+                       {Key: "t1", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v1"}}}},
+                       {Key: "t2", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v22"}}}},
+               }))
+       })
+})
+
+var _ = Describe("Property application", func() {
+       var deferFn func()
+       var conn *grpclib.ClientConn
+       var client property_v1.PropertyServiceClient
+       var md *property_v1.Metadata
+
+       BeforeEach(func() {
+               var addr string
+               addr, deferFn = setup.SetUp()
+               var err error
+               conn, err = grpclib.Dial(
+                       addr,
+                       
grpclib.WithTransportCredentials(insecure.NewCredentials()),
+               )
+               Expect(err).NotTo(HaveOccurred())
+               client = property_v1.NewPropertyServiceClient(conn)
+               md = &property_v1.Metadata{
+                       Container: &common_v1.Metadata{
+                               Name:  "p",
+                               Group: "g",
+                       },
+                       Id: "1",
+               }
+               resp, err := client.Apply(context.Background(), 
&property_v1.ApplyRequest{Property: &property_v1.Property{
+                       Metadata: md,
+                       Tags: []*model_v1.Tag{
+                               {Key: "t1", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v1"}}}},
+                               {Key: "t2", Value: &model_v1.TagValue{Value: 
&model_v1.TagValue_Str{Str: &model_v1.Str{Value: "v2"}}}},
+                       },
+               }})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(resp.Created).To(BeTrue())
+               Expect(resp.TagsNum).To(Equal(uint32(2)))
+       })
+       AfterEach(func() {
+               Expect(conn.Close()).To(Succeed())
+               deferFn()
+       })
+       It("lists properties", func() {
+               got, err := client.List(context.Background(), 
&property_v1.ListRequest{Container: md.Container})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(len(got.Property)).To(Equal(1))
+       })
+       It("deletes properties", func() {
+               got, err := client.Delete(context.Background(), 
&property_v1.DeleteRequest{Metadata: md})
+               Expect(err).NotTo(HaveOccurred())
+               Expect(got.Deleted).To(BeTrue())
+               _, err = client.Get(context.Background(), 
&property_v1.GetRequest{Metadata: md})
+               Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
+       })
+})

Reply via email to