diff options
Diffstat (limited to 'src/mango/src/mango_cursor.erl')
-rw-r--r-- | src/mango/src/mango_cursor.erl | 256 |
1 files changed, 0 insertions, 256 deletions
diff --git a/src/mango/src/mango_cursor.erl b/src/mango/src/mango_cursor.erl deleted file mode 100644 index b1cb4148e..000000000 --- a/src/mango/src/mango_cursor.erl +++ /dev/null @@ -1,256 +0,0 @@ -% 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). - - --export([ - create/3, - explain/1, - execute/3, - maybe_filter_indexes_by_ddoc/2, - remove_indexes_with_partial_filter_selector/1, - maybe_add_warning/4, - maybe_noop_range/2 -]). - - --include_lib("couch/include/couch_db.hrl"). --include("mango.hrl"). --include("mango_cursor.hrl"). --include("mango_idx.hrl"). - - --ifdef(HAVE_DREYFUS). --define(CURSOR_MODULES, [ - mango_cursor_view, - mango_cursor_text, - mango_cursor_special -]). --else. --define(CURSOR_MODULES, [ - mango_cursor_view, - mango_cursor_special -]). --endif. - --define(SUPERVISOR, mango_cursor_sup). - - -create(Db, Selector0, Opts) -> - Selector = mango_selector:normalize(Selector0), - UsableIndexes = mango_idx:get_usable_indexes(Db, Selector, Opts), - case mango_cursor:maybe_filter_indexes_by_ddoc(UsableIndexes, Opts) of - [] -> - % use_index doesn't match a valid index - fall back to a valid one - create_cursor(Db, UsableIndexes, Selector, Opts); - UserSpecifiedIndex -> - create_cursor(Db, UserSpecifiedIndex, Selector, Opts) - end. - - -explain(#cursor{}=Cursor) -> - #cursor{ - index = Idx, - selector = Selector, - opts = Opts0, - limit = Limit, - skip = Skip, - fields = Fields - } = Cursor, - Mod = mango_idx:cursor_mod(Idx), - Opts = lists:keydelete(user_ctx, 1, Opts0), - {[ - {dbname, mango_idx:dbname(Idx)}, - {index, mango_idx:to_json(Idx)}, - {partitioned, mango_idx:partitioned(Idx)}, - {selector, Selector}, - {opts, {Opts}}, - {limit, Limit}, - {skip, Skip}, - {fields, Fields} - ] ++ Mod:explain(Cursor)}. - - -execute(#cursor{index=Idx}=Cursor, UserFun, UserAcc) -> - Mod = mango_idx:cursor_mod(Idx), - Mod:execute(Cursor, UserFun, UserAcc). - - -maybe_filter_indexes_by_ddoc(Indexes, Opts) -> - case lists:keyfind(use_index, 1, Opts) of - {use_index, []} -> - []; - {use_index, [DesignId]} -> - filter_indexes(Indexes, DesignId); - {use_index, [DesignId, ViewName]} -> - filter_indexes(Indexes, DesignId, ViewName) - end. - - -filter_indexes(Indexes, DesignId0) -> - DesignId = case DesignId0 of - <<"_design/", _/binary>> -> - DesignId0; - Else -> - <<"_design/", Else/binary>> - end, - FiltFun = fun(I) -> mango_idx:ddoc(I) == DesignId end, - lists:filter(FiltFun, Indexes). - - -filter_indexes(Indexes0, DesignId, ViewName) -> - Indexes = filter_indexes(Indexes0, DesignId), - FiltFun = fun(I) -> mango_idx:name(I) == ViewName end, - lists:filter(FiltFun, Indexes). - - -remove_indexes_with_partial_filter_selector(Indexes) -> - FiltFun = fun(Idx) -> - case mango_idx:get_partial_filter_selector(Idx) of - undefined -> true; - _ -> false - end - end, - lists:filter(FiltFun, Indexes). - - -maybe_add_warning(UserFun, #cursor{index = Index, opts = Opts}, Stats, UserAcc) -> - W0 = invalid_index_warning(Index, Opts), - W1 = no_index_warning(Index), - W2 = index_scan_warning(Stats), - Warnings = lists:append([W0, W1, W2]), - case Warnings of - [] -> - UserAcc; - _ -> - WarningStr = iolist_to_binary(lists:join(<<"\n">>, Warnings)), - Arg = {add_key, warning, WarningStr}, - {_Go, UserAcc1} = UserFun(Arg, UserAcc), - UserAcc1 - end. - - -create_cursor(Db, Indexes, Selector, Opts) -> - [{CursorMod, CursorModIndexes} | _] = group_indexes_by_type(Indexes), - CursorMod:create(Db, CursorModIndexes, Selector, Opts). - - -group_indexes_by_type(Indexes) -> - IdxDict = lists:foldl(fun(I, D) -> - dict:append(mango_idx:cursor_mod(I), I, D) - end, dict:new(), Indexes), - % The first cursor module that has indexes will be - % used to service this query. This is so that we - % don't suddenly switch indexes for existing client - % queries. - lists:flatmap(fun(CMod) -> - case dict:find(CMod, IdxDict) of - {ok, CModIndexes} -> - [{CMod, CModIndexes}]; - error -> - [] - end - end, ?CURSOR_MODULES). - - -% warn if the _all_docs index was used to fulfil a query -no_index_warning(#idx{type = Type}) when Type =:= <<"special">> -> - couch_stats:increment_counter([mango, unindexed_queries]), - [<<"No matching index found, create an index to optimize query time.">>]; - -no_index_warning(_) -> - []. - - -% warn if user specified an index which doesn't exist or isn't valid -% for the selector. -% In this scenario, Mango will ignore the index hint and auto-select an index. -invalid_index_warning(Index, Opts) -> - UseIndex = lists:keyfind(use_index, 1, Opts), - invalid_index_warning_int(Index, UseIndex). - - -invalid_index_warning_int(Index, {use_index, [DesignId]}) -> - Filtered = filter_indexes([Index], DesignId), - if Filtered /= [] -> []; true -> - couch_stats:increment_counter([mango, query_invalid_index]), - Reason = fmt("_design/~s was not used because it does not contain a valid index for this query.", - [ddoc_name(DesignId)]), - [Reason] - end; - -invalid_index_warning_int(Index, {use_index, [DesignId, ViewName]}) -> - Filtered = filter_indexes([Index], DesignId, ViewName), - if Filtered /= [] -> []; true -> - couch_stats:increment_counter([mango, query_invalid_index]), - Reason = fmt("_design/~s, ~s was not used because it is not a valid index for this query.", - [ddoc_name(DesignId), ViewName]), - [Reason] - end; - -invalid_index_warning_int(_, _) -> - []. - - -% warn if a large number of documents needed to be scanned per result -% returned, implying a lot of in-memory filtering -index_scan_warning(#execution_stats { - totalDocsExamined = Docs, - totalQuorumDocsExamined = DocsQuorum, - resultsReturned = ResultCount - }) -> - % Docs and DocsQuorum are mutually exclusive so it's safe to sum them - DocsScanned = Docs + DocsQuorum, - Ratio = calculate_index_scan_ratio(DocsScanned, ResultCount), - Threshold = config:get_integer("mango", "index_scan_warning_threshold", 10), - case Threshold > 0 andalso Ratio > Threshold of - true -> - couch_stats:increment_counter([mango, too_many_docs_scanned]), - Reason = <<"The number of documents examined is high in proportion to the number of results returned. Consider adding a more specific index to improve this.">>, - [Reason]; - false -> [] - end. - -% When there is an empty array for certain operators, we don't actually -% want to execute the query so we deny it by making the range [empty]. -% To clarify, we don't want this query to execute: {"$or": []}. Results should -% be empty. We do want this query to execute: {"age": 22, "$or": []}. It should -% return the same results as {"age": 22} -maybe_noop_range({[{Op, []}]}, IndexRanges) -> - Noops = [<<"$all">>, <<"$and">>, <<"$or">>, <<"$in">>], - case lists:member(Op, Noops) of - true -> - [empty]; - false -> - IndexRanges - end; -maybe_noop_range(_, IndexRanges) -> - IndexRanges. - - -calculate_index_scan_ratio(DocsScanned, 0) -> - DocsScanned; - -calculate_index_scan_ratio(DocsScanned, ResultCount) -> - DocsScanned / ResultCount. - - -fmt(Format, Args) -> - iolist_to_binary(io_lib:format(Format, Args)). - - -ddoc_name(<<"_design/", Name/binary>>) -> - Name; - -ddoc_name(Name) -> - Name. |