diff options
author | Gabor Pali <gabor.pali@ibm.com> | 2023-03-20 18:13:14 +0100 |
---|---|---|
committer | Nick Vatamaniuc <nickva@users.noreply.github.com> | 2023-04-18 23:51:32 -0400 |
commit | 42deb8471dd3dbea0c88f65b8bc34ab28211148b (patch) | |
tree | 3a244ee86cf98401ed190de5ef15d4487a9974d3 | |
parent | c83b5ccb6757dafdee982deb3dfe7bb3cee9f67f (diff) | |
download | couchdb-42deb8471dd3dbea0c88f65b8bc34ab28211148b.tar.gz |
mango: add type information for better self-documentation
-rw-r--r-- | src/mango/src/mango.hrl | 12 | ||||
-rw-r--r-- | src/mango/src/mango_cursor_view.erl | 83 | ||||
-rw-r--r-- | src/mango/src/mango_idx_view.erl | 1 |
3 files changed, 88 insertions, 8 deletions
diff --git a/src/mango/src/mango.hrl b/src/mango/src/mango.hrl index d50d17b6f..2ff07aa4b 100644 --- a/src/mango/src/mango.hrl +++ b/src/mango/src/mango.hrl @@ -12,6 +12,8 @@ -define(MANGO_ERROR(R), throw({mango_error, ?MODULE, R})). +-type maybe(A) :: A | undefined. + -type abstract_text_selector() :: {'op_and', [abstract_text_selector()]} | {'op_or', [abstract_text_selector()]} | {'op_not', {abstract_text_selector(), abstract_text_selector()}} @@ -21,3 +23,13 @@ | {'op_null', {_, _}} | {'op_default', _} | {'op_regex', binary()}. + +-type database() :: binary(). +-type field() :: binary(). +-type fields() :: all_fields | [field()]. +-type selector() :: any(). +-type ejson() :: {[{atom(), any()}]}. + +-type shard_stats() :: {docs_examined, non_neg_integer()}. +-type row_property_key() :: id | key | value | doc. +-type row_properties() :: [{row_property_key(), any()}]. diff --git a/src/mango/src/mango_cursor_view.erl b/src/mango/src/mango_cursor_view.erl index 8e79f608e..80b7fe205 100644 --- a/src/mango/src/mango_cursor_view.erl +++ b/src/mango/src/mango_cursor_view.erl @@ -30,15 +30,38 @@ -include_lib("couch_mrview/include/couch_mrview.hrl"). -include_lib("fabric/include/fabric.hrl"). +-include("mango.hrl"). -include("mango_cursor.hrl"). -include("mango_idx.hrl"). -include("mango_idx_view.hrl"). -define(HEARTBEAT_INTERVAL_IN_USEC, 4000000). +-type cursor_options() :: [{term(), term()}]. +-type message() :: + {meta, _} + | {row, row_properties()} + | {execution_stats, shard_stats()} + | {stop, #cursor{}} + | {complete, #cursor{}} + | {error, any()}. + % viewcbargs wraps up the arguments that view_cb uses into a single % entry in the mrargs.extra list. We use a Map to allow us to later % add fields without having old messages causing errors/crashes. + +-type viewcbargs() :: + #{ + selector => selector(), + fields => fields(), + covering_index => maybe(#idx{}) + }. + +-spec viewcbargs_new(Selector, Fields, CoveringIndex) -> ViewCBArgs when + Selector :: selector(), + Fields :: fields(), + CoveringIndex :: maybe(#idx{}), + ViewCBArgs :: viewcbargs(). viewcbargs_new(Selector, Fields, CoveringIndex) -> #{ selector => Selector, @@ -46,6 +69,9 @@ viewcbargs_new(Selector, Fields, CoveringIndex) -> covering_index => CoveringIndex }. +-spec viewcbargs_get(Key, Args) -> maybe(term()) when + Key :: selector | fields | covering_index, + Args :: viewcbargs(). viewcbargs_get(selector, Args) when is_map(Args) -> maps:get(selector, Args, undefined); viewcbargs_get(fields, Args) when is_map(Args) -> @@ -53,6 +79,11 @@ viewcbargs_get(fields, Args) when is_map(Args) -> viewcbargs_get(covering_index, Args) when is_map(Args) -> maps:get(covering_index, Args, undefined). +-spec create(Db, Indexes, Selector, Options) -> {ok, #cursor{}} when + Db :: database(), + Indexes :: [#idx{}], + Selector :: selector(), + Options :: cursor_options(). create(Db, Indexes, Selector, Opts) -> FieldRanges = mango_idx_view:field_ranges(Selector), Composited = composite_indexes(Indexes, FieldRanges), @@ -77,6 +108,7 @@ create(Db, Indexes, Selector, Opts) -> bookmark = Bookmark }}. +-spec explain(#cursor{}) -> nonempty_list(term()). explain(#cursor{opts = Opts} = Cursor) -> BaseArgs = base_args(Cursor), Args0 = apply_opts(Opts, BaseArgs), @@ -117,6 +149,7 @@ maybe_replace_max_json([H | T] = EndKey) when is_list(EndKey) -> maybe_replace_max_json(EndKey) -> EndKey. +-spec base_args(#cursor{}) -> #mrargs{}. base_args(#cursor{index = Idx, selector = Selector, fields = Fields} = Cursor) -> {StartKey, EndKey} = case Cursor#cursor.ranges of @@ -153,6 +186,10 @@ base_args(#cursor{index = Idx, selector = Selector, fields = Fields} = Cursor) - ] }. +-spec execute(#cursor{}, UserFunction, UserAccumulator) -> Result when + UserFunction :: fun(), + UserAccumulator :: any(), + Result :: {ok, UserAccumulator} | {error, any()}. execute(#cursor{db = Db, index = Idx, execution_stats = Stats} = Cursor0, UserFun, UserAcc) -> Cursor = Cursor0#cursor{ user_fun = UserFun, @@ -201,11 +238,15 @@ execute(#cursor{db = Db, index = Idx, execution_stats = Stats} = Cursor0, UserFu end end. +-type comparator() :: '$lt' | '$lte' | '$eq' | '$gte' | '$gt'. +-type range() :: {comparator(), any(), comparator(), any()} | empty. + % Any of these indexes may be a composite index. For each % index find the most specific set of fields for each % index. Ie, if an index has columns a, b, c, d, then % check FieldRanges for a, b, c, and d and return % the longest prefix of columns found. +-spec composite_indexes([#idx{}], [{field(), range()}]) -> [{#idx{}, [range()], integer()}]. composite_indexes(Indexes, FieldRanges) -> lists:foldl( fun(Idx, Acc) -> @@ -221,6 +262,7 @@ composite_indexes(Indexes, FieldRanges) -> Indexes ). +-spec composite_prefix([field()], [{field(), range()}]) -> [range()]. composite_prefix([], _) -> []; composite_prefix([Col | Rest], Ranges) -> @@ -242,9 +284,6 @@ composite_prefix([Col | Rest], Ranges) -> % In the future we can look into doing a cached parallel % reduce view read on each index with the ranges to find % the one that has the fewest number of rows or something. --type comparator() :: '$lt' | '$lte' | '$eq' | '$gte' | '$gt'. --type range() :: {comparator(), any(), comparator(), any()} | empty. - -spec choose_best_index(IndexRanges) -> Selection when IndexRanges :: nonempty_list({#idx{}, [range()], integer()}), Selection :: {#idx{}, [range()]}. @@ -275,6 +314,11 @@ choose_best_index(IndexRanges) -> {SelectedIndex, SelectedIndexRanges, _} = hd(lists:sort(Cmp, IndexRanges)), {SelectedIndex, SelectedIndexRanges}. +-spec view_cb + (Message, #mrargs{}) -> Response when + Message :: {meta, any()} | {row, row_properties()} | complete, + Response :: {ok, #mrargs{}}; + (ok, ddoc_updated) -> any(). view_cb({meta, Meta}, Acc) -> % Map function starting put(mango_docs_examined, 0), @@ -346,11 +390,11 @@ view_cb(ok, ddoc_updated) -> %% match_and_extract_doc checks whether Doc matches Selector. If it does, %% extract Fields and return {match, FinalDoc}; otherwise return {no_match, undefined}. --spec match_and_extract_doc( - Doc :: term(), - Selector :: term(), - Fields :: [string()] | undefined | all_fields -) -> {match | no_match, term() | undefined}. +-spec match_and_extract_doc(Doc, Selector, Fields) -> Result when + Doc :: ejson(), + Selector :: selector(), + Fields :: maybe(fields()), + Result :: {match, term()} | {no_match, undefined}. match_and_extract_doc(Doc, Selector, Fields) -> case mango_selector:match(Selector, Doc) of true -> @@ -360,6 +404,7 @@ match_and_extract_doc(Doc, Selector, Fields) -> {no_match, undefined} end. +-spec derive_doc_from_index(#idx{}, #view_row{}) -> term(). derive_doc_from_index(Index, #view_row{id = DocId, key = Keys}) -> Columns = mango_idx:columns(Index), lists:foldr( @@ -368,6 +413,7 @@ derive_doc_from_index(Index, #view_row{id = DocId, key = Keys}) -> lists:zip(Columns, Keys) ). +-spec maybe_send_mango_ping() -> ok | term(). maybe_send_mango_ping() -> Current = os:timestamp(), LastPing = get(mango_last_msg_timestamp), @@ -381,9 +427,14 @@ maybe_send_mango_ping() -> set_mango_msg_timestamp() end. +-spec set_mango_msg_timestamp() -> term(). set_mango_msg_timestamp() -> put(mango_last_msg_timestamp, os:timestamp()). +-spec handle_message(message(), #cursor{}) -> Response when + Response :: + {ok, #cursor{}} + | {error, any()}. handle_message({meta, _}, Cursor) -> {ok, Cursor}; handle_message({row, Props}, Cursor) -> @@ -414,6 +465,10 @@ handle_message(complete, Cursor) -> handle_message({error, Reason}, _Cursor) -> {error, Reason}. +-spec handle_all_docs_message(message(), #cursor{}) -> Response when + Response :: + {ok, #cursor{}} + | {error, any()}. handle_all_docs_message({row, Props}, Cursor) -> case is_design_doc(Props) of true -> {ok, Cursor}; @@ -422,6 +477,8 @@ handle_all_docs_message({row, Props}, Cursor) -> handle_all_docs_message(Message, Cursor) -> handle_message(Message, Cursor). +-spec handle_doc(#cursor{}, doc()) -> Response when + Response :: {ok, #cursor{}} | {stop, #cursor{}}. handle_doc(#cursor{skip = S} = C, _) when S > 0 -> {ok, C#cursor{skip = S - 1}}; handle_doc(#cursor{limit = L, execution_stats = Stats} = C, Doc) when L > 0 -> @@ -436,6 +493,7 @@ handle_doc(#cursor{limit = L, execution_stats = Stats} = C, Doc) when L > 0 -> handle_doc(C, _Doc) -> {stop, C}. +-spec ddocid(#idx{}) -> binary(). ddocid(Idx) -> case mango_idx:ddoc(Idx) of <<"_design/", Rest/binary>> -> @@ -444,6 +502,7 @@ ddocid(Idx) -> Else end. +-spec apply_opts(cursor_options(), #mrargs{}) -> #mrargs{}. apply_opts([], Args) -> Args; apply_opts([{r, RStr} | Rest], Args) -> @@ -512,10 +571,16 @@ apply_opts([{_, _} | Rest], Args) -> % Ignore unknown options apply_opts(Rest, Args). +-spec consider_index_coverage(#idx{}, fields(), #mrargs{}) -> #mrargs{}. consider_index_coverage(Index, Fields, #mrargs{include_docs = IncludeDocs0} = Args) -> IncludeDocs = IncludeDocs0 andalso (not mango_idx_view:covers(Index, Fields)), Args#mrargs{include_docs = IncludeDocs}. +-spec doc_member_and_extract(#cursor{}, row_properties()) -> Result when + Result :: + {ok | no_match, term(), {execution_stats, shard_stats()}} + | {no_match, null, {execution_stats, shard_stats()}} + | any(). doc_member_and_extract(Cursor, RowProps) -> Db = Cursor#cursor.db, Opts = Cursor#cursor.opts, @@ -554,12 +619,14 @@ doc_member_and_extract(Cursor, RowProps) -> {no_match, null, {execution_stats, ExecutionStats}} end. +-spec is_design_doc(row_properties()) -> boolean(). is_design_doc(RowProps) -> case couch_util:get_value(id, RowProps) of <<"_design/", _/binary>> -> true; _ -> false end. +-spec update_bookmark_keys(#cursor{}, row_properties()) -> #cursor{}. update_bookmark_keys(#cursor{limit = Limit} = Cursor, Props) when Limit > 0 -> Id = couch_util:get_value(id, Props), Key = couch_util:get_value(key, Props), diff --git a/src/mango/src/mango_idx_view.erl b/src/mango/src/mango_idx_view.erl index 3ef410e12..d3444a0e2 100644 --- a/src/mango/src/mango_idx_view.erl +++ b/src/mango/src/mango_idx_view.erl @@ -527,6 +527,7 @@ can_use_sort([Col | RestCols], SortFields, Selector) -> % There is no information available about the full set of fields which % comes the following consequences: an index cannot (reliably) cover % an "all fields" type of query and nested fields are out of scope. +-spec covers(#idx{}, fields()) -> boolean(). covers(_, all_fields) -> false; covers(Idx, Fields) -> |