| 
 | 1 | +% Licensed under the Apache License, Version 2.0 (the "License"); you may not  | 
 | 2 | +% use this file except in compliance with the License. You may obtain a copy of  | 
 | 3 | +% the License at  | 
 | 4 | +%  | 
 | 5 | +% http://www.apache.org/licenses/LICENSE-2.0  | 
 | 6 | +%  | 
 | 7 | +% Unless required by applicable law or agreed to in writing, software  | 
 | 8 | +% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT  | 
 | 9 | +% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the  | 
 | 10 | +% License for the specific language governing permissions and limitations under  | 
 | 11 | +% the License.  | 
 | 12 | + | 
 | 13 | +-module(mango_simple_proc).  | 
 | 14 | +-behavior(gen_server).  | 
 | 15 | + | 
 | 16 | +-export([  | 
 | 17 | +    start_link/0,  | 
 | 18 | +    set_timeout/2,  | 
 | 19 | +    prompt/2  | 
 | 20 | +]).  | 
 | 21 | + | 
 | 22 | +-export([  | 
 | 23 | +    init/1,  | 
 | 24 | +    handle_call/3,  | 
 | 25 | +    handle_cast/2  | 
 | 26 | +]).  | 
 | 27 | + | 
 | 28 | +% DDoc Fields  | 
 | 29 | +-define(SELECTOR, <<"selector">>).  | 
 | 30 | +-define(KEYS, <<"keys">>).  | 
 | 31 | +-define(VALUES, <<"values">>).  | 
 | 32 | + | 
 | 33 | +% Proc commands  | 
 | 34 | +-define(RESET, <<"reset">>).  | 
 | 35 | +-define(ADD_FUN, <<"add_fun">>).  | 
 | 36 | +-define(MAP_DOC, <<"map_doc">>).  | 
 | 37 | +-define(REDUCE, <<"reduce">>).  | 
 | 38 | +-define(REREDUCE, <<"rereduce">>).  | 
 | 39 | + | 
 | 40 | +-record(st, {  | 
 | 41 | +    indexes = [],  | 
 | 42 | +    timeout = 5000  | 
 | 43 | +}).  | 
 | 44 | + | 
 | 45 | +-record(idx, {  | 
 | 46 | +    selector,  | 
 | 47 | +    keys,  | 
 | 48 | +    values  | 
 | 49 | +}).  | 
 | 50 | + | 
 | 51 | +start_link() ->  | 
 | 52 | +    gen_server:start_link(?MODULE, [], []).  | 
 | 53 | + | 
 | 54 | +set_timeout(Pid, TimeOut) when is_integer(TimeOut), TimeOut > 0 ->  | 
 | 55 | +    gen_server:call(Pid, {set_timeout, TimeOut}).  | 
 | 56 | + | 
 | 57 | +prompt(Pid, Data) ->  | 
 | 58 | +    gen_server:call(Pid, {prompt, Data}).  | 
 | 59 | + | 
 | 60 | +init(_) ->  | 
 | 61 | +    {ok, #st{}}.  | 
 | 62 | + | 
 | 63 | +handle_call({set_timeout, TimeOut}, _From, #st{} = St) ->  | 
 | 64 | +    {reply, ok, St#st{timeout = TimeOut}};  | 
 | 65 | +handle_call({prompt, [?RESET | _QueryConfig]}, _From, #st{} = St) ->  | 
 | 66 | +    {reply, true, St#st{indexes = []}};  | 
 | 67 | +handle_call({prompt, [?ADD_FUN, IndexInfo | _IgnoreRest]}, _From, #st{} = St) ->  | 
 | 68 | +    #st{indexes = Indexes} = St,  | 
 | 69 | +    case get_index_def(IndexInfo) of  | 
 | 70 | +        #idx{} = Idx -> {reply, true, St#st{indexes = Indexes ++ [Idx]}};  | 
 | 71 | +        undefined -> {reply, true, St}  | 
 | 72 | +    end;  | 
 | 73 | +handle_call({prompt, [?MAP_DOC, Doc]}, _From, St) ->  | 
 | 74 | +    {reply, map_doc(St, mango_json:to_binary(Doc)), St};  | 
 | 75 | +handle_call({prompt, [?REDUCE, RedSrcs, _]}, _From, St) ->  | 
 | 76 | +    {reply, [true, [null || _ <- RedSrcs]], St};  | 
 | 77 | +handle_call({prompt, [?REREDUCE, RedSrcs, _]}, _From, St) ->  | 
 | 78 | +    {reply, [true, [null || _ <- RedSrcs]], St};  | 
 | 79 | +handle_call(Msg, _From, St) ->  | 
 | 80 | +    {stop, {invalid_call, Msg}, {invalid_call, Msg}, St}.  | 
 | 81 | + | 
 | 82 | +handle_cast(garbage_collect, St) ->  | 
 | 83 | +    erlang:garbage_collect(),  | 
 | 84 | +    {noreply, St};  | 
 | 85 | +handle_cast(stop, St) ->  | 
 | 86 | +    {stop, normal, St};  | 
 | 87 | +handle_cast(Msg, St) ->  | 
 | 88 | +    {stop, {invalid_cast, Msg}, St}.  | 
 | 89 | + | 
 | 90 | +map_doc(#st{indexes = Indexes}, Doc) ->  | 
 | 91 | +    lists:map(fun(Idx) -> get_index_entries(Idx, Doc) end, Indexes).  | 
 | 92 | + | 
 | 93 | +get_index_entries(#idx{} = Idx, Doc) ->  | 
 | 94 | +    #idx{selector = Selector, keys = Keys, values = Values} = Idx,  | 
 | 95 | +    case should_index(Selector, Doc) of  | 
 | 96 | +        false -> [];  | 
 | 97 | +        true -> process_doc(Keys, Values, Doc)  | 
 | 98 | +    end.  | 
 | 99 | + | 
 | 100 | +process_doc(Keys, undefined, Doc) ->  | 
 | 101 | +    case get_index_values(Keys, Doc) of  | 
 | 102 | +        [] -> [];  | 
 | 103 | +        [_ | _] = KeyResults -> [[KeyResults, null]]  | 
 | 104 | +    end;  | 
 | 105 | +process_doc(Keys, [_ | _] = Values, Doc) ->  | 
 | 106 | +    case get_index_values(Keys, Doc) of  | 
 | 107 | +        [] ->  | 
 | 108 | +            [];  | 
 | 109 | +        [_ | _] = KeyResults ->  | 
 | 110 | +            case get_index_values(Values, Doc) of  | 
 | 111 | +                [] -> [];  | 
 | 112 | +                [_ | _] = ValueResults -> [[KeyResults, ValueResults]]  | 
 | 113 | +            end  | 
 | 114 | +    end.  | 
 | 115 | + | 
 | 116 | +get_index_values(Fields, Doc) ->  | 
 | 117 | +    MapF = fun(Field) ->  | 
 | 118 | +        case mango_doc:get_field(Doc, Field) of  | 
 | 119 | +            not_found -> not_found;  | 
 | 120 | +            bad_path -> not_found;  | 
 | 121 | +            Value -> Value  | 
 | 122 | +        end  | 
 | 123 | +    end,  | 
 | 124 | +    Results = lists:map(MapF, Fields),  | 
 | 125 | +    case lists:member(not_found, Results) of  | 
 | 126 | +        true -> [];  | 
 | 127 | +        false -> Results  | 
 | 128 | +    end.  | 
 | 129 | + | 
 | 130 | +should_index(Selector, Doc) ->  | 
 | 131 | +    case mango_doc:get_field(Doc, <<"_id">>) of  | 
 | 132 | +        <<"_design/", _/binary>> -> false;  | 
 | 133 | +        _ -> mango_selector:match(Selector, Doc)  | 
 | 134 | +    end.  | 
 | 135 | + | 
 | 136 | +get_selector({IdxProps}) ->  | 
 | 137 | +    case couch_util:get_value(?SELECTOR, IdxProps, {[]}) of  | 
 | 138 | +        {L} = Selector when is_list(L) -> mango_selector:normalize(Selector);  | 
 | 139 | +        _ -> undefined  | 
 | 140 | +    end.  | 
 | 141 | + | 
 | 142 | +get_field({IdxProps}, <<FieldName/binary>>) ->  | 
 | 143 | +    case couch_util:get_value(FieldName, IdxProps) of  | 
 | 144 | +        Fields when is_list(Fields) ->  | 
 | 145 | +            case lists:all(fun is_binary/1, Fields) of  | 
 | 146 | +                true -> Fields;  | 
 | 147 | +                false -> undefined  | 
 | 148 | +            end;  | 
 | 149 | +        _ ->  | 
 | 150 | +            undefined  | 
 | 151 | +    end.  | 
 | 152 | + | 
 | 153 | +get_index_def(IndexInfo) ->  | 
 | 154 | +    Selector = get_selector(IndexInfo),  | 
 | 155 | +    Keys = get_field(IndexInfo, ?KEYS),  | 
 | 156 | +    Values = get_field(IndexInfo, ?VALUES),  | 
 | 157 | +    case {Selector, Keys, Values} of  | 
 | 158 | +        {{_}, [_ | _], [_ | _]} ->  | 
 | 159 | +            #idx{selector = Selector, keys = Keys, values = Values};  | 
 | 160 | +        {{_}, [_ | _], undefined} ->  | 
 | 161 | +            #idx{selector = Selector, keys = Keys, values = undefined};  | 
 | 162 | +        {_, _, _} ->  | 
 | 163 | +            undefined  | 
 | 164 | +    end.  | 
0 commit comments