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 395bbf2f Support querying multi-groups in a single request  (#438)
395bbf2f is described below

commit 395bbf2ff70c4c94b60ea610e9a5176545c4ae5b
Author: Gao Hongtao <[email protected]>
AuthorDate: Thu Apr 25 22:10:49 2024 +0800

    Support querying multi-groups in a single request  (#438)
---
 .github/workflows/e2e.storage.yml                  |   2 +-
 CHANGES.md                                         |   1 +
 api/proto/banyandb/measure/v1/query.proto          |   9 +-
 api/proto/banyandb/measure/v1/rpc.proto            |   6 +-
 api/proto/banyandb/measure/v1/topn.proto           |  17 +--
 api/proto/banyandb/stream/v1/query.proto           |  19 ++--
 api/proto/banyandb/stream/v1/rpc.proto             |   4 +-
 banyand/dquery/measure.go                          |  13 ++-
 banyand/dquery/stream.go                           |  12 ++-
 banyand/dquery/topn.go                             |   4 +-
 banyand/internal/storage/index.go                  |   2 +-
 banyand/internal/storage/index_test.go             | 120 ++++++++++++++++-----
 banyand/measure/query.go                           |   4 +-
 banyand/query/processor.go                         |  24 ++++-
 banyand/query/processor_topn.go                    |  13 ++-
 banyand/stream/query.go                            |   8 +-
 bydbctl/internal/cmd/group.go                      |   2 +-
 bydbctl/internal/cmd/measure_test.go               |  10 +-
 bydbctl/internal/cmd/rest.go                       |  43 +++++---
 bydbctl/internal/cmd/stream_test.go                |  10 +-
 docs/api-reference.md                              |   9 +-
 .../logical/measure/measure_plan_distributed.go    |   3 +-
 .../logical/stream/stream_plan_distributed.go      |   3 +-
 scripts/build/version.mk                           |   8 +-
 test/cases/measure/data/input/all.yaml             |   5 +-
 test/cases/measure/data/input/all_latency.yaml     |   5 +-
 test/cases/measure/data/input/all_only_fields.yaml |   5 +-
 test/cases/measure/data/input/bottom.yaml          |   5 +-
 test/cases/measure/data/input/entity.yaml          |   5 +-
 test/cases/measure/data/input/entity_in.yaml       |   5 +-
 test/cases/measure/data/input/entity_service.yaml  |   5 +-
 test/cases/measure/data/input/err_invalid_le.yaml  |   5 +-
 test/cases/measure/data/input/float.yaml           |   5 +-
 test/cases/measure/data/input/float_agg_min.yaml   |   7 +-
 test/cases/measure/data/input/group_max.yaml       |   5 +-
 test/cases/measure/data/input/group_no_field.yaml  |   5 +-
 test/cases/measure/data/input/in.yaml              |   5 +-
 test/cases/measure/data/input/limit.yaml           |   5 +-
 test/cases/measure/data/input/linked_or.yaml       |   5 +-
 test/cases/measure/data/input/match_node.yaml      |   5 +-
 test/cases/measure/data/input/match_nodes.yaml     |   5 +-
 test/cases/measure/data/input/no_field.yaml        |   5 +-
 test/cases/measure/data/input/order_asc.yaml       |   5 +-
 test/cases/measure/data/input/order_desc.yaml      |   5 +-
 test/cases/measure/data/input/order_tag_asc.yaml   |   5 +-
 test/cases/measure/data/input/order_tag_desc.yaml  |   5 +-
 test/cases/measure/data/input/tag_filter.yaml      |   5 +-
 test/cases/measure/data/input/tag_filter_int.yaml  |   5 +-
 .../measure/data/input/tag_filter_unknown.yaml     |   5 +-
 test/cases/measure/data/input/top.yaml             |   5 +-
 test/cases/stream/data/input/all.yaml              |   7 +-
 test/cases/stream/data/input/filter_tag.yaml       |   5 +-
 test/cases/stream/data/input/filter_tag_empty.yaml |   5 +-
 test/cases/stream/data/input/global_index.yaml     |   5 +-
 test/cases/stream/data/input/global_indices.yaml   |   5 +-
 test/cases/stream/data/input/having.yaml           |   7 +-
 .../stream/data/input/having_non_indexed.yaml      |   7 +-
 .../stream/data/input/having_non_indexed_arr.yaml  |   7 +-
 test/cases/stream/data/input/indexed_only.yaml     |   7 +-
 test/cases/stream/data/input/less.yaml             |   7 +-
 test/cases/stream/data/input/less_eq.yaml          |   7 +-
 test/cases/stream/data/input/limit.yaml            |   7 +-
 test/cases/stream/data/input/logical.yaml          |   5 +-
 test/cases/stream/data/input/offset.yaml           |   7 +-
 test/cases/stream/data/input/search.yaml           |   7 +-
 test/cases/stream/data/input/sort_desc.yaml        |   7 +-
 test/cases/stream/data/input/sort_filter.yaml      |   7 +-
 test/cases/topn/data/input/aggr_desc.yaml          |   5 +-
 .../cases/topn/data/input/condition_aggr_desc.yaml |   5 +-
 test/cases/topn/data/input/null_group.yaml         |   5 +-
 test/e2e-v2/cases/cluster/storage-cases.yaml       |  84 ++++++++-------
 .../expected/metrics-has-value-percentile.yml      |  82 ++++----------
 .../cases/storage/expected/metrics-has-value.yml   |  26 +++--
 .../storage/expected/metrics-top-endpoint.yml}     |   8 +-
 test/e2e-v2/cases/storage/storage-cases.yaml       |  84 ++++++++-------
 test/e2e-v2/script/env                             |   2 +-
 test/integration/load/load_suite_test.go           |   7 +-
 test/stress/classic/env                            |   2 +-
 test/stress/classic/env.dev                        |   2 +-
 test/stress/trace/env                              |   2 +-
 ui/src/components/Read/index.vue                   |  12 +--
 81 files changed, 484 insertions(+), 429 deletions(-)

diff --git a/.github/workflows/e2e.storage.yml 
b/.github/workflows/e2e.storage.yml
index eb9bcebf..6bc171e9 100644
--- a/.github/workflows/e2e.storage.yml
+++ b/.github/workflows/e2e.storage.yml
@@ -93,7 +93,7 @@ jobs:
           make docker.build || make docker.build
           docker image ls
       - name: ${{ matrix.test.name }}
-        uses: 
apache/skywalking-infra-e2e@c7494f913e0b10d01884c840ec3fb1b0a844bcbe
+        uses: 
apache/skywalking-infra-e2e@1485ae03f0ad90496ad7626a5ae4a6a73a1f6296
         with:
           e2e-file: $GITHUB_WORKSPACE/${{ matrix.test.config }}
 
diff --git a/CHANGES.md b/CHANGES.md
index 6e8f3746..5dc02e8e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -25,6 +25,7 @@ Release Notes.
 - Remove "TREE" index type. The "TREE" index type is merged into "INVERTED" 
index type.
 - Remove "Location" field on IndexRule. Currently, the location of index is in 
a segment.
 - Remove "BlockInterval" from Group. The block size is determined by the part.
+- Support querying multiple groups in one request.
 
 ### Bugs
 
diff --git a/api/proto/banyandb/measure/v1/query.proto 
b/api/proto/banyandb/measure/v1/query.proto
index 8350ba86..9e07aea6 100644
--- a/api/proto/banyandb/measure/v1/query.proto
+++ b/api/proto/banyandb/measure/v1/query.proto
@@ -19,7 +19,6 @@ syntax = "proto3";
 
 package banyandb.measure.v1;
 
-import "banyandb/common/v1/common.proto";
 import "banyandb/model/v1/common.proto";
 import "banyandb/model/v1/query.proto";
 import "google/protobuf/timestamp.proto";
@@ -50,10 +49,12 @@ message QueryResponse {
 
 // QueryRequest is the request contract for query.
 message QueryRequest {
-  // metadata is required
-  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // 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 = 2 [(validate.rules).message.required = true];
+  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
diff --git a/api/proto/banyandb/measure/v1/rpc.proto 
b/api/proto/banyandb/measure/v1/rpc.proto
index 0347029e..31ad2f28 100644
--- a/api/proto/banyandb/measure/v1/rpc.proto
+++ b/api/proto/banyandb/measure/v1/rpc.proto
@@ -30,13 +30,13 @@ option java_package = 
"org.apache.skywalking.banyandb.measure.v1";
 option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = 
{base_path: "/api"};
 
 service MeasureService {
-  rpc Query(banyandb.measure.v1.QueryRequest) returns 
(banyandb.measure.v1.QueryResponse) {
+  rpc Query(QueryRequest) returns (QueryResponse) {
     option (google.api.http) = {
       post: "/v1/measure/data"
       body: "*"
     };
   }
 
-  rpc Write(stream banyandb.measure.v1.WriteRequest) returns (stream 
banyandb.measure.v1.WriteResponse);
-  rpc TopN(banyandb.measure.v1.TopNRequest) returns 
(banyandb.measure.v1.TopNResponse);
+  rpc Write(stream WriteRequest) returns (stream WriteResponse);
+  rpc TopN(TopNRequest) returns (TopNResponse);
 }
diff --git a/api/proto/banyandb/measure/v1/topn.proto 
b/api/proto/banyandb/measure/v1/topn.proto
index f7b426a1..f6366b1d 100644
--- a/api/proto/banyandb/measure/v1/topn.proto
+++ b/api/proto/banyandb/measure/v1/topn.proto
@@ -19,7 +19,6 @@ syntax = "proto3";
 
 package banyandb.measure.v1;
 
-import "banyandb/common/v1/common.proto";
 import "banyandb/model/v1/common.proto";
 import "banyandb/model/v1/query.proto";
 import "google/protobuf/timestamp.proto";
@@ -49,17 +48,19 @@ message TopNResponse {
 
 // TopNRequest is the request contract for query.
 message TopNRequest {
-  // metadata is required
-  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // 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 = 2 [(validate.rules).message.required = true];
+  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 = 3 [(validate.rules).int32.gt = 0];
+  int32 top_n = 4 [(validate.rules).int32.gt = 0];
   // agg aggregates lists grouped by field names in the time_range
   // TODO validate enum defined_only
-  model.v1.AggregationFunction agg = 4;
+  model.v1.AggregationFunction agg = 5;
   // criteria select counters. Only equals are acceptable.
-  repeated model.v1.Condition conditions = 5;
+  repeated model.v1.Condition conditions = 6;
   // field_value_sort indicates how to sort fields
-  model.v1.Sort field_value_sort = 6;
+  model.v1.Sort field_value_sort = 7;
 }
diff --git a/api/proto/banyandb/stream/v1/query.proto 
b/api/proto/banyandb/stream/v1/query.proto
index e654f2b6..3d2f480e 100644
--- a/api/proto/banyandb/stream/v1/query.proto
+++ b/api/proto/banyandb/stream/v1/query.proto
@@ -19,7 +19,6 @@ syntax = "proto3";
 
 package banyandb.stream.v1;
 
-import "banyandb/common/v1/common.proto";
 import "banyandb/model/v1/query.proto";
 import "google/protobuf/timestamp.proto";
 import "validate/validate.proto";
@@ -54,21 +53,23 @@ message QueryResponse {
 
 // QueryRequest is the request contract for query.
 message QueryRequest {
-  // metadata is required
-  common.v1.Metadata metadata = 1 [(validate.rules).message.required = true];
+  // 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 = 2;
+  model.v1.TimeRange time_range = 3;
   // offset is used to support pagination, together with the following limit
-  uint32 offset = 3;
+  uint32 offset = 4;
   // limit is used to impose a boundary on the number of records being returned
-  uint32 limit = 4;
+  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 = 5;
+  model.v1.QueryOrder order_by = 6;
   // tag_families are indexed.
-  model.v1.Criteria criteria = 6;
+  model.v1.Criteria criteria = 7;
   // projection can be used to select the key names of the element in the 
response
-  model.v1.TagProjection projection = 7 [(validate.rules).message.required = 
true];
+  model.v1.TagProjection projection = 8 [(validate.rules).message.required = 
true];
 }
diff --git a/api/proto/banyandb/stream/v1/rpc.proto 
b/api/proto/banyandb/stream/v1/rpc.proto
index dd4c98a0..adff33db 100644
--- a/api/proto/banyandb/stream/v1/rpc.proto
+++ b/api/proto/banyandb/stream/v1/rpc.proto
@@ -29,12 +29,12 @@ option java_package = 
"org.apache.skywalking.banyandb.stream.v1";
 option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = 
{base_path: "/api"};
 
 service StreamService {
-  rpc Query(banyandb.stream.v1.QueryRequest) returns 
(banyandb.stream.v1.QueryResponse) {
+  rpc Query(QueryRequest) returns (QueryResponse) {
     option (google.api.http) = {
       post: "/v1/stream/data"
       body: "*"
     };
   }
 
-  rpc Write(stream banyandb.stream.v1.WriteRequest) returns (stream 
banyandb.stream.v1.WriteResponse);
+  rpc Write(stream WriteRequest) returns (stream WriteResponse);
 }
diff --git a/banyand/dquery/measure.go b/banyand/dquery/measure.go
index 9dc95d82..5f1c515a 100644
--- a/banyand/dquery/measure.go
+++ b/banyand/dquery/measure.go
@@ -22,6 +22,7 @@ import (
        "time"
 
        "github.com/apache/skywalking-banyandb/api/common"
+       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
        measurev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
        "github.com/apache/skywalking-banyandb/banyand/measure"
        "github.com/apache/skywalking-banyandb/pkg/bus"
@@ -43,12 +44,20 @@ func (p *measureQueryProcessor) Rev(message bus.Message) 
(resp bus.Message) {
                resp = bus.NewMessage(bus.MessageID(now), 
common.NewError("invalid event data type"))
                return
        }
-       ml := p.log.Named("measure", queryCriteria.Metadata.Group, 
queryCriteria.Metadata.Name)
+       // TODO: support multiple groups
+       if len(queryCriteria.Groups) > 1 {
+               resp = bus.NewMessage(bus.MessageID(now), common.NewError("only 
support one group in the query request"))
+               return
+       }
+       ml := p.log.Named("measure", queryCriteria.Groups[0], 
queryCriteria.Name)
        if e := ml.Debug(); e.Enabled() {
                e.RawJSON("req", logger.Proto(queryCriteria)).Msg("received a 
query event")
        }
 
-       meta := queryCriteria.GetMetadata()
+       meta := &commonv1.Metadata{
+               Name:  queryCriteria.Name,
+               Group: queryCriteria.Groups[0],
+       }
        ec, err := p.measureService.Measure(meta)
        if err != nil {
                resp = bus.NewMessage(bus.MessageID(now), common.NewError("fail 
to get execution context for measure %s: %v", meta.GetName(), err))
diff --git a/banyand/dquery/stream.go b/banyand/dquery/stream.go
index 6fd129d0..1fc9520c 100644
--- a/banyand/dquery/stream.go
+++ b/banyand/dquery/stream.go
@@ -22,6 +22,7 @@ import (
        "time"
 
        "github.com/apache/skywalking-banyandb/api/common"
+       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
        streamv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/stream/v1"
        "github.com/apache/skywalking-banyandb/banyand/stream"
        "github.com/apache/skywalking-banyandb/pkg/bus"
@@ -46,8 +47,15 @@ func (p *streamQueryProcessor) Rev(message bus.Message) 
(resp bus.Message) {
        if p.log.Debug().Enabled() {
                p.log.Debug().RawJSON("criteria", 
logger.Proto(queryCriteria)).Msg("received a query request")
        }
-
-       meta := queryCriteria.GetMetadata()
+       // TODO: support multiple groups
+       if len(queryCriteria.Groups) > 1 {
+               resp = bus.NewMessage(bus.MessageID(now), common.NewError("only 
support one group in the query request"))
+               return
+       }
+       meta := &commonv1.Metadata{
+               Name:  queryCriteria.Name,
+               Group: queryCriteria.Groups[0],
+       }
        ec, err := p.streamService.Stream(meta)
        if err != nil {
                resp = bus.NewMessage(bus.MessageID(now), common.NewError("fail 
to get execution context for stream %s: %v", meta.GetName(), err))
diff --git a/banyand/dquery/topn.go b/banyand/dquery/topn.go
index ad666a45..9aefc08c 100644
--- a/banyand/dquery/topn.go
+++ b/banyand/dquery/topn.go
@@ -58,7 +58,7 @@ func (t *topNQueryProcessor) Rev(message bus.Message) (resp 
bus.Message) {
        now := bus.MessageID(request.TimeRange.Begin.Nanos)
        ff, err := t.broadcaster.Broadcast(defaultTopNQueryTimeout, 
data.TopicTopNQuery, bus.NewMessage(now, request))
        if err != nil {
-               resp = bus.NewMessage(now, common.NewError("execute the query 
%s: %v", request.Metadata.GetName(), err))
+               resp = bus.NewMessage(now, common.NewError("execute the query 
%s: %v", request.GetName(), err))
                return
        }
        var allErr error
@@ -92,7 +92,7 @@ func (t *topNQueryProcessor) Rev(message bus.Message) (resp 
bus.Message) {
                }
        }
        if allErr != nil {
-               resp = bus.NewMessage(now, common.NewError("execute the query 
%s: %v", request.Metadata.GetName(), allErr))
+               resp = bus.NewMessage(now, common.NewError("execute the query 
%s: %v", request.GetName(), allErr))
                return
        }
        if tags == nil {
diff --git a/banyand/internal/storage/index.go 
b/banyand/internal/storage/index.go
index 575ec706..3ce7c36b 100644
--- a/banyand/internal/storage/index.go
+++ b/banyand/internal/storage/index.go
@@ -315,7 +315,7 @@ func (sic *seriesIndexController[T, O]) loadIdx() 
([]string, error) {
 }
 
 func (sic *seriesIndexController[T, O]) newIdx(ctx context.Context) 
(*seriesIndex, error) {
-       return sic.openIdx(ctx, fmt.Sprintf("idx-%016x", time.Now().UnixNano()))
+       return sic.openIdx(ctx, fmt.Sprintf("idx-%016x", 
sic.clock.Now().UnixNano()))
 }
 
 func (sic *seriesIndexController[T, O]) openIdx(ctx context.Context, name 
string) (*seriesIndex, error) {
diff --git a/banyand/internal/storage/index_test.go 
b/banyand/internal/storage/index_test.go
index 8e59b4e1..37991955 100644
--- a/banyand/internal/storage/index_test.go
+++ b/banyand/internal/storage/index_test.go
@@ -34,6 +34,7 @@ import (
        pbv1 "github.com/apache/skywalking-banyandb/pkg/pb/v1"
        "github.com/apache/skywalking-banyandb/pkg/test"
        "github.com/apache/skywalking-banyandb/pkg/test/flags"
+       "github.com/apache/skywalking-banyandb/pkg/timestamp"
 )
 
 var testSeriesPool pbv1.SeriesPool
@@ -196,36 +197,97 @@ func TestSeriesIndexController(t *testing.T) {
        })
 
        t.Run("Test retention", func(t *testing.T) {
-               ctx := context.Background()
-               tmpDir, dfFn, err := test.NewSpace()
-               require.NoError(t, err)
-               defer dfFn()
+               scenarios := []struct {
+                       name string
+                       now  string
+               }{
+                       {"more than one hour before a new day", "2024-04-24 
22:30:00"},
+                       {"more than one hour after a new day", "2024-04-25 
01:30:00"},
+                       {"equal one hour after a new day", "2024-04-25 
01:00:00"},
+                       {"less one hour after a new day", "2024-04-25 
00:50:00"},
+               }
 
-               opts := TSDBOpts[TSTable, any]{
-                       Location: tmpDir,
-                       TTL:      ttl,
+               for _, scenario := range scenarios {
+                       t.Run(scenario.name, func(t *testing.T) {
+                               ctx := context.Background()
+                               c := timestamp.NewMockClock()
+                               now, err := time.ParseInLocation("2006-01-02 
15:04:05", scenario.now, time.Local)
+                               require.NoError(t, err)
+                               c.Set(now)
+                               ctx = timestamp.SetClock(ctx, c)
+                               tmpDir, dfFn, err := test.NewSpace()
+                               require.NoError(t, err)
+                               defer dfFn()
+
+                               opts := TSDBOpts[TSTable, any]{
+                                       Location: tmpDir,
+                                       TTL:      ttl,
+                               }
+                               sic, err := newSeriesIndexController(ctx, opts)
+                               require.NoError(t, err)
+                               defer sic.Close()
+                               c.Set(now.Add(-time.Hour*23 + 10*time.Minute))
+                               require.NoError(t, sic.run(c.Now()))
+                               sic.RLock()
+                               standby := sic.standby
+                               sic.RUnlock()
+                               require.NotNil(t, standby)
+                               idxNames := make([]string, 0)
+                               walkDir(tmpDir, "idx-", func(suffix string) 
error {
+                                       idxNames = append(idxNames, suffix)
+                                       return nil
+                               })
+                               assert.Equal(t, 2, len(idxNames))
+                               nextTime := standby.startTime
+                               c.Set(now.Add(time.Hour))
+                               require.NoError(t, sic.run(c.Now()))
+                               sic.RLock()
+                               standby = sic.standby
+                               hot := sic.hot
+                               sic.RUnlock()
+                               require.Nil(t, standby)
+                               assert.Equal(t, nextTime, hot.startTime)
+                       })
+               }
+               scenarios = []struct {
+                       name string
+                       now  string
+               }{
+                       {"less one hour before a new day", "2024-04-24 
23:10:00"},
+                       {"equal one hour before a new day", "2024-04-24 
23:00:00"},
+               }
+               for _, scenario := range scenarios {
+                       t.Run(scenario.name, func(t *testing.T) {
+                               ctx := context.Background()
+                               c := timestamp.NewMockClock()
+                               now, err := time.ParseInLocation("2006-01-02 
15:04:05", scenario.now, time.Local)
+                               require.NoError(t, err)
+                               c.Set(now)
+                               ctx = timestamp.SetClock(ctx, c)
+                               tmpDir, dfFn, err := test.NewSpace()
+                               require.NoError(t, err)
+                               defer dfFn()
+
+                               opts := TSDBOpts[TSTable, any]{
+                                       Location: tmpDir,
+                                       TTL:      ttl,
+                               }
+                               sic, err := newSeriesIndexController(ctx, opts)
+                               require.NoError(t, err)
+                               defer sic.Close()
+                               c.Set(now.Add(-time.Hour*23 + 10*time.Minute))
+                               require.NoError(t, sic.run(c.Now()))
+                               sic.RLock()
+                               standby := sic.standby
+                               sic.RUnlock()
+                               require.Nil(t, standby)
+                               idxNames := make([]string, 0)
+                               walkDir(tmpDir, "idx-", func(suffix string) 
error {
+                                       idxNames = append(idxNames, suffix)
+                                       return nil
+                               })
+                               assert.Equal(t, 1, len(idxNames))
+                       })
                }
-               sic, err := newSeriesIndexController(ctx, opts)
-               require.NoError(t, err)
-               defer sic.Close()
-               require.NoError(t, 
sic.run(time.Now().Add(-time.Hour*23+10*time.Minute)))
-               sic.RLock()
-               standby := sic.standby
-               sic.RUnlock()
-               require.NotNil(t, standby)
-               idxNames := make([]string, 0)
-               walkDir(tmpDir, "idx-", func(suffix string) error {
-                       idxNames = append(idxNames, suffix)
-                       return nil
-               })
-               assert.Equal(t, 2, len(idxNames))
-               nextTime := standby.startTime
-               require.NoError(t, sic.run(time.Now().Add(time.Hour)))
-               sic.RLock()
-               standby = sic.standby
-               hot := sic.hot
-               sic.RUnlock()
-               require.Nil(t, standby)
-               assert.Equal(t, nextTime, hot.startTime)
        })
 }
diff --git a/banyand/measure/query.go b/banyand/measure/query.go
index b7da2ad6..f2b43bcf 100644
--- a/banyand/measure/query.go
+++ b/banyand/measure/query.go
@@ -75,9 +75,10 @@ func (s *measure) Query(ctx context.Context, mqo 
pbv1.MeasureQueryOptions) (pbv1
        if len(mqo.TagProjection) == 0 && len(mqo.FieldProjection) == 0 {
                return nil, errors.New("invalid query options: tagProjection or 
fieldProjection is required")
        }
+       var result queryResult
        db := s.databaseSupplier.SupplyTSDB()
        if db == nil {
-               return nil, errors.New("cannot get tsdb")
+               return &result, nil
        }
        tsdb := db.(storage.TSDB[*tsTable, option])
        tabWrappers := tsdb.SelectTSTables(*mqo.TimeRange)
@@ -91,7 +92,6 @@ func (s *measure) Query(ctx context.Context, mqo 
pbv1.MeasureQueryOptions) (pbv1
        if err != nil {
                return nil, err
        }
-       var result queryResult
        if len(sl) < 1 {
                return &result, nil
        }
diff --git a/banyand/query/processor.go b/banyand/query/processor.go
index 70a89d8e..ccb039e9 100644
--- a/banyand/query/processor.go
+++ b/banyand/query/processor.go
@@ -25,6 +25,7 @@ import (
 
        "github.com/apache/skywalking-banyandb/api/common"
        "github.com/apache/skywalking-banyandb/api/data"
+       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
        measurev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
        streamv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/stream/v1"
        "github.com/apache/skywalking-banyandb/banyand/measure"
@@ -75,8 +76,15 @@ func (p *streamQueryProcessor) Rev(message bus.Message) 
(resp bus.Message) {
        if p.log.Debug().Enabled() {
                p.log.Debug().RawJSON("criteria", 
logger.Proto(queryCriteria)).Msg("received a query request")
        }
-
-       meta := queryCriteria.GetMetadata()
+       // TODO: support multiple groups
+       if len(queryCriteria.Groups) > 1 {
+               resp = bus.NewMessage(bus.MessageID(now), common.NewError("only 
support one group in the query request"))
+               return
+       }
+       meta := &commonv1.Metadata{
+               Name:  queryCriteria.Name,
+               Group: queryCriteria.Groups[0],
+       }
        ec, err := p.streamService.Stream(meta)
        if err != nil {
                resp = bus.NewMessage(bus.MessageID(now), common.NewError("fail 
to get execution context for stream %s: %v", meta.GetName(), err))
@@ -121,12 +129,20 @@ func (p *measureQueryProcessor) Rev(message bus.Message) 
(resp bus.Message) {
                resp = bus.NewMessage(bus.MessageID(now), 
common.NewError("invalid event data type"))
                return
        }
-       ml := p.log.Named("measure", queryCriteria.Metadata.Group, 
queryCriteria.Metadata.Name)
+       // TODO: support multiple groups
+       if len(queryCriteria.Groups) > 1 {
+               resp = bus.NewMessage(bus.MessageID(now), common.NewError("only 
support one group in the query request"))
+               return
+       }
+       ml := p.log.Named("measure", queryCriteria.Groups[0], 
queryCriteria.Name)
        if e := ml.Debug(); e.Enabled() {
                e.RawJSON("req", logger.Proto(queryCriteria)).Msg("received a 
query event")
        }
 
-       meta := queryCriteria.GetMetadata()
+       meta := &commonv1.Metadata{
+               Name:  queryCriteria.Name,
+               Group: queryCriteria.Groups[0],
+       }
        ec, err := p.measureService.Measure(meta)
        if err != nil {
                resp = bus.NewMessage(bus.MessageID(now), common.NewError("fail 
to get execution context for measure %s: %v", meta.GetName(), err))
diff --git a/banyand/query/processor_topn.go b/banyand/query/processor_topn.go
index e40339f9..8e64169f 100644
--- a/banyand/query/processor_topn.go
+++ b/banyand/query/processor_topn.go
@@ -26,6 +26,7 @@ import (
        "google.golang.org/protobuf/types/known/timestamppb"
 
        "github.com/apache/skywalking-banyandb/api/common"
+       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
        measurev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/measure/v1"
        modelv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
        "github.com/apache/skywalking-banyandb/banyand/measure"
@@ -50,7 +51,12 @@ func (t *topNQueryProcessor) Rev(message bus.Message) (resp 
bus.Message) {
                t.log.Warn().Msg("invalid event data type")
                return
        }
-       ml := t.log.Named("topn", request.Metadata.Group, request.Metadata.Name)
+       // TODO: support multiple groups
+       if len(request.Groups) > 1 {
+               resp = bus.NewMessage(bus.MessageID(now), common.NewError("only 
support one group in the query request"))
+               return
+       }
+       ml := t.log.Named("topn", request.Groups[0], request.Name)
        if e := ml.Debug(); e.Enabled() {
                e.RawJSON("req", logger.Proto(request)).Msg("received a topn 
event")
        }
@@ -61,7 +67,10 @@ func (t *topNQueryProcessor) Rev(message bus.Message) (resp 
bus.Message) {
        if e := t.log.Debug(); e.Enabled() {
                e.Stringer("req", request).Msg("received a topN query event")
        }
-       topNMetadata := request.GetMetadata()
+       topNMetadata := &commonv1.Metadata{
+               Name:  request.Name,
+               Group: request.Groups[0],
+       }
        topNSchema, err := 
t.metaService.TopNAggregationRegistry().GetTopNAggregation(context.TODO(), 
topNMetadata)
        if err != nil {
                t.log.Error().Err(err).
diff --git a/banyand/stream/query.go b/banyand/stream/query.go
index 876be765..923e0b17 100644
--- a/banyand/stream/query.go
+++ b/banyand/stream/query.go
@@ -365,7 +365,7 @@ func (s *stream) Filter(ctx context.Context, sfo 
pbv1.StreamFilterOptions) (sfr
        }
        db := s.databaseSupplier.SupplyTSDB()
        if db == nil {
-               return nil, errors.New("no tsdb found")
+               return sfr, nil
        }
        tsdb := db.(storage.TSDB[*tsTable, option])
        tabWrappers := tsdb.SelectTSTables(*sfo.TimeRange)
@@ -439,7 +439,7 @@ func (s *stream) Sort(ctx context.Context, sso 
pbv1.StreamSortOptions) (ssr pbv1
        }
        db := s.databaseSupplier.SupplyTSDB()
        if db == nil {
-               return nil, errors.New("no tsdb found")
+               return ssr, nil
        }
        tsdb := db.(storage.TSDB[*tsTable, option])
        tabWrappers := tsdb.SelectTSTables(*sso.TimeRange)
@@ -495,8 +495,9 @@ func (s *stream) Query(ctx context.Context, sqo 
pbv1.StreamQueryOptions) (pbv1.S
                return nil, errors.New("invalid query options: tagProjection is 
required")
        }
        db := s.databaseSupplier.SupplyTSDB()
+       var result queryResult
        if db == nil {
-               return nil, errors.New("no tsdb found")
+               return &result, nil
        }
        tsdb := db.(storage.TSDB[*tsTable, option])
        tabWrappers := tsdb.SelectTSTables(*sqo.TimeRange)
@@ -510,7 +511,6 @@ func (s *stream) Query(ctx context.Context, sqo 
pbv1.StreamQueryOptions) (pbv1.S
                return nil, err
        }
 
-       var result queryResult
        if len(sl) < 1 {
                return &result, nil
        }
diff --git a/bydbctl/internal/cmd/group.go b/bydbctl/internal/cmd/group.go
index b549fa72..094b9039 100644
--- a/bydbctl/internal/cmd/group.go
+++ b/bydbctl/internal/cmd/group.go
@@ -115,7 +115,7 @@ func newGroupCmd() *cobra.Command {
                                return request.req.SetPathParam("group", 
request.group).Delete(getPath("/api/v1/group/schema/{group}"))
                        },
                                func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("group %s is deleted", 
reqBody.name)
+                                       fmt.Printf("group %s is deleted", 
reqBody.group)
                                        fmt.Println()
                                        return nil
                                }, enableTLS, insecure, grpcCert)
diff --git a/bydbctl/internal/cmd/measure_test.go 
b/bydbctl/internal/cmd/measure_test.go
index 93dd42f2..34706b2b 100644
--- a/bydbctl/internal/cmd/measure_test.go
+++ b/bydbctl/internal/cmd/measure_test.go
@@ -213,9 +213,8 @@ var _ = Describe("Measure Data Query", func() {
                rootCmd.SetArgs([]string{"measure", "query", "-a", addr, "-f", 
"-"})
                issue := func() string {
                        rootCmd.SetIn(strings.NewReader(fmt.Sprintf(`
-metadata:
-  name: service_cpm_minute
-  group: sw_metric
+name: service_cpm_minute
+groups: ["sw_metric"]
 timeRange:
   begin: %s
   end: %s
@@ -255,9 +254,8 @@ tagProjection:
                rootCmd.SetArgs(args)
                issue := func() string {
                        rootCmd.SetIn(strings.NewReader(`
-metadata:
- name: service_cpm_minute
- group: sw_metric
+name: service_cpm_minute
+groups: ["sw_metric"]
 tagProjection:
  tagFamilies:
    - name: default
diff --git a/bydbctl/internal/cmd/rest.go b/bydbctl/internal/cmd/rest.go
index cd659709..600310cf 100644
--- a/bydbctl/internal/cmd/rest.go
+++ b/bydbctl/internal/cmd/rest.go
@@ -113,21 +113,36 @@ func parseFromYAML(tryParseGroup bool, reader io.Reader) 
(requests []reqBody, er
                if err != nil {
                        return nil, err
                }
-               metadata, ok := data["metadata"].(map[string]interface{})
-               if !ok {
-                       return nil, errors.WithMessage(errMalformedInput, 
"absent node: metadata")
-               }
-               group, ok := metadata["group"].(string)
-               if !ok && tryParseGroup {
-                       group = viper.GetString("group")
-                       if group == "" {
-                               return nil, errors.New("please specify a group 
through the input json or the config file")
+
+               var group string
+               // TODO:// bydbctl should support multiple groups
+               if metadata, ok := data["metadata"].(map[string]interface{}); 
ok {
+                       group, ok = metadata["group"].(string)
+                       if !ok && tryParseGroup {
+                               group = viper.GetString("group")
+                               if group == "" {
+                                       return nil, errors.New("please specify 
a group through the input json or the config file")
+                               }
+                               metadata["group"] = group
                        }
-                       metadata["group"] = group
-               }
-               name, ok = metadata["name"].(string)
-               if !ok {
-                       return nil, errors.WithMessage(errMalformedInput, 
"absent node: name in metadata")
+                       name, ok = metadata["name"].(string)
+                       if !ok {
+                               return nil, 
errors.WithMessage(errMalformedInput, "absent node: name in metadata")
+                       }
+               } else if name, ok = data["name"].(string); ok {
+                       groups, ok := data["groups"].([]any)
+                       if ok {
+                               group = groups[0].(string)
+                       }
+                       if !ok && tryParseGroup {
+                               group = viper.GetString("group")
+                               if group == "" {
+                                       return nil, errors.New("please specify 
a group through the input json or the config file")
+                               }
+                               data["groups"] = []string{group}
+                       }
+               } else {
+                       return nil, errors.WithMessage(errMalformedInput, 
"absent node: metadata or name&group")
                }
                j, err = json.Marshal(data)
                if err != nil {
diff --git a/bydbctl/internal/cmd/stream_test.go 
b/bydbctl/internal/cmd/stream_test.go
index ef2643fa..1238c317 100644
--- a/bydbctl/internal/cmd/stream_test.go
+++ b/bydbctl/internal/cmd/stream_test.go
@@ -215,9 +215,8 @@ var _ = Describe("Stream Data Query", func() {
                rootCmd.SetArgs([]string{"stream", "query", "-a", addr, "-f", 
"-"})
                issue := func() string {
                        rootCmd.SetIn(strings.NewReader(fmt.Sprintf(`
-metadata:
-  name: sw
-  group: default
+name: sw
+groups: ["default"]
 timeRange:
   begin: %s
   end: %s
@@ -256,9 +255,8 @@ projection:
                rootCmd.SetArgs(args)
                issue := func() string {
                        rootCmd.SetIn(strings.NewReader(`
-metadata:
-  name: sw
-  group: default
+name: sw
+groups: ["default"]
 projection:
   tagFamilies:
     - name: searchable
diff --git a/docs/api-reference.md b/docs/api-reference.md
index a4e2df3f..56620177 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -2319,7 +2319,8 @@ QueryRequest is the request contract for query.
 
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
-| metadata | [banyandb.common.v1.Metadata](#banyandb-common-v1-Metadata) |  | 
metadata is required |
+| groups | [string](#string) | repeated | groups indicate where the data 
points are stored. |
+| name | [string](#string) |  | name is the identity of a measure. |
 | time_range | [banyandb.model.v1.TimeRange](#banyandb-model-v1-TimeRange) |  
| time_range is a range query with begin/end time of entities in the timeunit 
of milliseconds. |
 | criteria | [banyandb.model.v1.Criteria](#banyandb-model-v1-Criteria) |  | 
tag_families are indexed. |
 | tag_projection | 
[banyandb.model.v1.TagProjection](#banyandb-model-v1-TagProjection) |  | 
tag_projection can be used to select tags of the data points in the response |
@@ -2471,7 +2472,8 @@ TopNRequest is the request contract for query.
 
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
-| metadata | [banyandb.common.v1.Metadata](#banyandb-common-v1-Metadata) |  | 
metadata is required |
+| groups | [string](#string) | repeated | groups indicate where the data 
points are stored. |
+| name | [string](#string) |  | name is the identity of a measure. |
 | time_range | [banyandb.model.v1.TimeRange](#banyandb-model-v1-TimeRange) |  
| time_range is a range query with begin/end time of entities in the timeunit 
of milliseconds. |
 | top_n | [int32](#int32) |  | top_n set the how many items should be returned 
in each list. |
 | agg | 
[banyandb.model.v1.AggregationFunction](#banyandb-model-v1-AggregationFunction) 
|  | agg aggregates lists grouped by field names in the time_range TODO 
validate enum defined_only |
@@ -2933,7 +2935,8 @@ QueryRequest is the request contract for query.
 
 | Field | Type | Label | Description |
 | ----- | ---- | ----- | ----------- |
-| metadata | [banyandb.common.v1.Metadata](#banyandb-common-v1-Metadata) |  | 
metadata is required |
+| groups | [string](#string) | repeated | groups indicate where the elements 
are stored. |
+| name | [string](#string) |  | name is the identity of a stream. |
 | time_range | [banyandb.model.v1.TimeRange](#banyandb-model-v1-TimeRange) |  
| 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 |
 | offset | [uint32](#uint32) |  | offset is used to support pagination, 
together with the following limit |
 | limit | [uint32](#uint32) |  | limit is used to impose a boundary on the 
number of records being returned |
diff --git a/pkg/query/logical/measure/measure_plan_distributed.go 
b/pkg/query/logical/measure/measure_plan_distributed.go
index d0aee004..215bdbcc 100644
--- a/pkg/query/logical/measure/measure_plan_distributed.go
+++ b/pkg/query/logical/measure/measure_plan_distributed.go
@@ -79,7 +79,8 @@ func (ud *unresolvedDistributed) Analyze(s logical.Schema) 
(logical.Plan, error)
        temp := &measurev1.QueryRequest{
                TagProjection:   ud.originalQuery.TagProjection,
                FieldProjection: ud.originalQuery.FieldProjection,
-               Metadata:        ud.originalQuery.Metadata,
+               Name:            ud.originalQuery.Name,
+               Groups:          ud.originalQuery.Groups,
                Criteria:        ud.originalQuery.Criteria,
                Limit:           limit,
                OrderBy:         ud.originalQuery.OrderBy,
diff --git a/pkg/query/logical/stream/stream_plan_distributed.go 
b/pkg/query/logical/stream/stream_plan_distributed.go
index 8c3ed930..ae6830c8 100644
--- a/pkg/query/logical/stream/stream_plan_distributed.go
+++ b/pkg/query/logical/stream/stream_plan_distributed.go
@@ -69,7 +69,8 @@ func (ud *unresolvedDistributed) Analyze(s logical.Schema) 
(logical.Plan, error)
        }
        temp := &streamv1.QueryRequest{
                Projection: ud.originalQuery.Projection,
-               Metadata:   ud.originalQuery.Metadata,
+               Name:       ud.originalQuery.Name,
+               Groups:     ud.originalQuery.Groups,
                Criteria:   ud.originalQuery.Criteria,
                Limit:      limit,
                OrderBy:    ud.originalQuery.OrderBy,
diff --git a/scripts/build/version.mk b/scripts/build/version.mk
index 18bb0bd0..1bf9fe5f 100644
--- a/scripts/build/version.mk
+++ b/scripts/build/version.mk
@@ -16,12 +16,12 @@
 # under the License.
 #
 
-BUF_VERSION := v1.27.2
-PROTOC_GEN_GO_VERSION := v1.31.0
+BUF_VERSION := v1.31.0
+PROTOC_GEN_GO_VERSION := v1.33.0
 PROTOC_GEN_GO_GRPC_VERSION := v1.3.0
 PROTOC_GEN_DOC_VERSION := v1.5.1
-GRPC_GATEWAY_VERSION := v2.18.0
-PROTOC_GEN_VALIDATE_VERSION := v1.0.2
+GRPC_GATEWAY_VERSION := v2.19.1
+PROTOC_GEN_VALIDATE_VERSION := v1.0.4
 
 GOLANGCI_LINT_VERSION := v1.55.1
 REVIVE_VERSION := v1.3.4
diff --git a/test/cases/measure/data/input/all.yaml 
b/test/cases/measure/data/input/all.yaml
index 81d07351..213b0f56 100644
--- a/test/cases/measure/data/input/all.yaml
+++ b/test/cases/measure/data/input/all.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/all_latency.yaml 
b/test/cases/measure/data/input/all_latency.yaml
index cd48d603..b2cdf45e 100644
--- a/test/cases/measure/data/input/all_latency.yaml
+++ b/test/cases/measure/data/input/all_latency.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_latency_minute"
-  group: "sw_metric"
+name: "service_latency_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "storage_only"
diff --git a/test/cases/measure/data/input/all_only_fields.yaml 
b/test/cases/measure/data/input/all_only_fields.yaml
index b3256df0..6dc6281d 100644
--- a/test/cases/measure/data/input/all_only_fields.yaml
+++ b/test/cases/measure/data/input/all_only_fields.yaml
@@ -15,8 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 fieldProjection:
   names: ["total", "value"]
diff --git a/test/cases/measure/data/input/bottom.yaml 
b/test/cases/measure/data/input/bottom.yaml
index 0cc0e4c2..19a4b5ef 100644
--- a/test/cases/measure/data/input/bottom.yaml
+++ b/test/cases/measure/data/input/bottom.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/entity.yaml 
b/test/cases/measure/data/input/entity.yaml
index fde1b0c2..40a1c756 100644
--- a/test/cases/measure/data/input/entity.yaml
+++ b/test/cases/measure/data/input/entity.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_traffic"
-  group: "sw_metric"
+name: "service_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/entity_in.yaml 
b/test/cases/measure/data/input/entity_in.yaml
index 07c938d4..eb19686a 100644
--- a/test/cases/measure/data/input/entity_in.yaml
+++ b/test/cases/measure/data/input/entity_in.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_traffic"
-  group: "sw_metric"
+name: "service_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/entity_service.yaml 
b/test/cases/measure/data/input/entity_service.yaml
index 9828d423..1074be66 100644
--- a/test/cases/measure/data/input/entity_service.yaml
+++ b/test/cases/measure/data/input/entity_service.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_traffic"
-  group: "sw_metric"
+name: "service_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/err_invalid_le.yaml 
b/test/cases/measure/data/input/err_invalid_le.yaml
index 85fff59f..e93b8e46 100644
--- a/test/cases/measure/data/input/err_invalid_le.yaml
+++ b/test/cases/measure/data/input/err_invalid_le.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/float.yaml 
b/test/cases/measure/data/input/float.yaml
index 73e71f12..b21a8fea 100644
--- a/test/cases/measure/data/input/float.yaml
+++ b/test/cases/measure/data/input/float.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "instance_clr_cpu_minute"
-  group: "sw_metric"
+name: "instance_clr_cpu_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/float_agg_min.yaml 
b/test/cases/measure/data/input/float_agg_min.yaml
index 5762a016..3f151c33 100644
--- a/test/cases/measure/data/input/float_agg_min.yaml
+++ b/test/cases/measure/data/input/float_agg_min.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "instance_clr_cpu_minute"
-  group: "sw_metric"
+name: "instance_clr_cpu_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
@@ -26,4 +25,4 @@ fieldProjection:
   names: ["summation", "count", "value"]
 agg:
   function: "AGGREGATION_FUNCTION_MIN"
-  fieldName: "value"
\ No newline at end of file
+  fieldName: "value"
diff --git a/test/cases/measure/data/input/group_max.yaml 
b/test/cases/measure/data/input/group_max.yaml
index 8287303b..73d31ed0 100644
--- a/test/cases/measure/data/input/group_max.yaml
+++ b/test/cases/measure/data/input/group_max.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/group_no_field.yaml 
b/test/cases/measure/data/input/group_no_field.yaml
index b9632f78..48dbf486 100644
--- a/test/cases/measure/data/input/group_no_field.yaml
+++ b/test/cases/measure/data/input/group_no_field.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/in.yaml 
b/test/cases/measure/data/input/in.yaml
index 7b0fe828..b568b665 100644
--- a/test/cases/measure/data/input/in.yaml
+++ b/test/cases/measure/data/input/in.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/limit.yaml 
b/test/cases/measure/data/input/limit.yaml
index cb97f345..5f94caf2 100644
--- a/test/cases/measure/data/input/limit.yaml
+++ b/test/cases/measure/data/input/limit.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/linked_or.yaml 
b/test/cases/measure/data/input/linked_or.yaml
index bf9d90ad..d2662a84 100644
--- a/test/cases/measure/data/input/linked_or.yaml
+++ b/test/cases/measure/data/input/linked_or.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/match_node.yaml 
b/test/cases/measure/data/input/match_node.yaml
index e5edb554..a71b4ed4 100644
--- a/test/cases/measure/data/input/match_node.yaml
+++ b/test/cases/measure/data/input/match_node.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_instance_traffic"
-  group: "sw_metric"
+name: "service_instance_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/match_nodes.yaml 
b/test/cases/measure/data/input/match_nodes.yaml
index e4b4e8e0..6d24b5a4 100644
--- a/test/cases/measure/data/input/match_nodes.yaml
+++ b/test/cases/measure/data/input/match_nodes.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_instance_traffic"
-  group: "sw_metric"
+name: "service_instance_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/no_field.yaml 
b/test/cases/measure/data/input/no_field.yaml
index ed2255c9..14454a22 100644
--- a/test/cases/measure/data/input/no_field.yaml
+++ b/test/cases/measure/data/input/no_field.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_traffic"
-  group: "sw_metric"
+name: "service_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/order_asc.yaml 
b/test/cases/measure/data/input/order_asc.yaml
index 50110708..1643a383 100644
--- a/test/cases/measure/data/input/order_asc.yaml
+++ b/test/cases/measure/data/input/order_asc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/order_desc.yaml 
b/test/cases/measure/data/input/order_desc.yaml
index ce1dac97..cf7389a7 100644
--- a/test/cases/measure/data/input/order_desc.yaml
+++ b/test/cases/measure/data/input/order_desc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/order_tag_asc.yaml 
b/test/cases/measure/data/input/order_tag_asc.yaml
index a752c72b..c1080622 100644
--- a/test/cases/measure/data/input/order_tag_asc.yaml
+++ b/test/cases/measure/data/input/order_tag_asc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/order_tag_desc.yaml 
b/test/cases/measure/data/input/order_tag_desc.yaml
index c5007d75..72647905 100644
--- a/test/cases/measure/data/input/order_tag_desc.yaml
+++ b/test/cases/measure/data/input/order_tag_desc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/tag_filter.yaml 
b/test/cases/measure/data/input/tag_filter.yaml
index b5481e8f..7be5bc0e 100644
--- a/test/cases/measure/data/input/tag_filter.yaml
+++ b/test/cases/measure/data/input/tag_filter.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/tag_filter_int.yaml 
b/test/cases/measure/data/input/tag_filter_int.yaml
index f44e1911..d8c7d1a0 100644
--- a/test/cases/measure/data/input/tag_filter_int.yaml
+++ b/test/cases/measure/data/input/tag_filter_int.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_traffic"
-  group: "sw_metric"
+name: "service_traffic"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/tag_filter_unknown.yaml 
b/test/cases/measure/data/input/tag_filter_unknown.yaml
index 6b124b1b..a55b44ac 100644
--- a/test/cases/measure/data/input/tag_filter_unknown.yaml
+++ b/test/cases/measure/data/input/tag_filter_unknown.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/measure/data/input/top.yaml 
b/test/cases/measure/data/input/top.yaml
index 5d66f2dc..554dc06e 100644
--- a/test/cases/measure/data/input/top.yaml
+++ b/test/cases/measure/data/input/top.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_cpm_minute"
-  group: "sw_metric"
+name: "service_cpm_minute"
+groups: ["sw_metric"]
 tagProjection:
   tagFamilies:
   - name: "default"
diff --git a/test/cases/stream/data/input/all.yaml 
b/test/cases/stream/data/input/all.yaml
index 34efbc63..5d9c17ca 100644
--- a/test/cases/stream/data/input/all.yaml
+++ b/test/cases/stream/data/input/all.yaml
@@ -15,12 +15,11 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
     tags: ["trace_id"]
   - name: "data"
-    tags: ["data_binary"]
\ No newline at end of file
+    tags: ["data_binary"]
diff --git a/test/cases/stream/data/input/filter_tag.yaml 
b/test/cases/stream/data/input/filter_tag.yaml
index 47bc94d1..ffb8b211 100644
--- a/test/cases/stream/data/input/filter_tag.yaml
+++ b/test/cases/stream/data/input/filter_tag.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
diff --git a/test/cases/stream/data/input/filter_tag_empty.yaml 
b/test/cases/stream/data/input/filter_tag_empty.yaml
index bbc82bbe..881df748 100644
--- a/test/cases/stream/data/input/filter_tag_empty.yaml
+++ b/test/cases/stream/data/input/filter_tag_empty.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
diff --git a/test/cases/stream/data/input/global_index.yaml 
b/test/cases/stream/data/input/global_index.yaml
index fb1b2b01..9beb9160 100644
--- a/test/cases/stream/data/input/global_index.yaml
+++ b/test/cases/stream/data/input/global_index.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
diff --git a/test/cases/stream/data/input/global_indices.yaml 
b/test/cases/stream/data/input/global_indices.yaml
index 4a5c0fa5..84db8172 100644
--- a/test/cases/stream/data/input/global_indices.yaml
+++ b/test/cases/stream/data/input/global_indices.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
diff --git a/test/cases/stream/data/input/having.yaml 
b/test/cases/stream/data/input/having.yaml
index 88e32189..e63816b6 100644
--- a/test/cases/stream/data/input/having.yaml
+++ b/test/cases/stream/data/input/having.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -30,4 +29,4 @@ criteria:
     op: "BINARY_OP_HAVING"
     value:
       strArray:
-        value: ["c", "b"]
\ No newline at end of file
+        value: ["c", "b"]
diff --git a/test/cases/stream/data/input/having_non_indexed.yaml 
b/test/cases/stream/data/input/having_non_indexed.yaml
index 4cba2440..13e960e7 100644
--- a/test/cases/stream/data/input/having_non_indexed.yaml
+++ b/test/cases/stream/data/input/having_non_indexed.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -30,4 +29,4 @@ criteria:
     op: "BINARY_OP_HAVING"
     value:
       str:
-        value: "c"
\ No newline at end of file
+        value: "c"
diff --git a/test/cases/stream/data/input/having_non_indexed_arr.yaml 
b/test/cases/stream/data/input/having_non_indexed_arr.yaml
index c23b82c0..26e1eb44 100644
--- a/test/cases/stream/data/input/having_non_indexed_arr.yaml
+++ b/test/cases/stream/data/input/having_non_indexed_arr.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -30,4 +29,4 @@ criteria:
     op: "BINARY_OP_HAVING"
     value:
       strArray:
-        value: ["b","c"]
\ No newline at end of file
+        value: ["b", "c"]
diff --git a/test/cases/stream/data/input/indexed_only.yaml 
b/test/cases/stream/data/input/indexed_only.yaml
index 894b42d1..37314114 100644
--- a/test/cases/stream/data/input/indexed_only.yaml
+++ b/test/cases/stream/data/input/indexed_only.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -28,4 +27,4 @@ criteria:
     op: "BINARY_OP_EQ"
     value:
       str:
-        value: "10.0.0.1_id"
\ No newline at end of file
+        value: "10.0.0.1_id"
diff --git a/test/cases/stream/data/input/less.yaml 
b/test/cases/stream/data/input/less.yaml
index 82efd579..2cc05f87 100644
--- a/test/cases/stream/data/input/less.yaml
+++ b/test/cases/stream/data/input/less.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -30,4 +29,4 @@ criteria:
     op: "BINARY_OP_LT"
     value:
       int:
-        value: 500
\ No newline at end of file
+        value: 500
diff --git a/test/cases/stream/data/input/less_eq.yaml 
b/test/cases/stream/data/input/less_eq.yaml
index be32544a..42c1a0d3 100644
--- a/test/cases/stream/data/input/less_eq.yaml
+++ b/test/cases/stream/data/input/less_eq.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -30,4 +29,4 @@ criteria:
     op: "BINARY_OP_LE"
     value:
       int:
-        value: 500
\ No newline at end of file
+        value: 500
diff --git a/test/cases/stream/data/input/limit.yaml 
b/test/cases/stream/data/input/limit.yaml
index c1921a86..fd10e155 100644
--- a/test/cases/stream/data/input/limit.yaml
+++ b/test/cases/stream/data/input/limit.yaml
@@ -15,13 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
     tags: ["trace_id"]
   - name: "data"
     tags: ["data_binary"]
-limit: 3
\ No newline at end of file
+limit: 3
diff --git a/test/cases/stream/data/input/logical.yaml 
b/test/cases/stream/data/input/logical.yaml
index 5f8f1a1d..fc4d8569 100644
--- a/test/cases/stream/data/input/logical.yaml
+++ b/test/cases/stream/data/input/logical.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
diff --git a/test/cases/stream/data/input/offset.yaml 
b/test/cases/stream/data/input/offset.yaml
index 97a800f1..7613fae3 100644
--- a/test/cases/stream/data/input/offset.yaml
+++ b/test/cases/stream/data/input/offset.yaml
@@ -15,13 +15,12 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
     tags: ["trace_id"]
   - name: "data"
     tags: ["data_binary"]
-offset: 3
\ No newline at end of file
+offset: 3
diff --git a/test/cases/stream/data/input/search.yaml 
b/test/cases/stream/data/input/search.yaml
index 7417236e..9b6a3c11 100644
--- a/test/cases/stream/data/input/search.yaml
+++ b/test/cases/stream/data/input/search.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -30,4 +29,4 @@ criteria:
     op: "BINARY_OP_MATCH"
     value:
       str:
-        value: "mysql"
\ No newline at end of file
+        value: "mysql"
diff --git a/test/cases/stream/data/input/sort_desc.yaml 
b/test/cases/stream/data/input/sort_desc.yaml
index a6cc132c..b0d694e1 100644
--- a/test/cases/stream/data/input/sort_desc.yaml
+++ b/test/cases/stream/data/input/sort_desc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -26,4 +25,4 @@ projection:
     tags: ["data_binary"]
 orderBy:
   indexRuleName: "duration"
-  sort: "SORT_DESC"
\ No newline at end of file
+  sort: "SORT_DESC"
diff --git a/test/cases/stream/data/input/sort_filter.yaml 
b/test/cases/stream/data/input/sort_filter.yaml
index afaa9d38..015567ca 100644
--- a/test/cases/stream/data/input/sort_filter.yaml
+++ b/test/cases/stream/data/input/sort_filter.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  group: "default"
-  name: "sw"
+name: "sw"
+groups: ["default"]
 projection:
   tagFamilies:
   - name: "searchable"
@@ -33,4 +32,4 @@ criteria:
         value: 500
 orderBy:
   indexRuleName: "duration"
-  sort: "SORT_DESC"
\ No newline at end of file
+  sort: "SORT_DESC"
diff --git a/test/cases/topn/data/input/aggr_desc.yaml 
b/test/cases/topn/data/input/aggr_desc.yaml
index 3151ab64..dd489349 100644
--- a/test/cases/topn/data/input/aggr_desc.yaml
+++ b/test/cases/topn/data/input/aggr_desc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_instance_cpm_minute_top_bottom_100"
-  group: "sw_metric"
+name: "service_instance_cpm_minute_top_bottom_100"
+groups: ["sw_metric"]
 topN: 3
 fieldValueSort: 1
 agg: 2
diff --git a/test/cases/topn/data/input/condition_aggr_desc.yaml 
b/test/cases/topn/data/input/condition_aggr_desc.yaml
index 844af5a3..f893cbdb 100644
--- a/test/cases/topn/data/input/condition_aggr_desc.yaml
+++ b/test/cases/topn/data/input/condition_aggr_desc.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_instance_cpm_minute_top_bottom_100"
-  group: "sw_metric"
+name: "service_instance_cpm_minute_top_bottom_100"
+groups: ["sw_metric"]
 topN: 1
 fieldValueSort: 1
 agg: 2
diff --git a/test/cases/topn/data/input/null_group.yaml 
b/test/cases/topn/data/input/null_group.yaml
index 7cb92e1b..905118f4 100644
--- a/test/cases/topn/data/input/null_group.yaml
+++ b/test/cases/topn/data/input/null_group.yaml
@@ -15,9 +15,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-metadata:
-  name: "service_instance_endpoint_cpm_minute_top_bottom_100"
-  group: "sw_metric"
+name: "service_instance_endpoint_cpm_minute_top_bottom_100"
+groups: ["sw_metric"]
 topN: 3
 fieldValueSort: 1
 agg: 2
diff --git a/test/e2e-v2/cases/cluster/storage-cases.yaml 
b/test/e2e-v2/cases/cluster/storage-cases.yaml
index 0498a582..28c128b0 100644
--- a/test/e2e-v2/cases/cluster/storage-cases.yaml
+++ b/test/e2e-v2/cases/cluster/storage-cases.yaml
@@ -57,89 +57,97 @@ cases:
       )
     expected: ../storage/expected/trace-users-detail.yml
   # service metrics
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics single 
--name=service_sla --service-name=e2e-service-provider
+    expected: ../storage/expected/metrics-single-sla.yml
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics nullable 
--name=service_sla --service-name=e2e-service-provider
+    expected: ../storage/expected/metrics-nullable-single-sla.yml
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics nullable 
--name=service_sla --service-name=e2e-service-provider --start "2023-01-08" 
--end "2023-01-09"
+    expected: ../storage/expected/metrics-nullable-single-sla-empty.yml
   - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics sorted --name 
service_apdex 5
     expected: ../storage/expected/metrics-top-service.yml
   - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name 
service_sla 5
     expected: ../storage/expected/metrics-top-service.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_sla --service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_sla --service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_cpm --service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_cpm --service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_resp_time --service-name=e2e-service-provider |yq e 'to_entries' 
-
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_resp_time --service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_apdex --service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_apdex --service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_sla --service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_sla --service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_cpm --service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_cpm --service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_resp_time --service-name=e2e-service-consumer |yq e 'to_entries' 
-
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_resp_time --service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_apdex --service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_apdex --service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
   # service instance metrics
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_instance_resp_time --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_instance_resp_time --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_instance_cpm --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_instance_cpm --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_instance_sla --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_instance_sla --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
   # service instance JVM metrics
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap_max --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap_max --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_noheap --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_noheap --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_live_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_live_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_daemon_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_daemon_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_peak_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_peak_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_runnable_state_thread_count 
--instance-name=consumer1 --service-name=e2e-service-consumer |yq e 
'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_runnable_state_thread_count 
--instance-name=consumer1 --service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_loaded_class_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_loaded_class_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_total_loaded_class_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_total_loaded_class_count 
--instance-name=consumer1 --service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap_max --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap_max --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_noheap --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_noheap --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_live_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_live_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_daemon_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_daemon_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_peak_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_peak_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_runnable_state_thread_count 
--instance-name=provider1 --service-name=e2e-service-provider |yq e 
'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_runnable_state_thread_count 
--instance-name=provider1 --service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_loaded_class_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_loaded_class_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_total_loaded_class_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_total_loaded_class_count 
--instance-name=provider1 --service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
   # service endpoint metrics
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-provider | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-provider | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-provider | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-provider
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-consumer | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-consumer | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-consumer | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value.yml
   # service endpoint metrics percentile
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics multiple-linear 
--name=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer |yq e 'to_entries | with(.[] ; 
.value=(.value | to_entries))' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value-percentile.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics multiple-linear 
--name=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer |yq e 'to_entries | with(.[] ; 
.value=(.value | to_entries))' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: ../storage/expected/metrics-has-value-percentile.yml
-
+  # Endpoint TopN with service_id
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name 
endpoint_cpm --service-name e2e-service-provider 5
+    expected: ../storage/expected/metrics-top-endpoint.yml
   # native event: event list
   - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql event list
     expected: ../storage/expected/event-list.yml
diff --git 
a/test/e2e-v2/cases/storage/expected/metrics-has-value-percentile.yml 
b/test/e2e-v2/cases/storage/expected/metrics-has-value-percentile.yml
index c360120b..0e662e43 100644
--- a/test/e2e-v2/cases/storage/expected/metrics-has-value-percentile.yml
+++ b/test/e2e-v2/cases/storage/expected/metrics-has-value-percentile.yml
@@ -13,65 +13,23 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-{{- contains . }}
-- key: 0
-  value:
-  {{- contains .value }}
-  - key: {{ notEmpty .key }}
-    value:
-      value: 0
-      isemptyvalue: true
-  - key: {{ notEmpty .key }}
-    value:
-      value: {{ ge .value.value 1 }}
-      isemptyvalue: false
-  {{- end }}
-- key: 1
-  value:
-  {{- contains .value }}
-  - key: {{ notEmpty .key }}
-    value:
-      value: 0
-      isemptyvalue: true
-  - key: {{ notEmpty .key }}
-    value:
-      value: {{ ge .value.value 1 }}
-      isemptyvalue: false
-  {{- end }}
-- key: 2
-  value:
-  {{- contains .value }}
-  - key: {{ notEmpty .key }}
-    value:
-      value: 0
-      isemptyvalue: true
-  - key: {{ notEmpty .key }}
-    value:
-      value: {{ ge .value.value 1 }}
-      isemptyvalue: false
-  {{- end }}
-- key: 3
-  value:
-  {{- contains .value }}
-  - key: {{ notEmpty .key }}
-    value:
-      value: 0
-      isemptyvalue: true
-  - key: {{ notEmpty .key }}
-    value:
-      value: {{ ge .value.value 1 }}
-      isemptyvalue: false
-  {{- end }}
-- key: 4
-  value:
-  {{- contains .value }}
-  - key: {{ notEmpty .key }}
-    value:
-      value: 0
-      isemptyvalue: true
-  - key: {{ notEmpty .key }}
-    value:
-      value: {{ ge .value.value 1 }}
-      isemptyvalue: false
-  {{- end }}
-{{- end }}
+type: TIME_SERIES_VALUES
+results:
+  {{- contains .results }}
+  - metric:
+      labels:
+        {{- contains .metric.labels }}
+        - key: p
+          value: {{ .value }}
+        {{- end}}
+    values:
+      {{- contains .values }}
+      - id: {{ notEmpty .id }}
+        value: {{ .value }}
+        traceid: null
+      - id: {{ notEmpty .id }}
+        value: null
+        traceid: null
+      {{- end}}
+  {{- end}}
+error: null
diff --git a/test/e2e-v2/cases/storage/expected/metrics-has-value.yml 
b/test/e2e-v2/cases/storage/expected/metrics-has-value.yml
index ceb5b007..dc71f566 100644
--- a/test/e2e-v2/cases/storage/expected/metrics-has-value.yml
+++ b/test/e2e-v2/cases/storage/expected/metrics-has-value.yml
@@ -13,13 +13,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-{{- contains . }}
-- key: {{ notEmpty .key }}
-  value:
-    value: 0
-    isemptyvalue: true
-- key: {{ notEmpty .key }}
-  value:
-    value: {{ ge .value.value 1 }}
-    isemptyvalue: false
-{{- end }}
\ No newline at end of file
+type: TIME_SERIES_VALUES
+results:
+  {{- contains .results }}
+  - metric:
+      labels: []
+    values:
+      {{- contains .values }}
+      - id: {{ notEmpty .id }}
+        value: {{ .value }}
+        traceid: null
+      - id: {{ notEmpty .id }}
+        value: null
+        traceid: null
+      {{- end}}
+  {{- end}}
+error: null
diff --git a/test/stress/trace/env 
b/test/e2e-v2/cases/storage/expected/metrics-top-endpoint.yml
similarity index 87%
copy from test/stress/trace/env
copy to test/e2e-v2/cases/storage/expected/metrics-top-endpoint.yml
index 3acd4a0a..a89a98d7 100644
--- a/test/stress/trace/env
+++ b/test/e2e-v2/cases/storage/expected/metrics-top-endpoint.yml
@@ -13,5 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-SW_OAP_COMMIT=cec72540e712e0c9a56c18171fb3e98897b27f11
+{{- contains . }}
+- name: {{ notEmpty .name }}
+  id: ""
+  value: '{{- gt .value "0" }}'
+  refid: ~
+{{- end }}
diff --git a/test/e2e-v2/cases/storage/storage-cases.yaml 
b/test/e2e-v2/cases/storage/storage-cases.yaml
index 520fbe42..f7a06f39 100644
--- a/test/e2e-v2/cases/storage/storage-cases.yaml
+++ b/test/e2e-v2/cases/storage/storage-cases.yaml
@@ -57,89 +57,97 @@ cases:
       )
     expected: expected/trace-users-detail.yml
   # service metrics
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics single 
--name=service_sla --service-name=e2e-service-provider
+    expected: expected/metrics-single-sla.yml
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics nullable 
--name=service_sla --service-name=e2e-service-provider
+    expected: expected/metrics-nullable-single-sla.yml
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics nullable 
--name=service_sla --service-name=e2e-service-provider --start "2023-01-08" 
--end "2023-01-09"
+    expected: expected/metrics-nullable-single-sla-empty.yml
   - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics sorted --name 
service_apdex 5
     expected: expected/metrics-top-service.yml
   - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name 
service_sla 5
     expected: expected/metrics-top-service.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_sla --service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_sla --service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_cpm --service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_cpm --service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_resp_time --service-name=e2e-service-provider |yq e 'to_entries' 
-
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_resp_time --service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_apdex --service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_apdex --service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_sla --service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_sla --service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_cpm --service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_cpm --service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_resp_time --service-name=e2e-service-consumer |yq e 'to_entries' 
-
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_resp_time --service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_apdex --service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_apdex --service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
   # service instance metrics
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_instance_resp_time --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_instance_resp_time --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_instance_cpm --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_instance_cpm --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=service_instance_sla --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=service_instance_sla --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
   # service instance JVM metrics
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap_max --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap_max --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_noheap --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_noheap --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_live_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_live_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_daemon_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_daemon_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_peak_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_peak_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_runnable_state_thread_count 
--instance-name=consumer1 --service-name=e2e-service-consumer |yq e 
'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_runnable_state_thread_count 
--instance-name=consumer1 --service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_loaded_class_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_loaded_class_count --instance-name=consumer1 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_total_loaded_class_count --instance-name=consumer1 
--service-name=e2e-service-consumer |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_total_loaded_class_count 
--instance-name=consumer1 --service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_heap_max --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_heap_max --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_memory_noheap --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_memory_noheap --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_live_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_live_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_daemon_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_daemon_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_peak_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_peak_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_thread_runnable_state_thread_count 
--instance-name=provider1 --service-name=e2e-service-provider |yq e 
'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_thread_runnable_state_thread_count 
--instance-name=provider1 --service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_loaded_class_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_loaded_class_count --instance-name=provider1 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=instance_jvm_class_total_loaded_class_count --instance-name=provider1 
--service-name=e2e-service-provider |yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=instance_jvm_class_total_loaded_class_count 
--instance-name=provider1 --service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
   # service endpoint metrics
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-provider | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-provider | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-provider | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-provider
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-consumer | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_cpm --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-consumer | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_resp_time --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics linear 
--name=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-consumer | yq e 'to_entries' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_sla --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value.yml
   # service endpoint metrics percentile
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics multiple-linear 
--name=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer |yq e 'to_entries | with(.[] ; 
.value=(.value | to_entries))' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value-percentile.yml
-  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics multiple-linear 
--name=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer |yq e 'to_entries | with(.[] ; 
.value=(.value | to_entries))' -
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec 
--expression=endpoint_percentile --endpoint-name=POST:/users 
--service-name=e2e-service-consumer
     expected: expected/metrics-has-value-percentile.yml
-
+  # Endpoint TopN with service_id
+  - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql metrics top --name 
endpoint_cpm --service-name e2e-service-provider 5
+    expected: expected/metrics-top-endpoint.yml
   # native event: event list
   - query: swctl --display yaml 
--base-url=http://${oap_host}:${oap_12800}/graphql event list
     expected: expected/event-list.yml
diff --git a/test/e2e-v2/script/env b/test/e2e-v2/script/env
index e0dc9ddc..f77b1011 100644
--- a/test/e2e-v2/script/env
+++ b/test/e2e-v2/script/env
@@ -25,5 +25,5 @@ 
SW_KUBERNETES_COMMIT_SHA=e2c61c6774cf377b23516fca6f8a1e119d3191c5
 SW_ROVER_COMMIT=fc8d074c6d34ecfee585a7097cbd5aef1ca680a5
 SW_CTL_COMMIT=6b2eb0011e38b630db6af7203db215806bd141ed
 
-SW_OAP_COMMIT=cec72540e712e0c9a56c18171fb3e98897b27f11
+SW_OAP_COMMIT=ad80d9cca7a862b719f7dd8229cb9bfe01b77c3c
 SW_AGENT_E2E_SERVICE_PROVIDER_COMMIT=cc7a2c9e97fd2c421adbe3e9c471688459a446d9
diff --git a/test/integration/load/load_suite_test.go 
b/test/integration/load/load_suite_test.go
index 30f07696..357fcfcc 100644
--- a/test/integration/load/load_suite_test.go
+++ b/test/integration/load/load_suite_test.go
@@ -28,7 +28,6 @@ import (
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
 
-       commonv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
        modelv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
        streamv1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/stream/v1"
        "github.com/apache/skywalking-banyandb/pkg/grpchelper"
@@ -99,10 +98,8 @@ var _ = Describe("Load Test Suit", func() {
                        BaseTime:   now,
                }
                query := &streamv1.QueryRequest{
-                       Metadata: &commonv1.Metadata{
-                               Name:  "sw",
-                               Group: "default",
-                       },
+                       Name:   "sw",
+                       Groups: []string{"default"},
                        Projection: &modelv1.TagProjection{
                                TagFamilies: []*modelv1.TagProjection_TagFamily{
                                        {
diff --git a/test/stress/classic/env b/test/stress/classic/env
index 14a0ae37..90529139 100644
--- a/test/stress/classic/env
+++ b/test/stress/classic/env
@@ -26,7 +26,7 @@ 
SW_KUBERNETES_COMMIT_SHA=e2c61c6774cf377b23516fca6f8a1e119d3191c5
 SW_ROVER_COMMIT=fc8d074c6d34ecfee585a7097cbd5aef1ca680a5
 SW_CTL_COMMIT=6b2eb0011e38b630db6af7203db215806bd141ed
 
-SW_OAP_COMMIT=cec72540e712e0c9a56c18171fb3e98897b27f11
+SW_OAP_COMMIT=ad80d9cca7a862b719f7dd8229cb9bfe01b77c3c
 SW_AGENT_E2E_SERVICE_PROVIDER_COMMIT=cc7a2c9e97fd2c421adbe3e9c471688459a446d9
 
 VUS=10
diff --git a/test/stress/classic/env.dev b/test/stress/classic/env.dev
index 0cd645ce..86eea512 100644
--- a/test/stress/classic/env.dev
+++ b/test/stress/classic/env.dev
@@ -25,7 +25,7 @@ 
SW_KUBERNETES_COMMIT_SHA=e2c61c6774cf377b23516fca6f8a1e119d3191c5
 SW_ROVER_COMMIT=fc8d074c6d34ecfee585a7097cbd5aef1ca680a5
 SW_CTL_COMMIT=6b2eb0011e38b630db6af7203db215806bd141ed
 
-SW_OAP_COMMIT=cec72540e712e0c9a56c18171fb3e98897b27f11
+SW_OAP_COMMIT=ad80d9cca7a862b719f7dd8229cb9bfe01b77c3c
 SW_AGENT_E2E_SERVICE_PROVIDER_COMMIT=cc7a2c9e97fd2c421adbe3e9c471688459a446d9
 
 VUS=1
diff --git a/test/stress/trace/env b/test/stress/trace/env
index 3acd4a0a..864bcd53 100644
--- a/test/stress/trace/env
+++ b/test/stress/trace/env
@@ -14,4 +14,4 @@
 # limitations under the License.
 
 
-SW_OAP_COMMIT=cec72540e712e0c9a56c18171fb3e98897b27f11
+SW_OAP_COMMIT=ad80d9cca7a862b719f7dd8229cb9bfe01b77c3c
diff --git a/ui/src/components/Read/index.vue b/ui/src/components/Read/index.vue
index 908d8b80..b11452a1 100644
--- a/ui/src/components/Read/index.vue
+++ b/ui/src/components/Read/index.vue
@@ -107,13 +107,8 @@ const shortcuts = [
     },
 ]
 const param = {
-    metadata: {
-        group: '',
-        name: '',
-        createRevision: '',
-        modRevision: '',
-        id: ''
-    },
+    groups: [],
+    name: '',
     offset: null,
     limit: null,
     //criteria: {},
@@ -250,7 +245,8 @@ function getTableData() {
     }
     /* paramList.offset = data.queryInfo.pagenum
     paramList.limit = data.queryInfo.pagesize */
-    paramList.metadata = data.resourceData.metadata
+    paramList.name = data.resourceData.metadata.name
+    paramList.groups = [data.resourceData.metadata.group]
     getTableList(paramList, data.type)
         .then((res) => {
             if (res.status == 200) {

Reply via email to