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

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

commit 164447d7385441da4c0c7087b5faa4ff15c299a7
Author: Gao Hongtao <[email protected]>
AuthorDate: Thu Dec 18 15:15:43 2025 +0000

    Add proto files for Banyandb API including common, database, measure, 
model, property, stream, and trace definitions
---
 proto/banyandb/v1/banyandb-common.proto   | 182 ++++++++
 proto/banyandb/v1/banyandb-database.proto | 742 ++++++++++++++++++++++++++++++
 proto/banyandb/v1/banyandb-measure.proto  | 219 +++++++++
 proto/banyandb/v1/banyandb-model.proto    | 198 ++++++++
 proto/banyandb/v1/banyandb-property.proto | 112 +++++
 proto/banyandb/v1/banyandb-stream.proto   | 130 ++++++
 proto/banyandb/v1/banyandb-trace.proto    | 122 +++++
 7 files changed, 1705 insertions(+)

diff --git a/proto/banyandb/v1/banyandb-common.proto 
b/proto/banyandb/v1/banyandb-common.proto
new file mode 100644
index 0000000..d4ace04
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-common.proto
@@ -0,0 +1,182 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.common.v1";
+
+package banyandb.common.v1;
+
+import "google/protobuf/timestamp.proto";
+import "validate/validate.proto";
+
+enum Catalog {
+  CATALOG_UNSPECIFIED = 0;
+  CATALOG_STREAM = 1;
+  CATALOG_MEASURE = 2;
+  CATALOG_PROPERTY = 3;
+  CATALOG_TRACE = 4;
+}
+
+// Metadata is for multi-tenant, multi-model use
+message Metadata {
+  // group contains a set of options, like retention policy, max
+  string group = 1;
+  // name of the entity
+  string name = 2;
+  // id is the unique identifier of the entity
+  // if id is not set, the system will generate a unique id
+  uint32 id = 3;
+  // readonly. create_revision is the revision of last creation on this key.
+  int64 create_revision = 4;
+  // readonly. mod_revision is the revision of last modification on this key.
+  int64 mod_revision = 5;
+}
+
+// IntervalRule is a structured duration
+message IntervalRule {
+  enum Unit {
+    UNIT_UNSPECIFIED = 0;
+    UNIT_HOUR = 1;
+    UNIT_DAY = 2;
+  }
+  // unit can only be UNIT_HOUR or UNIT_DAY
+  Unit unit = 1 [(validate.rules).enum.defined_only = true];
+  uint32 num = 2 [(validate.rules).uint32.gt = 0];
+}
+
+message LifecycleStage {
+  // The stage name (e.g., "warm", "cold").
+  // This should be a non-empty string.
+  string name = 1 [(validate.rules).string.min_len = 1];
+
+  // Number of shards allocated for this stage.
+  // Must be greater than zero.
+  uint32 shard_num = 2 [(validate.rules).uint32.gt = 0];
+
+  // Defines the interval for data segmentation in this stage.
+  // This is a required field and uses the IntervalRule structure.
+  IntervalRule segment_interval = 3 [(validate.rules).message.required = true];
+
+  // Specifies the time-to-live for data in this stage before moving to the 
next.
+  // This is also a required field using the IntervalRule structure.
+  IntervalRule ttl = 4 [(validate.rules).message.required = true];
+
+  // Node selector specifying target nodes for this stage.
+  // Optional; if provided, it must be a non-empty string.
+  string node_selector = 5 [(validate.rules).string.min_len = 1];
+
+  // Indicates whether segments that are no longer live should be closed.
+  bool close = 6;
+
+  // replicas is the number of replicas for this stage.
+  // This is an optional field and defaults to 0.
+  // A value of 0 means no replicas, while a value of 1 means one primary 
shard and one replica.
+  // Higher values indicate more replicas.
+  uint32 replicas = 7;
+}
+
+message ResourceOpts {
+  // shard_num is the number of shards
+  uint32 shard_num = 1 [(validate.rules).uint32.gt = 0];
+  // segment_interval indicates the length of a segment
+  IntervalRule segment_interval = 2;
+  // ttl indicates time to live, how long the data will be cached
+  IntervalRule ttl = 3;
+  // stages defines the ordered lifecycle stages. Data progresses through 
these stages sequentially.
+  repeated LifecycleStage stages = 4;
+  // default_stages is the name of the default stage
+  repeated string default_stages = 5;
+  // replicas is the number of replicas. This is used to ensure high 
availability and fault tolerance.
+  // This is an optional field and defaults to 0.
+  // A value of 0 means no replicas, while a value of 1 means one primary 
shard and one replica.
+  // Higher values indicate more replicas.
+  uint32 replicas = 6;
+}
+
+// Group is an internal object for Group management
+message Group {
+  // metadata define the group's identity
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // catalog denotes which type of data the group contains
+  common.v1.Catalog catalog = 2;
+  // resourceOpts indicates the structure of the underlying kv storage
+  ResourceOpts resource_opts = 3;
+  // updated_at indicates when resources of the group are updated
+  google.protobuf.Timestamp updated_at = 4;
+}
+
+// APIVersion is the version of the API
+message APIVersion {
+  // version is the version of the API
+  string version = 1;
+  // revision is the commit hash of the API
+  string revision = 2;
+}
+
+// GetAPIVersionRequest is the request for GetAPIVersion
+message GetAPIVersionRequest {
+  // empty
+}
+
+// GetAPIVersionResponse is the response for GetAPIVersion
+message GetAPIVersionResponse {
+  // version is the version of the API
+  APIVersion version = 1;
+}
+
+// Service is the service for the API
+service Service {
+  // GetAPIVersion returns the version of the API
+  rpc GetAPIVersion(GetAPIVersionRequest) returns (GetAPIVersionResponse);
+}
+
+// Trace is the top level message of a trace.
+message Trace {
+  // trace_id is the unique identifier of the trace.
+  string trace_id = 1;
+  // spans is a list of spans in the trace.
+  repeated Span spans = 2;
+  // error indicates whether the trace is an error trace.
+  bool error = 3;
+}
+
+// Span is the basic unit of a trace.
+message Span {
+  // start_time is the start time of the span.
+  google.protobuf.Timestamp start_time = 1;
+  // end_time is the end time of the span.
+  google.protobuf.Timestamp end_time = 2;
+  // error indicates whether the span is an error span.
+  bool error = 3;
+  // tags is a list of tags of the span.
+  repeated Tag tags = 4;
+  // message is the message generated by the span.
+  string message = 5;
+  // children is a list of child spans of the span.
+  repeated Span children = 6;
+  // duration is the duration of the span.
+  int64 duration = 7;
+}
+
+// Tag is the key-value pair of a span.
+message Tag {
+  // key is the key of the tag.
+  string key = 1;
+  // value is the value of the tag.
+  string value = 2;
+}
diff --git a/proto/banyandb/v1/banyandb-database.proto 
b/proto/banyandb/v1/banyandb-database.proto
new file mode 100644
index 0000000..bd8242a
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-database.proto
@@ -0,0 +1,742 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.database.v1";
+
+package banyandb.database.v1;
+
+import "google/protobuf/timestamp.proto";
+import "validate/validate.proto";
+import "banyandb/v1/banyandb-common.proto";
+import "banyandb/v1/banyandb-model.proto";
+
+enum TagType {
+  TAG_TYPE_UNSPECIFIED = 0;
+  TAG_TYPE_STRING = 1;
+  TAG_TYPE_INT = 2;
+  TAG_TYPE_STRING_ARRAY = 3;
+  TAG_TYPE_INT_ARRAY = 4;
+  TAG_TYPE_DATA_BINARY = 5;
+  TAG_TYPE_TIMESTAMP = 6;
+}
+
+message TagFamilySpec {
+  string name = 1 [(validate.rules).string.min_len = 1];
+  // tags defines accepted tags
+  repeated TagSpec tags = 2 [(validate.rules).repeated.min_items = 1];
+}
+
+message TagSpec {
+  string name = 1 [(validate.rules).string.min_len = 1];
+  TagType type = 2 [(validate.rules).enum.defined_only = true];
+}
+
+// Stream intends to store streaming data, for example, traces or logs
+message Stream {
+  // metadata is the identity of a trace series
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // tag_families
+  repeated TagFamilySpec tag_families = 2 [(validate.rules).repeated.min_items 
= 1];
+  // entity indicates how to generate a series and shard a stream
+  Entity entity = 3 [(validate.rules).message.required = true];
+  // updated_at indicates when the stream is updated
+  google.protobuf.Timestamp updated_at = 4;
+}
+
+message Entity {
+  repeated string tag_names = 1 [(validate.rules).repeated.min_items = 1];
+}
+
+message ShardingKey {
+  repeated string tag_names = 1 [(validate.rules).repeated.min_items = 1];
+}
+
+enum FieldType {
+  FIELD_TYPE_UNSPECIFIED = 0;
+  FIELD_TYPE_STRING = 1;
+  FIELD_TYPE_INT = 2;
+  FIELD_TYPE_DATA_BINARY = 3;
+  FIELD_TYPE_FLOAT = 4;
+}
+
+enum EncodingMethod {
+  ENCODING_METHOD_UNSPECIFIED = 0;
+  ENCODING_METHOD_GORILLA = 1;
+}
+
+enum CompressionMethod {
+  COMPRESSION_METHOD_UNSPECIFIED = 0;
+  COMPRESSION_METHOD_ZSTD = 1;
+}
+
+// FieldSpec is the specification of field
+message FieldSpec {
+  // name is the identity of a field
+  string name = 1 [(validate.rules).string.min_len = 1];
+  // field_type denotes the type of field value
+  FieldType field_type = 2 [(validate.rules).enum.defined_only = true];
+  // encoding_method indicates how to encode data during writing
+  EncodingMethod encoding_method = 3 [(validate.rules).enum.defined_only = 
true];
+  // compression_method indicates how to compress data during writing
+  CompressionMethod compression_method = 4 [(validate.rules).enum.defined_only 
= true];
+}
+
+// Measure intends to store data point
+message Measure {
+  // metadata is the identity of a measure
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // tag_families are for filter measures
+  repeated TagFamilySpec tag_families = 2 [(validate.rules).repeated.min_items 
= 1];
+  // fields denote measure values
+  repeated FieldSpec fields = 3;
+  // entity indicates which tags will be to generate a series and shard a 
measure
+  Entity entity = 4 [(validate.rules).message.required = true];
+  // interval indicates how frequently to send a data point
+  // valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d".
+  string interval = 5;
+  // updated_at indicates when the measure is updated
+  google.protobuf.Timestamp updated_at = 6;
+  // index_mode specifies whether the data should be stored exclusively in the 
index,
+  // meaning it will not be stored in the data storage system.
+  bool index_mode = 7;
+  // sharding_key determines the distribution of TopN-related data.
+  ShardingKey sharding_key = 8;
+}
+
+// TopNAggregation generates offline TopN statistics for a measure's TopN 
approximation
+message TopNAggregation {
+  // metadata is the identity of an aggregation
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // source_measure denotes the data source of this aggregation
+  common.v1.Metadata source_measure = 2 [(validate.rules).message.required = 
true];
+  // field_name is the name of field used for ranking
+  string field_name = 3 [(validate.rules).string.min_len = 1];
+  // field_value_sort indicates how to sort fields
+  // ASC: bottomN
+  // DESC: topN
+  // UNSPECIFIED: topN + bottomN
+  // todo validate plugin exist bug 
https://github.com/bufbuild/protoc-gen-validate/issues/672
+  model.v1.Sort field_value_sort = 4;
+  // group_by_tag_names groups data points into statistical counters
+  repeated string group_by_tag_names = 5;
+  // criteria select partial data points from measure
+  model.v1.Criteria criteria = 6;
+  // counters_number sets the number of counters to be tracked. The default 
value is 1000
+  int32 counters_number = 7;
+  // lru_size defines how much entry is allowed to be maintained in the memory
+  int32 lru_size = 8;
+  // updated_at indicates when the measure is updated
+  google.protobuf.Timestamp updated_at = 9;
+}
+
+// IndexRule defines how to generate indices based on tags and the index type
+// IndexRule should bind to a subject through an IndexRuleBinding to generate 
proper indices.
+message IndexRule {
+  // metadata define the rule's identity
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // tags are the combination that refers to an indexed object
+  // If the elements in tags are more than 1, the object will generate a 
multi-tag index
+  // Caveat: All tags in a multi-tag MUST have an identical IndexType
+  repeated string tags = 2 [(validate.rules).repeated.min_items = 1];
+  // Type determine the index structure under the hood
+  enum Type {
+    TYPE_UNSPECIFIED = 0;
+    TYPE_INVERTED = 1;
+    TYPE_SKIPPING = 2;
+    // TYPE_TREE is a tree index, which is used for storing hierarchical data.
+    TYPE_TREE = 3;
+  }
+  // type is the IndexType of this IndexObject.
+  Type type = 3 [(validate.rules).enum.defined_only = true];
+  // updated_at indicates when the IndexRule is updated
+  google.protobuf.Timestamp updated_at = 4;
+
+  // analyzer analyzes tag value to support the full-text searching for 
TYPE_INVERTED indices.
+  // available analyzers are:
+  // - "standard" provides grammar based tokenization
+  // - "simple" breaks text into tokens at any non-letter character,
+  //            such as numbers, spaces, hyphens and apostrophes, discards 
non-letter characters,
+  //            and changes uppercase to lowercase.
+  // - "keyword" is a “noop” analyzer which returns the entire input string as 
a single token.
+  // - "url" breaks test into tokens at any non-letter and non-digit character.
+  string analyzer = 5;
+  // no_sort indicates whether the index is not for sorting.
+  bool no_sort = 6;
+}
+
+// Subject defines which stream or measure would generate indices
+message Subject {
+  // catalog is where the subject belongs to
+  // todo validate plugin exist bug 
https://github.com/bufbuild/protoc-gen-validate/issues/672
+  common.v1.Catalog catalog = 1;
+  // name refers to a stream or measure in a particular catalog
+  string name = 2 [(validate.rules).string.min_len = 1];
+}
+
+// IndexRuleBinding is a bridge to connect severalIndexRules to a subject
+// This binding is valid between begin_at_nanoseconds and 
expire_at_nanoseconds, that provides flexible strategies
+// to control how to generate time series indices.
+message IndexRuleBinding {
+  // metadata is the identity of this binding
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // rules refers to the IndexRule
+  repeated string rules = 2 [(validate.rules).repeated.min_items = 1];
+  // subject indicates the subject of binding action
+  Subject subject = 3 [(validate.rules).message.required = true];
+  // begin_at_nanoseconds is the timestamp, after which the binding will be 
active
+  google.protobuf.Timestamp begin_at = 4 [(validate.rules).timestamp.required 
= true];
+  // expire_at_nanoseconds it the timestamp, after which the binding will be 
inactive
+  // expire_at_nanoseconds must be larger than begin_at_nanoseconds
+  google.protobuf.Timestamp expire_at = 5 [(validate.rules).timestamp.required 
= true];
+  // updated_at indicates when the IndexRuleBinding is updated
+  google.protobuf.Timestamp updated_at = 6;
+}
+
+// Property stores the user defined data
+message Property {
+  // metadata is the identity of a property
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // tag stores the content of a property
+  repeated TagSpec tags = 2 [(validate.rules).repeated.min_items = 1];
+  // updated_at indicates when the property is updated
+  google.protobuf.Timestamp updated_at = 6;
+}
+
+// TraceTagSpec defines the specification of a tag in a trace.
+message TraceTagSpec {
+  // name is the name of the tag.
+  string name = 1 [(validate.rules).string.min_len = 1];
+  // type is the type of the tag.
+  TagType type = 2 [(validate.rules).enum.defined_only = true];
+}
+
+// Trace defines a tracing-specific storage resource.
+// It is suitable for storing traces and spans.
+// The name of a Trace is a logical namespace within a group,
+// while the group of a Trace corresponds to a physical directory.
+message Trace {
+  // metadata is the identity of the trace resource.
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // tags are the specification of tags.
+  repeated TraceTagSpec tags = 2 [(validate.rules).repeated.min_items = 1];
+  // trace_id_tag_name is the name of the tag that stores the trace ID.
+  string trace_id_tag_name = 3 [(validate.rules).string.min_len = 1];
+  // timestamp_tag_name is the name of the tag that stores the timestamp.
+  string timestamp_tag_name = 4 [(validate.rules).string.min_len = 1];
+  // updated_at indicates when the trace resource is updated.
+  google.protobuf.Timestamp updated_at = 5;
+  // span_id_tag_name is the name of the tag that stores the span ID.
+  string span_id_tag_name = 6 [(validate.rules).string.min_len = 1];
+}
+
+message StreamRegistryServiceCreateRequest {
+  banyandb.database.v1.Stream stream = 1;
+}
+
+message StreamRegistryServiceCreateResponse {
+  int64 mod_revision = 1;
+}
+
+message StreamRegistryServiceUpdateRequest {
+  banyandb.database.v1.Stream stream = 1;
+}
+
+message StreamRegistryServiceUpdateResponse {
+  int64 mod_revision = 1;
+}
+
+message StreamRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message StreamRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message StreamRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message StreamRegistryServiceGetResponse {
+  banyandb.database.v1.Stream stream = 1;
+}
+
+message StreamRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message StreamRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_stream = 2;
+}
+
+message StreamRegistryServiceListRequest {
+  string group = 1;
+}
+
+message StreamRegistryServiceListResponse {
+  repeated banyandb.database.v1.Stream stream = 1;
+}
+
+service StreamRegistryService {
+  rpc Create(StreamRegistryServiceCreateRequest) returns 
(StreamRegistryServiceCreateResponse);
+  rpc Update(StreamRegistryServiceUpdateRequest) returns 
(StreamRegistryServiceUpdateResponse);
+  rpc Delete(StreamRegistryServiceDeleteRequest) returns 
(StreamRegistryServiceDeleteResponse);
+
+  rpc Get(StreamRegistryServiceGetRequest) returns 
(StreamRegistryServiceGetResponse);
+
+  rpc List(StreamRegistryServiceListRequest) returns 
(StreamRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(StreamRegistryServiceExistRequest) returns 
(StreamRegistryServiceExistResponse);
+}
+
+message IndexRuleBindingRegistryServiceCreateRequest {
+  banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
+}
+
+message IndexRuleBindingRegistryServiceCreateResponse {}
+
+message IndexRuleBindingRegistryServiceUpdateRequest {
+  banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
+}
+
+message IndexRuleBindingRegistryServiceUpdateResponse {}
+
+message IndexRuleBindingRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message IndexRuleBindingRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message IndexRuleBindingRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message IndexRuleBindingRegistryServiceGetResponse {
+  banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
+}
+
+message IndexRuleBindingRegistryServiceListRequest {
+  string group = 1;
+}
+
+message IndexRuleBindingRegistryServiceListResponse {
+  repeated banyandb.database.v1.IndexRuleBinding index_rule_binding = 1;
+}
+
+message IndexRuleBindingRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message IndexRuleBindingRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_index_rule_binding = 2;
+}
+
+service IndexRuleBindingRegistryService {
+  rpc Create(IndexRuleBindingRegistryServiceCreateRequest) returns 
(IndexRuleBindingRegistryServiceCreateResponse);
+  rpc Update(IndexRuleBindingRegistryServiceUpdateRequest) returns 
(IndexRuleBindingRegistryServiceUpdateResponse);
+  rpc Delete(IndexRuleBindingRegistryServiceDeleteRequest) returns 
(IndexRuleBindingRegistryServiceDeleteResponse);
+
+  rpc Get(IndexRuleBindingRegistryServiceGetRequest) returns 
(IndexRuleBindingRegistryServiceGetResponse);
+
+  rpc List(IndexRuleBindingRegistryServiceListRequest) returns 
(IndexRuleBindingRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(IndexRuleBindingRegistryServiceExistRequest) returns 
(IndexRuleBindingRegistryServiceExistResponse);
+}
+
+message IndexRuleRegistryServiceCreateRequest {
+  banyandb.database.v1.IndexRule index_rule = 1;
+}
+
+message IndexRuleRegistryServiceCreateResponse {}
+
+message IndexRuleRegistryServiceUpdateRequest {
+  banyandb.database.v1.IndexRule index_rule = 1;
+}
+
+message IndexRuleRegistryServiceUpdateResponse {}
+
+message IndexRuleRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message IndexRuleRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message IndexRuleRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message IndexRuleRegistryServiceGetResponse {
+  banyandb.database.v1.IndexRule index_rule = 1;
+}
+
+message IndexRuleRegistryServiceListRequest {
+  string group = 1;
+}
+
+message IndexRuleRegistryServiceListResponse {
+  repeated banyandb.database.v1.IndexRule index_rule = 1;
+}
+
+message IndexRuleRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message IndexRuleRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_index_rule = 2;
+}
+
+service IndexRuleRegistryService {
+  rpc Create(IndexRuleRegistryServiceCreateRequest) returns 
(IndexRuleRegistryServiceCreateResponse);
+  rpc Update(IndexRuleRegistryServiceUpdateRequest) returns 
(IndexRuleRegistryServiceUpdateResponse);
+  rpc Delete(IndexRuleRegistryServiceDeleteRequest) returns 
(IndexRuleRegistryServiceDeleteResponse);
+
+  rpc Get(IndexRuleRegistryServiceGetRequest) returns 
(IndexRuleRegistryServiceGetResponse);
+
+  rpc List(IndexRuleRegistryServiceListRequest) returns 
(IndexRuleRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(IndexRuleRegistryServiceExistRequest) returns 
(IndexRuleRegistryServiceExistResponse);
+}
+
+message MeasureRegistryServiceCreateRequest {
+  banyandb.database.v1.Measure measure = 1;
+}
+
+message MeasureRegistryServiceCreateResponse {
+  int64 mod_revision = 1;
+}
+
+message MeasureRegistryServiceUpdateRequest {
+  banyandb.database.v1.Measure measure = 1;
+}
+
+message MeasureRegistryServiceUpdateResponse {
+  int64 mod_revision = 1;
+}
+
+message MeasureRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message MeasureRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message MeasureRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message MeasureRegistryServiceGetResponse {
+  banyandb.database.v1.Measure measure = 1;
+}
+
+message MeasureRegistryServiceListRequest {
+  string group = 1;
+}
+
+message MeasureRegistryServiceListResponse {
+  repeated banyandb.database.v1.Measure measure = 1;
+}
+
+message MeasureRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message MeasureRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_measure = 2;
+}
+
+service MeasureRegistryService {
+  rpc Create(MeasureRegistryServiceCreateRequest) returns 
(MeasureRegistryServiceCreateResponse);
+  rpc Update(MeasureRegistryServiceUpdateRequest) returns 
(MeasureRegistryServiceUpdateResponse);
+  rpc Delete(MeasureRegistryServiceDeleteRequest) returns 
(MeasureRegistryServiceDeleteResponse);
+
+  rpc Get(MeasureRegistryServiceGetRequest) returns 
(MeasureRegistryServiceGetResponse);
+
+  rpc List(MeasureRegistryServiceListRequest) returns 
(MeasureRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(MeasureRegistryServiceExistRequest) returns 
(MeasureRegistryServiceExistResponse);
+}
+
+message GroupRegistryServiceCreateRequest {
+  banyandb.common.v1.Group group = 1;
+}
+
+message GroupRegistryServiceCreateResponse {}
+
+message GroupRegistryServiceUpdateRequest {
+  banyandb.common.v1.Group group = 1;
+}
+
+message GroupRegistryServiceUpdateResponse {}
+
+message GroupRegistryServiceDeleteRequest {
+  string group = 1;
+}
+
+message GroupRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message GroupRegistryServiceGetRequest {
+  string group = 1;
+}
+
+message GroupRegistryServiceGetResponse {
+  banyandb.common.v1.Group group = 1;
+}
+
+message GroupRegistryServiceListRequest {}
+
+message GroupRegistryServiceListResponse {
+  repeated banyandb.common.v1.Group group = 1;
+}
+
+message GroupRegistryServiceExistRequest {
+  string group = 1;
+}
+
+message GroupRegistryServiceExistResponse {
+  bool has_group = 1;
+}
+
+service GroupRegistryService {
+  rpc Create(GroupRegistryServiceCreateRequest) returns 
(GroupRegistryServiceCreateResponse);
+  rpc Update(GroupRegistryServiceUpdateRequest) returns 
(GroupRegistryServiceUpdateResponse);
+  rpc Delete(GroupRegistryServiceDeleteRequest) returns 
(GroupRegistryServiceDeleteResponse);
+
+  rpc Get(GroupRegistryServiceGetRequest) returns 
(GroupRegistryServiceGetResponse);
+
+  rpc List(GroupRegistryServiceListRequest) returns 
(GroupRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(GroupRegistryServiceExistRequest) returns 
(GroupRegistryServiceExistResponse);
+}
+
+message TopNAggregationRegistryServiceCreateRequest {
+  banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
+}
+
+message TopNAggregationRegistryServiceCreateResponse {}
+
+message TopNAggregationRegistryServiceUpdateRequest {
+  banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
+}
+
+message TopNAggregationRegistryServiceUpdateResponse {}
+
+message TopNAggregationRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message TopNAggregationRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message TopNAggregationRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message TopNAggregationRegistryServiceGetResponse {
+  banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
+}
+
+message TopNAggregationRegistryServiceListRequest {
+  string group = 1;
+}
+
+message TopNAggregationRegistryServiceListResponse {
+  repeated banyandb.database.v1.TopNAggregation top_n_aggregation = 1;
+}
+
+message TopNAggregationRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message TopNAggregationRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_top_n_aggregation = 2;
+}
+
+service TopNAggregationRegistryService {
+  rpc Create(TopNAggregationRegistryServiceCreateRequest) returns 
(TopNAggregationRegistryServiceCreateResponse);
+  rpc Update(TopNAggregationRegistryServiceUpdateRequest) returns 
(TopNAggregationRegistryServiceUpdateResponse);
+  rpc Delete(TopNAggregationRegistryServiceDeleteRequest) returns 
(TopNAggregationRegistryServiceDeleteResponse);
+  rpc Get(TopNAggregationRegistryServiceGetRequest) returns 
(TopNAggregationRegistryServiceGetResponse);
+  rpc List(TopNAggregationRegistryServiceListRequest) returns 
(TopNAggregationRegistryServiceListResponse);
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(TopNAggregationRegistryServiceExistRequest) returns 
(TopNAggregationRegistryServiceExistResponse);
+}
+
+message SnapshotRequest {
+  message Group {
+    common.v1.Catalog catalog = 1;
+    string group = 2;
+  }
+  repeated Group groups = 1;
+}
+
+message Snapshot {
+  common.v1.Catalog catalog = 1;
+  string name = 2;
+  string error = 3;
+}
+
+message SnapshotResponse {
+  repeated Snapshot snapshots = 1;
+}
+
+service SnapshotService {
+  rpc Snapshot(SnapshotRequest) returns (SnapshotResponse);
+}
+
+message PropertyRegistryServiceCreateRequest {
+  banyandb.database.v1.Property property = 1;
+}
+
+message PropertyRegistryServiceCreateResponse {
+  int64 mod_revision = 1;
+}
+
+message PropertyRegistryServiceUpdateRequest {
+  banyandb.database.v1.Property property = 1;
+}
+
+message PropertyRegistryServiceUpdateResponse {
+  int64 mod_revision = 1;
+}
+
+message PropertyRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message PropertyRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message PropertyRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message PropertyRegistryServiceGetResponse {
+  banyandb.database.v1.Property property = 1;
+}
+
+message PropertyRegistryServiceListRequest {
+  string group = 1;
+}
+
+message PropertyRegistryServiceListResponse {
+  repeated banyandb.database.v1.Property properties = 1;
+}
+
+message PropertyRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message PropertyRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_property = 2;
+}
+
+service PropertyRegistryService {
+  rpc Create(PropertyRegistryServiceCreateRequest) returns 
(PropertyRegistryServiceCreateResponse);
+  rpc Update(PropertyRegistryServiceUpdateRequest) returns 
(PropertyRegistryServiceUpdateResponse);
+  rpc Delete(PropertyRegistryServiceDeleteRequest) returns 
(PropertyRegistryServiceDeleteResponse);
+
+  rpc Get(PropertyRegistryServiceGetRequest) returns 
(PropertyRegistryServiceGetResponse);
+
+  rpc List(PropertyRegistryServiceListRequest) returns 
(PropertyRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(PropertyRegistryServiceExistRequest) returns 
(PropertyRegistryServiceExistResponse);
+}
+
+message TraceRegistryServiceCreateRequest {
+  banyandb.database.v1.Trace trace = 1;
+}
+
+message TraceRegistryServiceCreateResponse {
+  int64 mod_revision = 1;
+}
+
+message TraceRegistryServiceUpdateRequest {
+  banyandb.database.v1.Trace trace = 1;
+}
+
+message TraceRegistryServiceUpdateResponse {
+  int64 mod_revision = 1;
+}
+
+message TraceRegistryServiceDeleteRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message TraceRegistryServiceDeleteResponse {
+  bool deleted = 1;
+}
+
+message TraceRegistryServiceGetRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message TraceRegistryServiceGetResponse {
+  banyandb.database.v1.Trace trace = 1;
+}
+
+message TraceRegistryServiceExistRequest {
+  banyandb.common.v1.Metadata metadata = 1;
+}
+
+message TraceRegistryServiceExistResponse {
+  bool has_group = 1;
+  bool has_trace = 2;
+}
+
+message TraceRegistryServiceListRequest {
+  string group = 1;
+}
+
+message TraceRegistryServiceListResponse {
+  repeated banyandb.database.v1.Trace trace = 1;
+}
+
+service TraceRegistryService {
+  rpc Create(TraceRegistryServiceCreateRequest) returns 
(TraceRegistryServiceCreateResponse);
+  rpc Update(TraceRegistryServiceUpdateRequest) returns 
(TraceRegistryServiceUpdateResponse);
+  rpc Delete(TraceRegistryServiceDeleteRequest) returns 
(TraceRegistryServiceDeleteResponse);
+
+  rpc Get(TraceRegistryServiceGetRequest) returns 
(TraceRegistryServiceGetResponse);
+
+  rpc List(TraceRegistryServiceListRequest) returns 
(TraceRegistryServiceListResponse);
+
+  // Exist doesn't expose an HTTP endpoint. Please use HEAD method to touch 
Get instead
+  rpc Exist(TraceRegistryServiceExistRequest) returns 
(TraceRegistryServiceExistResponse);
+}
diff --git a/proto/banyandb/v1/banyandb-measure.proto 
b/proto/banyandb/v1/banyandb-measure.proto
new file mode 100644
index 0000000..f8c4f91
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-measure.proto
@@ -0,0 +1,219 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.measure.v1";
+
+package banyandb.measure.v1;
+
+import "google/protobuf/timestamp.proto";
+import "validate/validate.proto";
+import "banyandb/v1/banyandb-common.proto";
+import "banyandb/v1/banyandb-model.proto";
+
+// DataPoint is stored in Measures
+message DataPoint {
+  // timestamp is in the timeunit of milliseconds.
+  google.protobuf.Timestamp timestamp = 1;
+  // tag_families contains tags selected in the projection
+  repeated model.v1.TagFamily tag_families = 2;
+  message Field {
+    string name = 1;
+    model.v1.FieldValue value = 2;
+  }
+  // fields contains fields selected in the projection
+  repeated Field fields = 3;
+  // sid is the series id of the data point
+  uint64 sid = 4;
+  // version is the version of the data point in a series
+  // sid, timestamp and version are used to identify a data point
+  int64 version = 5;
+}
+
+// QueryResponse is the response for a query to the Query module.
+message QueryResponse {
+  // data_points are the actual data returned
+  repeated DataPoint data_points = 1;
+  // trace contains the trace information of the query when trace is enabled
+  common.v1.Trace trace = 2;
+}
+
+// QueryRequest is the request contract for query.
+message QueryRequest {
+  // groups indicate where the data points are stored.
+  repeated string groups = 1 [(validate.rules).repeated.min_items = 1];
+  // name is the identity of a measure.
+  string name = 2 [(validate.rules).string.min_len = 1];
+  // time_range is a range query with begin/end time of entities in the 
timeunit of milliseconds.
+  model.v1.TimeRange time_range = 3 [(validate.rules).message.required = true];
+  // tag_families are indexed.
+  model.v1.Criteria criteria = 4;
+  // tag_projection can be used to select tags of the data points in the 
response
+  model.v1.TagProjection tag_projection = 5;
+  message FieldProjection {
+    repeated string names = 1;
+  }
+  // field_projection can be used to select fields of the data points in the 
response
+  FieldProjection field_projection = 6;
+  message GroupBy {
+    // tag_projection must be a subset of the tag_projection of QueryRequest
+    model.v1.TagProjection tag_projection = 1;
+    // field_name must be one of fields indicated by field_projection
+    string field_name = 2;
+  }
+  // group_by groups data points based on their field value for a specific tag 
and use field_name as the projection name
+  GroupBy group_by = 7;
+  message Aggregation {
+    model.v1.AggregationFunction function = 1;
+    // field_name must be one of files indicated by the field_projection
+    string field_name = 2;
+  }
+  // agg aggregates data points based on a field
+  Aggregation agg = 8;
+  message Top {
+    // number set the how many items should be returned
+    int32 number = 1;
+    // field_name must be one of files indicated by the field_projection
+    string field_name = 2;
+    // field_value_sort indicates how to sort fields
+    // ASC: bottomN
+    // DESC: topN
+    // UNSPECIFIED: topN
+    model.v1.Sort field_value_sort = 3;
+  }
+  // top limits the result based on a particular field.
+  // If order_by is specified, top sorts the dataset based on order_by's output
+  Top top = 9;
+  // offset is used to support pagination, together with the following limit.
+  // If top is specified, offset processes the dataset based on top's output
+  uint32 offset = 10;
+  // limit is used to impose a boundary on the number of records being 
returned.
+  // If top is specified, limit processes the dataset based on top's output
+  uint32 limit = 11;
+  // order_by is given to specify the sort for a tag.
+  model.v1.QueryOrder order_by = 12;
+  // trace is used to enable trace for the query
+  bool trace = 13;
+  // stages is used to specify the stage of the data points in the lifecycle
+  repeated string stages = 14;
+  // rewriteAggTopNResult will rewrite agg result to raw data
+  bool rewrite_agg_top_n_result = 15;
+}
+
+
+
+service MeasureService {
+  rpc Query(QueryRequest) returns (QueryResponse);
+  rpc Write(stream WriteRequest) returns (stream WriteResponse);
+  rpc TopN(TopNRequest) returns (TopNResponse);
+}
+
+// TopNList contains a series of topN items
+message TopNList {
+  // timestamp is in the timeunit of milliseconds.
+  google.protobuf.Timestamp timestamp = 1;
+  message Item {
+    repeated model.v1.Tag entity = 1;
+    model.v1.FieldValue value = 2;
+  }
+  // items contains top-n items in a list
+  repeated Item items = 2;
+}
+
+// TopNResponse is the response for a query to the Query module.
+message TopNResponse {
+  // lists contain a series topN lists ranked by timestamp
+  // if agg_func in query request is specified, lists' size should be one.
+  repeated TopNList lists = 1;
+  // trace contains the trace information of the query when trace is enabled
+  common.v1.Trace trace = 2;
+}
+
+// TopNRequest is the request contract for query.
+message TopNRequest {
+  // groups indicate where the data points are stored.
+  repeated string groups = 1 [(validate.rules).repeated.min_items = 1];
+  // name is the identity of a measure.
+  string name = 2 [(validate.rules).string.min_len = 1];
+  // time_range is a range query with begin/end time of entities in the 
timeunit of milliseconds.
+  model.v1.TimeRange time_range = 3 [(validate.rules).message.required = true];
+  // top_n set the how many items should be returned in each list.
+  int32 top_n = 4 [(validate.rules).int32.gt = 0];
+  // agg aggregates lists grouped by field names in the time_range
+  model.v1.AggregationFunction agg = 5;
+  // criteria select counters. Only equals are acceptable.
+  repeated model.v1.Condition conditions = 6;
+  // field_value_sort indicates how to sort fields
+  model.v1.Sort field_value_sort = 7;
+  // trace is used to enable trace for the query
+  bool trace = 8;
+  // stages is used to specify the stage of the data points in the lifecycle
+  repeated string stages = 9;
+}
+
+// TagFamilySpec defines the specification of a tag family.
+message TagFamilySpec {
+  // name of the tag family
+  string name = 1;
+  // names of tags in the tag family
+  repeated string tag_names = 2;
+}
+
+// DataPointSpec defines the specification of a data point.
+message DataPointSpec {
+  // the tag family specification
+  repeated TagFamilySpec tag_family_spec = 1;
+  // the field names
+  repeated string field_names = 2;
+}
+
+// DataPointValue is the data point for writing. It only contains values.
+message DataPointValue {
+  // timestamp is in the timeunit of milliseconds.
+  google.protobuf.Timestamp timestamp = 1 [(validate.rules).timestamp.required 
= true];
+  // the order of tag_families' items match DataPointSpec
+  repeated model.v1.TagFamilyForWrite tag_families = 2 
[(validate.rules).repeated.min_items = 1];
+  // the order of fields match DataPointSpec
+  repeated model.v1.FieldValue fields = 3;
+  // the version of the data point
+  int64 version = 4;
+}
+
+// WriteRequest is the request contract for write
+message WriteRequest {
+  // the metadata is required only for the first request of gRPC stream.
+  common.v1.Metadata metadata = 1;
+  // the data_point is required.
+  DataPointValue data_point = 2 [(validate.rules).message.required = true];
+  // the message_id is required.
+  uint64 message_id = 3 [(validate.rules).uint64.gt = 0];
+  // the data point specification.
+  // If this is not set with the indicated metadata, use the schema definition.
+  // If this is not set, use the existing spec declaration from previous 
requests in the current gRPC stream.
+  DataPointSpec data_point_spec = 4;
+}
+
+// WriteResponse is the response contract for write
+message WriteResponse {
+  // the message_id from request.
+  uint64 message_id = 1 [(validate.rules).uint64.gt = 0];
+  // status indicates the request processing result
+  string status = 2;
+  // the metadata from request when request fails
+  common.v1.Metadata metadata = 3;
+}
diff --git a/proto/banyandb/v1/banyandb-model.proto 
b/proto/banyandb/v1/banyandb-model.proto
new file mode 100644
index 0000000..e6066fc
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-model.proto
@@ -0,0 +1,198 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.model.v1";
+
+package banyandb.model.v1;
+
+import "google/protobuf/struct.proto";
+import "google/protobuf/timestamp.proto";
+
+message Str {
+  string value = 1;
+}
+
+message Int {
+  int64 value = 1;
+}
+
+message Float {
+  double value = 1;
+}
+
+message StrArray {
+  repeated string value = 1;
+}
+
+message IntArray {
+  repeated int64 value = 1;
+}
+
+message TagValue {
+  oneof value {
+    google.protobuf.NullValue null = 1;
+    Str str = 2;
+    StrArray str_array = 3;
+    Int int = 4;
+    IntArray int_array = 5;
+    bytes binary_data = 6;
+    google.protobuf.Timestamp timestamp = 7;
+  }
+}
+
+message TagFamilyForWrite {
+  repeated TagValue tags = 1;
+}
+
+message FieldValue {
+  oneof value {
+    google.protobuf.NullValue null = 1;
+    model.v1.Str str = 2;
+    model.v1.Int int = 3;
+    bytes binary_data = 4;
+    model.v1.Float float = 5;
+  }
+}
+
+enum AggregationFunction {
+  AGGREGATION_FUNCTION_UNSPECIFIED = 0;
+  AGGREGATION_FUNCTION_MEAN = 1;
+  AGGREGATION_FUNCTION_MAX = 2;
+  AGGREGATION_FUNCTION_MIN = 3;
+  AGGREGATION_FUNCTION_COUNT = 4;
+  AGGREGATION_FUNCTION_SUM = 5;
+}
+
+// Pair is the building block of a record which is equivalent to a key-value 
pair.
+// In the context of Trace, it could be metadata of a trace such as 
service_name, service_instance, etc.
+// Besides, other tags are organized in key-value pair in the underlying 
storage layer.
+// One should notice that the values can be a multi-value.
+message Tag {
+  string key = 1;
+  TagValue value = 2;
+}
+
+message TagFamily {
+  string name = 1;
+  repeated Tag tags = 2;
+}
+
+// Condition consists of the query condition with a single binary operator to 
be imposed
+// For 1:1 BinaryOp, values in condition must be an array with length = 1,
+// while for 1:N BinaryOp, values can be an array with length >= 1.
+message Condition {
+  // BinaryOp specifies the operation imposed to the given query condition
+  // For EQ, NE, LT, GT, LE and GE, only one operand should be given, i.e. 
one-to-one relationship.
+  // HAVING and NOT_HAVING allow multi-value to be the operand such as 
array/vector, i.e. one-to-many relationship.
+  // For example, "keyA" contains "valueA" **and** "valueB"
+  // MATCH performances a full-text search if the tag is analyzed.
+  // The string value applies to the same analyzer as the tag, but string 
array value does not.
+  // Each item in a string array is seen as a token instead of a query 
expression.
+  enum BinaryOp {
+    BINARY_OP_UNSPECIFIED = 0;
+    BINARY_OP_EQ = 1;
+    BINARY_OP_NE = 2;
+    BINARY_OP_LT = 3;
+    BINARY_OP_GT = 4;
+    BINARY_OP_LE = 5;
+    BINARY_OP_GE = 6;
+    BINARY_OP_HAVING = 7;
+    BINARY_OP_NOT_HAVING = 8;
+    BINARY_OP_IN = 9;
+    BINARY_OP_NOT_IN = 10;
+    BINARY_OP_MATCH = 11;
+  }
+  string name = 1;
+  BinaryOp op = 2;
+  TagValue value = 3;
+  message MatchOption {
+    string analyzer = 1;
+    enum Operator {
+      OPERATOR_UNSPECIFIED = 0;
+      OPERATOR_AND = 1;
+      OPERATOR_OR = 2;
+    }
+    Operator operator = 2;
+  }
+  MatchOption match_option = 4;
+}
+
+// tag_families are indexed.
+message Criteria {
+  oneof exp {
+    LogicalExpression le = 1;
+    Condition condition = 2;
+  }
+}
+
+// LogicalExpression supports logical operation
+message LogicalExpression {
+  enum LogicalOp {
+    LOGICAL_OP_UNSPECIFIED = 0;
+    LOGICAL_OP_AND = 1;
+    LOGICAL_OP_OR = 2;
+  }
+  // op is a logical operation
+  LogicalOp op = 1;
+  Criteria left = 2;
+  Criteria right = 3;
+}
+
+enum Sort {
+  SORT_UNSPECIFIED = 0;
+  SORT_DESC = 1;
+  SORT_ASC = 2;
+}
+
+// QueryOrder means a Sort operation to be done for a given index rule.
+// The index_rule_name refers to the name of a index rule bound to the subject.
+message QueryOrder {
+  string index_rule_name = 1;
+  Sort sort = 2;
+}
+
+// TagProjection is used to select the names of keys to be returned.
+message TagProjection {
+  message TagFamily {
+    string name = 1;
+    repeated string tags = 2;
+  }
+  repeated TagFamily tag_families = 1;
+}
+
+// TimeRange is a range query for uint64,
+// the range here follows left-inclusive and right-exclusive rule, i.e. 
[begin, end) if both edges exist
+message TimeRange {
+  google.protobuf.Timestamp begin = 1;
+  google.protobuf.Timestamp end = 2;
+}
+
+// Status is the response status for write
+enum Status {
+  STATUS_UNSPECIFIED = 0;
+  STATUS_SUCCEED = 1;
+  STATUS_INVALID_TIMESTAMP = 2;
+  STATUS_NOT_FOUND = 3;
+  STATUS_EXPIRED_SCHEMA = 4;
+  STATUS_INTERNAL_ERROR = 5;
+  STATUS_DISK_FULL = 6;
+  STATUS_VERSION_UNSUPPORTED = 7; // Client version not supported
+  STATUS_VERSION_DEPRECATED = 8; // Client version deprecated but still 
supported
+  STATUS_METADATA_REQUIRED = 9; // Metadata is required for the first request
+}
diff --git a/proto/banyandb/v1/banyandb-property.proto 
b/proto/banyandb/v1/banyandb-property.proto
new file mode 100644
index 0000000..b602ef5
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-property.proto
@@ -0,0 +1,112 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.property.v1";
+
+package banyandb.property.v1;
+
+import "google/protobuf/timestamp.proto";
+import "validate/validate.proto";
+import "banyandb/v1/banyandb-common.proto";
+import "banyandb/v1/banyandb-model.proto";
+
+// Property stores the user defined data
+message Property {
+  // metadata is the identity of a property
+  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // id is the identity of a property
+  string id = 2 [(validate.rules).string.min_len = 1];
+  // tag stores the content of a property
+  repeated model.v1.Tag tags = 3 [(validate.rules).repeated.min_items = 1];
+  // updated_at indicates when the property is updated
+  google.protobuf.Timestamp updated_at = 4;
+}
+
+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 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 {
+  // groups indicate where the data points are stored.
+  string group = 1 [(validate.rules).string.min_len = 1];
+  // name is the identity of a property.
+  string name = 2 [(validate.rules).string.min_len = 1];
+  // id is the identity of item in the property.
+  string id = 3;
+}
+
+message DeleteResponse {
+  bool deleted = 1;
+}
+
+message QueryOrder {
+  // tag_name is the name of the tag to be ordered.
+  string tag_name = 1;
+  // order_by is given to specify the sort for a tag.
+  model.v1.Sort sort = 12;
+}
+
+// QueryRequest is the request contract for query.
+message QueryRequest {
+  // groups indicate where the data points are stored.
+  repeated string groups = 1 [(validate.rules).repeated.min_items = 1];
+  // name is created when it receives the first property
+  string name = 2;
+  // ids is the identities of properties
+  repeated string ids = 3;
+  // criteria is used to filter properties based on tags
+  model.v1.Criteria criteria = 4;
+  // tag_projection can be used to select tags of the data points in the 
response
+  repeated string tag_projection = 5;
+  uint32 limit = 6;
+  // trace is used to enable trace for the query
+  bool trace = 7;
+  // order_by is given to specify the sort for a tag.
+  QueryOrder order_by = 8;
+}
+
+// QueryResponse is the response for a query to the Query module.
+message QueryResponse {
+  // properties are the actual data returned
+  repeated banyandb.property.v1.Property properties = 1;
+  // trace contains the trace information of the query when trace is enabled
+  common.v1.Trace trace = 2;
+}
+
+service PropertyService {
+  // Apply creates a property if it's absent, or update a existed one based on 
a strategy.
+  rpc Apply(ApplyRequest) returns (ApplyResponse);
+  rpc Delete(DeleteRequest) returns (DeleteResponse);
+
+  rpc Query(QueryRequest) returns (QueryResponse);
+}
diff --git a/proto/banyandb/v1/banyandb-stream.proto 
b/proto/banyandb/v1/banyandb-stream.proto
new file mode 100644
index 0000000..642b189
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-stream.proto
@@ -0,0 +1,130 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.stream.v1";
+
+package banyandb.stream.v1;
+
+import "google/protobuf/timestamp.proto";
+import "validate/validate.proto";
+import "banyandb/v1/banyandb-common.proto";
+import "banyandb/v1/banyandb-model.proto";
+
+// Element represents
+// (stream context) a Span defined in Google Dapper paper or equivalently a 
Segment in Skywalking.
+// (Log context) a log
+message Element {
+  // element_id could be span_id of a Span or segment_id of a Segment in the 
context of stream
+  string element_id = 1;
+  // timestamp represents a millisecond
+  // 1) either the start time of a Span/Segment,
+  // 2) or the timestamp of a log
+  google.protobuf.Timestamp timestamp = 2;
+  // fields contains all indexed Field. Some typical names,
+  // - stream_id
+  // - duration
+  // - service_name
+  // - service_instance_id
+  // - end_time_milliseconds
+  repeated model.v1.TagFamily tag_families = 3;
+}
+
+// QueryResponse is the response for a query to the Query module.
+message QueryResponse {
+  // elements are the actual data returned
+  repeated Element elements = 1;
+  // trace contains the trace information of the query when trace is enabled
+  common.v1.Trace trace = 2;
+}
+
+// QueryRequest is the request contract for query.
+message QueryRequest {
+  // groups indicate where the elements are stored.
+  repeated string groups = 1 [(validate.rules).repeated.min_items = 1];
+  // name is the identity of a stream.
+  string name = 2 [(validate.rules).string.min_len = 1];
+  // time_range is a range query with begin/end time of entities in the 
timeunit of milliseconds.
+  // In the context of stream, it represents the range of the `startTime` for 
spans/segments,
+  // while in the context of Log, it means the range of the timestamp(s) for 
logs.
+  // it is always recommended to specify time range for performance reason
+  model.v1.TimeRange time_range = 3;
+  // offset is used to support pagination, together with the following limit
+  uint32 offset = 4;
+  // limit is used to impose a boundary on the number of records being returned
+  uint32 limit = 5;
+  // order_by is given to specify the sort for a field. So far, only fields in 
the type of Integer are supported
+  model.v1.QueryOrder order_by = 6;
+  // tag_families are indexed.
+  model.v1.Criteria criteria = 7;
+  // projection can be used to select the key names of the element in the 
response
+  model.v1.TagProjection projection = 8 [(validate.rules).message.required = 
true];
+  // trace is used to enable trace for the query
+  bool trace = 9;
+  // stage is used to specify the stage of the query in the lifecycle
+  repeated string stages = 10;
+}
+
+
+
+service StreamService {
+  rpc Query(QueryRequest) returns (QueryResponse);
+  rpc Write(stream WriteRequest) returns (stream WriteResponse);
+
+}
+
+// TagFamilySpec defines the specification of a tag family.
+message TagFamilySpec {
+  // name of the tag family
+  string name = 1;
+  // names of tags in the tag family
+  repeated string tag_names = 2;
+}
+
+message ElementValue {
+  // element_id could be span_id of a Span or segment_id of a Segment in the 
context of stream
+  string element_id = 1;
+  // timestamp is in the timeunit of milliseconds. It represents
+  // 1) either the start time of a Span/Segment,
+  // 2) or the timestamp of a log
+  google.protobuf.Timestamp timestamp = 2;
+  // the order of tag_families' items match TagFamilySpec
+  repeated model.v1.TagFamilyForWrite tag_families = 3;
+}
+
+message WriteRequest {
+  // the metadata is required only for the first request of gRPC stream.
+  common.v1.Metadata metadata = 1;
+  // the element is required.
+  ElementValue element = 2 [(validate.rules).message.required = true];
+  // the message_id is required.
+  uint64 message_id = 3 [(validate.rules).uint64.gt = 0];
+  // the tag family specification.
+  // If this is not set with the indicated metadata, use the schema definition.
+  // If this is not set, use the existing spec declaration from previous 
requests in the current gRPC stream.
+  repeated TagFamilySpec tag_family_spec = 4;
+}
+
+message WriteResponse {
+  // the message_id from request.
+  uint64 message_id = 1 [(validate.rules).uint64.gt = 0];
+  // status indicates the request processing result
+  string status = 2;
+  // the metadata from request when request fails
+  common.v1.Metadata metadata = 3;
+}
diff --git a/proto/banyandb/v1/banyandb-trace.proto 
b/proto/banyandb/v1/banyandb-trace.proto
new file mode 100644
index 0000000..b822b59
--- /dev/null
+++ b/proto/banyandb/v1/banyandb-trace.proto
@@ -0,0 +1,122 @@
+// 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.
+
+syntax = "proto3";
+
+option java_package = "org.apache.skywalking.banyandb.trace.v1";
+
+package banyandb.trace.v1;
+
+import "validate/validate.proto";
+import "banyandb/v1/banyandb-common.proto";
+import "banyandb/v1/banyandb-model.proto";
+
+// Span is a single operation within a trace.
+message Span {
+  // tags are the indexed tags of the span.
+  repeated model.v1.Tag tags = 1;
+  // span is the raw span data.
+  bytes span = 2;
+  // span_id is the unique identifier of the span.
+  string span_id = 3;
+}
+
+// InternalTrace is the trace that is used for internal use.
+message InternalTrace {
+  // spans are the spans that belong to this trace.
+  repeated Span spans = 1;
+  // trace_id is the unique identifier of the trace.
+  string trace_id = 2;
+  // key is used for sorting.
+  int64 key = 3;
+}
+
+// Trace contains all spans that belong to a single trace ID.
+message Trace {
+  // spans is the list of spans that belong to this trace.
+  repeated Span spans = 1;
+  // trace_id is the unique identifier of the trace.
+  string trace_id = 2;
+}
+
+// QueryResponse is the response of a query.
+message QueryResponse {
+  // traces is a list of traces that match the query, with spans grouped by 
trace ID.
+  repeated Trace traces = 1;
+  // trace_query_result contains the trace of the query execution if tracing 
is enabled.
+  common.v1.Trace trace_query_result = 2;
+}
+
+// InternalQueryResponse is the response of an internal query.
+message InternalQueryResponse {
+  // internal_traces is a list of internal traces that match the query.
+  repeated InternalTrace internal_traces = 1;
+  // trace_query_result contains the trace of the query execution if tracing 
is enabled.
+  common.v1.Trace trace_query_result = 2;
+}
+
+// QueryRequest is the request contract for query.
+message QueryRequest {
+  // groups indicates the physical data location.
+  repeated string groups = 1 [(validate.rules).repeated.min_items = 1];
+  // name is the identity of a trace.
+  string name = 2 [(validate.rules).string.min_len = 1];
+  // time_range is a range query with begin/end time of entities in the 
timeunit of milliseconds.
+  // In the context of trace, it represents the range of the `startTime` for 
spans/segments,
+  // it is always recommended to specify time range for performance reason
+  model.v1.TimeRange time_range = 3;
+  // offset is used to support pagination, together with the following limit
+  uint32 offset = 4;
+  // limit is used to impose a boundary on the number of spans being returned
+  uint32 limit = 5;
+  // order_by is given to specify the sort for a tag. So far, only tags in the 
type of Integer are supported
+  model.v1.QueryOrder order_by = 6;
+  // criteria is the filter criteria.
+  model.v1.Criteria criteria = 7;
+  // projection can be used to select the names of the tags in the response
+  repeated string tag_projection = 8;
+  // trace is used to enable trace for the query
+  bool trace = 9;
+  // stage is used to specify the stage of the query in the lifecycle
+  repeated string stages = 10;
+}
+
+
+
+service TraceService {
+  rpc Query(QueryRequest) returns (QueryResponse);
+  rpc Write(stream WriteRequest) returns (stream WriteResponse);
+
+}
+
+message TagSpec {
+  repeated string tag_names = 1;
+}
+
+message WriteRequest {
+  common.v1.Metadata metadata = 1;
+  repeated model.v1.TagValue tags = 2 [(validate.rules).repeated.min_items = 
1];
+  bytes span = 3;
+  uint64 version = 4 [(validate.rules).uint64.gt = 0];
+  TagSpec tag_spec = 5;
+}
+
+message WriteResponse {
+  common.v1.Metadata metadata = 1;
+  uint64 version = 2;
+  string status = 3;
+}

Reply via email to