blob: c466b5db1686d6cb1e7160de78304828484a9ded [file] [log] [blame]
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.
-module(mango_cursor_special).
-export([
create/4,
explain/1,
execute/3
]).
-export([
handle_message/2
]).
-include("mango.hrl").
-include("mango_cursor.hrl").
-spec create(Db, {Indexes, Trace}, Selector, Options) -> {ok, #cursor{}} when
Db :: database(),
Indexes :: [#idx{}],
Trace :: trace(),
Selector :: selector(),
Options :: cursor_options().
create(Db, {Indexes, Trace0}, Selector, Opts) ->
InitialRange = mango_idx_view:field_ranges(Selector),
CatchAll = [{<<"_id">>, {'$gt', null, '$lt', mango_json_max}}],
% order matters here - we only want to use the catchall index
% if no other range can fulfill the query (because we know)
% catchall is the most expensive range
FieldRanges = InitialRange ++ CatchAll,
Composited = mango_cursor_view:composite_indexes(Indexes, FieldRanges),
{{Index, IndexRanges}, SortedIndexRanges} = mango_cursor_view:choose_best_index(Composited),
Limit = couch_util:get_value(limit, Opts, mango_opts:default_limit()),
Skip = couch_util:get_value(skip, Opts, 0),
Fields = couch_util:get_value(fields, Opts, all_fields),
Bookmark = couch_util:get_value(bookmark, Opts),
Stats = mango_execution_stats:stats_init(couch_db:name(Db)),
IndexRanges1 = mango_cursor:maybe_noop_range(Selector, IndexRanges),
Trace = maps:merge(Trace0, #{sorted_index_ranges => SortedIndexRanges}),
{ok, #cursor{
db = Db,
index = Index,
ranges = IndexRanges1,
trace = Trace,
selector = Selector,
opts = Opts,
limit = Limit,
skip = Skip,
fields = Fields,
bookmark = Bookmark,
execution_stats = Stats
}}.
explain(Cursor) ->
mango_cursor_view:explain(Cursor).
execute(Cursor0, UserFun, UserAcc) ->
mango_cursor_view:execute(Cursor0, UserFun, UserAcc).
handle_message(Msg, Cursor) ->
mango_cursor_view:handle_message(Msg, Cursor).
-ifdef(TEST).
-include_lib("couch/include/couch_eunit.hrl").
create_test_() ->
{
foreach,
fun() ->
meck:expect(couch_db, name, fun(A) when is_atom(A) -> atom_to_binary(A) end)
end,
fun(_) -> meck:unload() end,
[
?TDEF_FE(t_create)
]
}.
t_create(_) ->
Index = #idx{type = <<"special">>, def = all_docs},
Indexes = [Index],
Ranges = [{'$gt', null, '$lt', mango_json_max}],
Trace = #{},
Selector = {[]},
Options = [{limit, limit}, {skip, skip}, {fields, fields}, {bookmark, bookmark}],
Stats = mango_execution_stats:stats_init(couch_db:name(db)),
Cursor =
#cursor{
db = db,
index = Index,
ranges = Ranges,
selector = Selector,
opts = Options,
limit = limit,
skip = skip,
fields = fields,
bookmark = bookmark,
trace = #{sorted_index_ranges => [{Index, Ranges, 0}]},
execution_stats = Stats
},
?assertEqual({ok, Cursor}, create(db, {Indexes, Trace}, Selector, Options)).
misc_test_() ->
{
foreach,
fun() -> meck:new(mango_cursor_view) end,
fun(_) -> meck:unload(mango_cursor_view) end,
[
?TDEF_FE(t_explain),
?TDEF_FE(t_execute),
?TDEF_FE(t_handle_message)
]
}.
t_explain(_) ->
meck:expect(mango_cursor_view, explain, [cursor], meck:val(explain_cursor)),
?assertEqual(explain_cursor, explain(cursor)),
?assert(meck:called(mango_cursor_view, explain, '_')).
t_execute(_) ->
meck:expect(mango_cursor_view, execute, [cursor, userfn, useracc], meck:val(execute_cursor)),
?assertEqual(execute_cursor, execute(cursor, userfn, useracc)),
?assert(meck:called(mango_cursor_view, execute, '_')).
t_handle_message(_) ->
meck:expect(mango_cursor_view, handle_message, [message, cursor], meck:val(result)),
?assertEqual(result, handle_message(message, cursor)),
?assert(meck:called(mango_cursor_view, handle_message, '_')).
-endif.