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

iilyak pushed a commit to branch couch-stats-resource-tracker-v3-rebase-http-2
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit b0bad086fbb0d952892d1605ee3c102c73b5bef3
Author: ILYA Khlopotov <iil...@apache.org>
AuthorDate: Wed Jun 25 11:03:56 2025 -0700

    Implement csrt:query/2 and csrt:query/4
---
 src/couch_stats/src/csrt.erl       |  23 +++++---
 src/couch_stats/src/csrt_query.erl | 105 +++++++++++++++++++++++++++++++------
 2 files changed, 105 insertions(+), 23 deletions(-)

diff --git a/src/couch_stats/src/csrt.erl b/src/couch_stats/src/csrt.erl
index 08678d248..1864f9f44 100644
--- a/src/couch_stats/src/csrt.erl
+++ b/src/couch_stats/src/csrt.erl
@@ -92,8 +92,8 @@
     find_workers_by_pidref/1,
     group_by/2,
     group_by/3,
-    query/1,
     query/2,
+    query/4,
     query_matcher/1,
     query_matcher/2,
     sorted/1,
@@ -495,14 +495,21 @@ query_matcher(MatcherName) ->
 query_matcher(MatcherName, Limit) ->
     csrt_query:query_matcher(MatcherName, Limit).
 
--spec query(Keys :: string() | [string()], Options :: query_options()) -> {ok, 
query_result()}
+-spec query(MatcherName :: string(), Keys :: binary() | rctx_field() | 
[binary()] | [rctx_field()]) ->
+    {ok, query_result()}
     | {error, any()}.
-query(Keys) ->
-    csrt_query:query(Keys).
-
-%% #{{<<"adm">>,<<"bench-yktbb3as46rzffea">>} => 2}
-query(Keys, Options) ->
-    csrt_query:query(Keys, Options).
+query(MatcherName, AggregationKeys) ->
+    csrt_query:query(MatcherName, AggregationKeys).
+
+-spec query(MatcherName, AggregationKeys, ValueKey, Options :: 
query_options()) ->
+    {ok, query_result()}
+    | {error, any()}
+when
+    MatcherName :: string(),
+    AggregationKeys :: binary() | rctx_field() | [binary()] | [rctx_field()],
+    ValueKey :: binary() | rctx_field().
+query(MatcherName, AggregationKeys, ValueKey, Options) ->
+    csrt_query:query(MatcherName, AggregationKeys, ValueKey, Options).
 
 sorted(Map) ->
     csrt_query:sorted(Map).
diff --git a/src/couch_stats/src/csrt_query.erl 
b/src/couch_stats/src/csrt_query.erl
index b461fedcb..75536c85d 100644
--- a/src/couch_stats/src/csrt_query.erl
+++ b/src/couch_stats/src/csrt_query.erl
@@ -12,6 +12,8 @@
 
 -module(csrt_query).
 
+-feature(maybe_expr, enable).
+
 -include_lib("stdlib/include/ms_transform.hrl").
 -include_lib("couch_stats_resource_tracker.hrl").
 
@@ -32,8 +34,8 @@
     find_workers_by_pidref/1,
     group_by/3,
     group_by/4,
-    query/1,
     query/2,
+    query/4,
     query_matcher/1,
     query_matcher/2,
     query_matcher_rows/1,
@@ -270,11 +272,11 @@ query_matcher(MatcherName) when is_list(MatcherName)  ->
 -spec query_matcher(MatcherName :: matcher_name(), Limit :: pos_integer()) -> 
{ok, query_result()}
     | {error, any()}.
 query_matcher(MatcherName, Limit) when is_list(MatcherName) andalso 
is_integer(Limit) ->
-    case csrt_logger:get_matcher(MatcherName) of
-        undefined ->
-            {error, {unknown_matcher, MatcherName}};
-        Matcher ->
-            query_matcher_rows(Matcher, Limit)
+    case get_matcher(MatcherName) of
+        {ok, Matcher} ->
+            query_matcher_rows(Matcher, Limit);
+        Error ->
+            Error
     end.
 
 -spec query_matcher_rows(Matcher :: matcher()) -> {ok, query_result()}
@@ -302,17 +304,90 @@ query_matcher_rows({MSpec, _CompMSpec}, Limit) when 
is_list(MSpec) andalso is_in
             {error, Error}
     end.
 
--spec query(Keys :: string() | [string()]) -> {ok, query_result()}
-    | {error, any()}.
-%% #{{<<"adm">>,<<"bench-yktbb3as46rzffea">>} => 2}
-query(Keys) ->
-    query(Keys, []).
+get_matcher(MatcherName) ->
+    case csrt_logger:get_matcher(MatcherName) of
+        undefined ->
+            {error, {unknown_matcher, MatcherName}};
+        Matcher ->
+            {ok, Matcher}
+    end.
 
--spec query(Keys :: string() | [string()], Options :: query_options()) -> {ok, 
query_result()}
+-spec query(MatcherName :: string(), Keys :: binary() | rctx_field() | 
[binary()] | [rctx_field()]) ->
+    {ok, query_result()}
     | {error, any()}.
-%% #{{<<"adm">>,<<"bench-yktbb3as46rzffea">>} => 2}
-query(_Keys, _Options) ->
-    {error, todo}.
+%% {ok, #{{<<"adm">>,<<"bench-yktbb3as46rzffea">>} => 2}}
+query(MatcherName, AggregationKeys) ->
+    query(MatcherName, AggregationKeys, undefined, #{}).
+
+-spec query(MatcherName, AggregationKeys, ValueKey, Options :: 
query_options()) ->
+    {ok, query_result()}
+    | {error, any()}
+when
+    MatcherName :: string(),
+    AggregationKeys :: binary() | rctx_field() | [binary()] | [rctx_field()],
+    ValueKey :: binary() | rctx_field().
+%% {ok, #{{<<"adm">>,<<"bench-yktbb3as46rzffea">>} => 2}}
+query(MatcherName, AggregationKeys, ValueKey, Options) ->
+    AggregationKey = parse_key(AggregationKeys),
+    VKey = parse_key(ValueKey),
+    Limit = maps:get(Options, aggregation, query_limit()),
+    Aggregation = maps:get(Options, aggregation, none),
+    maybe
+        ok ?= validate_limit(Limit),
+        ok ?= validate_aggregation(Aggregation),
+        {ok, Matcher} ?= get_matcher(MatcherName),
+        case Aggregation of
+            group_by ->
+                group_by(Matcher, AggregationKey, VKey);
+            sort_by ->
+                sort_by(Matcher, AggregationKey, VKey);
+            count_by when VKey == undefined ->
+                count_by(Matcher, AggregationKey);
+            count_by ->
+                {error, {extra_argument, value_key}};
+            none ->
+                query_matcher_rows(Matcher, Limit)
+        end
+    end.
 
 query_limit() ->
     config:get_integer(?CSRT, "query_limit", ?QUERY_LIMIT).
+
+validate_limit(Limit) when is_integer(Limit) ->
+    case Limit =< query_limit() of
+        true ->
+            ok;
+        false ->
+            {error, {beyond_limit, query_limit()}}
+    end;
+validate_limit(Limit) ->
+    {error, {invalid_limit, Limit}}.
+
+validate_aggregation(none) ->
+    ok;
+validate_aggregation(group_by) ->
+    ok;
+validate_aggregation(sort_by) ->
+    ok;
+validate_aggregation(count_by) ->
+    ok;
+validate_aggregation(Other) ->
+    {error, {invalid_aggregation, Other}}.
+
+-spec parse_key(Keys :: binary() | atom() | [binary()] | [atom()]) ->
+    [rctx_field()]
+    | throw({bad_request, Reason :: binary()}).
+
+parse_key(Keys) when is_list(Keys) ->
+    parse_key(Keys, []);
+parse_key(BinKey) when is_binary(BinKey) ->
+    csrt_entry:key(BinKey);
+parse_key(undefined) ->
+    undefined;
+parse_key(Key) when is_atom(Key) ->
+    csrt_entry:key(Key).
+
+parse_key([BinKey | Rest], Keys) ->
+    parse_key(Rest, [csrt_entry:key(BinKey) | Keys]);
+parse_key([], Keys) ->
+    lists:reverse(Keys).

Reply via email to