You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/10/05 10:16:11 UTC

[skywalking-banyandb] branch main updated: Refactor Property (#181)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 35a8b9b  Refactor Property (#181)
35a8b9b is described below

commit 35a8b9b3bc7aae262eeced1602da89e8c42f8121
Author: Gao Hongtao <ha...@gmail.com>
AuthorDate: Wed Oct 5 18:16:05 2022 +0800

    Refactor Property (#181)
    
    * Merge Create and Update to Apply
    * Add tag selector to Get
    * Add id and tag selector to List
    
    Signed-off-by: Gao Hongtao <ha...@gmail.com>
---
 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"))
+	})
+})