blob: 993574f82050e169246d2736502b020b9676ede3 [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_idx_view).
-export([
validate/1,
add/2,
remove/2,
from_ddoc/1,
to_json/1,
columns/1,
start_key/1,
end_key/1
]).
-include_lib("couch/include/couch_db.hrl").
-include("mango.hrl").
-include("mango_idx.hrl").
validate(#idx{}=Idx) ->
{ok, Def} = do_validate(Idx#idx.def),
{ok, Idx#idx{def=Def}}.
add(#doc{body={Props0}}=DDoc, Idx) ->
Views1 = case proplists:get_value(<<"views">>, Props0) of
{Views0} -> Views0;
_ -> []
end,
NewView = make_view(Idx),
Views2 = lists:keystore(element(1, NewView), 1, Views1, NewView),
Props1 = lists:keystore(<<"views">>, 1, Props0, {<<"views">>, {Views2}}),
{ok, DDoc#doc{body={Props1}}}.
remove(#doc{body={Props0}}=DDoc, Idx) ->
Views1 = case proplists:get_value(<<"views">>, Props0) of
{Views0} ->
Views0;
_ ->
?MANGO_ERROR({index_not_found, Idx#idx.name})
end,
Views2 = lists:keydelete(Idx#idx.name, 1, Views1),
if Views2 /= Views1 -> ok; true ->
?MANGO_ERROR({index_not_found, Idx#idx.name})
end,
Props1 = case Views2 of
[] ->
lists:keydelete(<<"views">>, 1, Props0);
_ ->
lists:keystore(<<"views">>, 1, Props0, {<<"views">>, {Views2}})
end,
{ok, DDoc#doc{body={Props1}}}.
from_ddoc({Props}) ->
case lists:keyfind(<<"views">>, 1, Props) of
{<<"views">>, {Views}} when is_list(Views) ->
lists:flatmap(fun({Name, {VProps}}) ->
Def = proplists:get_value(<<"map">>, VProps),
{Opts0} = proplists:get_value(<<"options">>, VProps),
Opts = lists:keydelete(<<"sort">>, 1, Opts0),
I = #idx{
type = <<"json">>,
name = Name,
def = Def,
opts = Opts
},
% TODO: Validate the index definition
[I]
end, Views);
_ ->
[]
end.
to_json(Idx) ->
{[
{ddoc, Idx#idx.ddoc},
{name, Idx#idx.name},
{type, Idx#idx.type},
{def, {def_to_json(Idx#idx.def)}}
]}.
columns(Idx) ->
{Props} = Idx#idx.def,
{<<"fields">>, {Fields}} = lists:keyfind(<<"fields">>, 1, Props),
[Key || {Key, _} <- Fields].
start_key([]) ->
[];
start_key([{'$gt', Key, _, _} | Rest]) ->
case mango_json:special(Key) of
true ->
[];
false ->
[Key | start_key(Rest)]
end;
start_key([{'$gte', Key, _, _} | Rest]) ->
false = mango_json:special(Key),
[Key | start_key(Rest)];
start_key([{'$eq', Key, '$eq', Key} | Rest]) ->
false = mango_json:special(Key),
[Key | start_key(Rest)].
end_key([]) ->
[{[]}];
end_key([{_, _, '$lt', Key} | Rest]) ->
case mango_json:special(Key) of
true ->
[{[]}];
false ->
[Key | end_key(Rest)]
end;
end_key([{_, _, '$lte', Key} | Rest]) ->
false = mango_json:special(Key),
[Key | end_key(Rest)];
end_key([{'$eq', Key, '$eq', Key} | Rest]) ->
false = mango_json:special(Key),
[Key | end_key(Rest)].
do_validate({Props}) ->
{ok, Opts} = mango_opts:validate(Props, opts()),
{ok, {Opts}};
do_validate(Else) ->
?MANGO_ERROR({invalid_index_json, Else}).
def_to_json({Props}) ->
def_to_json(Props);
def_to_json([]) ->
[];
def_to_json([{fields, Fields} | Rest]) ->
[{<<"fields">>, mango_sort:to_json(Fields)} | def_to_json(Rest)];
def_to_json([{<<"fields">>, Fields} | Rest]) ->
[{<<"fields">>, mango_sort:to_json(Fields)} | def_to_json(Rest)];
def_to_json([{Key, Value} | Rest]) ->
[{Key, Value} | def_to_json(Rest)].
opts() ->
[
{<<"fields">>, [
{tag, fields},
{validator, fun mango_opts:validate_sort/1}
]}
].
make_view(Idx) ->
View = {[
{<<"map">>, Idx#idx.def},
{<<"reduce">>, <<"_count">>},
{<<"options">>, {Idx#idx.opts}}
]},
{Idx#idx.name, View}.