summaryrefslogtreecommitdiff
path: root/src/mango/src/mango_doc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mango/src/mango_doc.erl')
-rw-r--r--src/mango/src/mango_doc.erl543
1 files changed, 0 insertions, 543 deletions
diff --git a/src/mango/src/mango_doc.erl b/src/mango/src/mango_doc.erl
deleted file mode 100644
index f8cb4c63b..000000000
--- a/src/mango/src/mango_doc.erl
+++ /dev/null
@@ -1,543 +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_doc).
-
--export([
- from_bson/1,
-
- apply_update/2,
- update_as_insert/1,
- has_operators/1,
-
- get_field/2,
- get_field/3,
- rem_field/2,
- set_field/3
-]).
-
--include_lib("couch/include/couch_db.hrl").
--include("mango.hrl").
-
-from_bson({Props}) ->
- DocProps =
- case lists:keytake(<<"_id">>, 1, Props) of
- {value, {<<"_id">>, DocId0}, RestProps} ->
- DocId =
- case DocId0 of
- {[{<<"$id">>, Id}]} ->
- Id;
- Else ->
- Else
- end,
- [{<<"_id">>, DocId} | RestProps];
- false ->
- Props
- end,
- Doc = couch_doc:from_json_obj({DocProps}),
- case Doc#doc.id of
- <<"">> ->
- Doc#doc{id = couch_uuids:new(), revs = {0, []}};
- _ ->
- Doc
- end.
-
-apply_update(#doc{body = {Props}} = Doc, Update) ->
- NewProps = apply_update(Props, Update),
- Doc#doc{body = {NewProps}};
-apply_update({Props}, {Update}) ->
- Result = do_update({Props}, Update),
- case has_operators(Result) of
- true ->
- ?MANGO_ERROR(update_leaves_operators);
- false ->
- ok
- end,
- Result.
-
-update_as_insert({Update}) ->
- NewProps = do_update_to_insert(Update, {[]}),
- apply_update(NewProps, {Update}).
-
-has_operators(#doc{body = Body}) ->
- has_operators(Body);
-has_operators({Props}) when is_list(Props) ->
- has_operators_obj(Props);
-has_operators(Arr) when is_list(Arr) ->
- has_operators_arr(Arr);
-has_operators(Val) when is_atom(Val) ->
- false;
-has_operators(Val) when is_number(Val) ->
- false;
-has_operators(Val) when is_binary(Val) ->
- false.
-
-has_operators_obj([]) ->
- false;
-has_operators_obj([{K, V} | Rest]) ->
- case K of
- <<"$", _/binary>> ->
- true;
- _ ->
- case has_operators(V) of
- true ->
- true;
- false ->
- has_operators_obj(Rest)
- end
- end.
-
-has_operators_arr([]) ->
- false;
-has_operators_arr([V | Rest]) ->
- case has_operators(V) of
- true ->
- true;
- false ->
- has_operators_arr(Rest)
- end.
-
-do_update(Props, []) ->
- Props;
-do_update(Props, [{Op, Value} | Rest]) ->
- UpdateFun = update_operator_fun(Op),
- NewProps =
- case UpdateFun of
- undefined ->
- lists:keystore(Op, 1, Props, {Op, Value});
- Fun when is_function(Fun, 2) ->
- case Value of
- {ValueProps} ->
- Fun(Props, ValueProps);
- _ ->
- ?MANGO_ERROR({invalid_operand, Op, Value})
- end
- end,
- do_update(NewProps, Rest).
-
-update_operator_fun(<<"$", _/binary>> = Op) ->
- OperatorFuns = [
- % Object operators
- {<<"$inc">>, fun do_update_inc/2},
- {<<"$rename">>, fun do_update_rename/2},
- {<<"$setOnInsert">>, fun do_update_set_on_insert/2},
- {<<"$set">>, fun do_update_set/2},
- {<<"$unset">>, fun do_update_unset/2},
-
- % Array opereators
- {<<"$addToSet">>, fun do_update_add_to_set/2},
- {<<"$pop">>, fun do_update_pop/2},
- {<<"$pullAll">>, fun do_update_pull_all/2},
- {<<"$pull">>, fun do_update_pull/2},
- {<<"$pushAll">>, fun do_update_push_all/2},
- {<<"$push">>, fun do_update_push/2},
-
- % Bitwise Operators
- {<<"$bit">>, fun do_update_bitwise/2}
- ],
- case lists:keyfind(Op, 1, OperatorFuns) of
- {Op, Fun} ->
- Fun;
- false ->
- ?MANGO_ERROR({update_operator_not_supported, Op})
- end;
-update_operator_fun(_) ->
- undefined.
-
-do_update_inc(Props, []) ->
- Props;
-do_update_inc(Props, [{Field, Incr} | Rest]) ->
- if
- is_number(Incr) -> ok;
- true -> ?MANGO_ERROR({invalid_increment, Incr})
- end,
- NewProps =
- case get_field(Props, Field, fun is_number/1) of
- Value when is_number(Value) ->
- set_field(Props, Field, Value + Incr);
- not_found ->
- set_field(Props, Field, Incr);
- _ ->
- Props
- end,
- do_update_inc(NewProps, Rest).
-
-do_update_rename(Props, []) ->
- Props;
-do_update_rename(Props, [{OldField, NewField} | Rest]) ->
- NewProps =
- case rem_field(Props, OldField) of
- {RemProps, OldValue} ->
- set_field(RemProps, NewField, OldValue);
- _ ->
- Props
- end,
- do_update_rename(NewProps, Rest).
-
-do_update_set_on_insert(Props, _) ->
- % This is only called during calls to apply_update/2
- % which means this isn't an insert, so drop it on
- % the floor.
- Props.
-
-do_update_set(Props, []) ->
- Props;
-do_update_set(Props, [{Field, Value} | Rest]) ->
- NewProps = set_field(Props, Field, Value),
- do_update_set(NewProps, Rest).
-
-do_update_unset(Props, []) ->
- Props;
-do_update_unset(Props, [{Field, _} | Rest]) ->
- NewProps =
- case rem_field(Props, Field) of
- {RemProps, _} ->
- RemProps;
- _ ->
- Props
- end,
- do_update_unset(NewProps, Rest).
-
-do_update_add_to_set(Props, []) ->
- Props;
-do_update_add_to_set(Props, [{Field, NewValue} | Rest]) ->
- ToAdd =
- case NewValue of
- {[{<<"$each">>, NewValues}]} when is_list(NewValues) ->
- NewValues;
- {[{<<"$each">>, NewValue}]} ->
- [NewValue];
- Else ->
- [Else]
- end,
- NewProps =
- case get_field(Props, Field) of
- OldValues when is_list(OldValues) ->
- FinalValues = lists:foldl(
- fun(V, Acc) ->
- lists:append(Acc, [V])
- end,
- OldValues,
- ToAdd
- ),
- set_field(Props, Field, FinalValues);
- _ ->
- Props
- end,
- do_update_add_to_set(NewProps, Rest).
-
-do_update_pop(Props, []) ->
- Props;
-do_update_pop(Props, [{Field, Pos} | Rest]) ->
- NewProps =
- case get_field(Props, Field) of
- OldValues when is_list(OldValues) ->
- NewValues =
- case Pos > 0 of
- true ->
- lists:sublist(OldValues, 1, length(OldValues) - 1);
- false ->
- lists:sublist(OldValues, 2, length(OldValues) - 1)
- end,
- set_field(Props, Field, NewValues);
- _ ->
- Props
- end,
- do_update_pop(NewProps, Rest).
-
-do_update_pull_all(Props, []) ->
- Props;
-do_update_pull_all(Props, [{Field, Values} | Rest]) ->
- ToRem =
- case is_list(Values) of
- true -> Values;
- false -> [Values]
- end,
- NewProps =
- case get_field(Props, Field) of
- OldValues when is_list(OldValues) ->
- NewValues = lists:foldl(
- fun(ValToRem, Acc) ->
- % The logic in these filter functions is a bit
- % subtle. The way to think of this is that we
- % return true for all elements we want to keep.
- FilterFun =
- case has_operators(ValToRem) of
- true ->
- fun(A) ->
- Sel = mango_selector:normalize(ValToRem),
- not mango_selector:match(A, Sel)
- end;
- false ->
- fun(A) -> A /= ValToRem end
- end,
- lists:filter(FilterFun, Acc)
- end,
- OldValues,
- ToRem
- ),
- set_field(Props, Field, NewValues);
- _ ->
- Props
- end,
- do_update_add_to_set(NewProps, Rest).
-
-do_update_pull(Props, []) ->
- Props;
-do_update_pull(Props, [{Field, Value} | Rest]) ->
- ToRem =
- case Value of
- {[{<<"$each">>, Values}]} when is_list(Values) ->
- Values;
- {[{<<"$each">>, Value}]} ->
- [Value];
- Else ->
- [Else]
- end,
- NewProps = do_update_pull_all(Props, [{Field, ToRem}]),
- do_update_pull(NewProps, Rest).
-
-do_update_push_all(_, []) ->
- [];
-do_update_push_all(Props, [{Field, Values} | Rest]) ->
- ToAdd =
- case is_list(Values) of
- true -> Values;
- false -> [Values]
- end,
- NewProps =
- case get_field(Props, Field) of
- OldValues when is_list(OldValues) ->
- NewValues = OldValues ++ ToAdd,
- set_field(Props, Field, NewValues);
- _ ->
- Props
- end,
- do_update_push_all(NewProps, Rest).
-
-do_update_push(Props, []) ->
- Props;
-do_update_push(Props, [{Field, Value} | Rest]) ->
- ToAdd =
- case Value of
- {[{<<"$each">>, Values}]} when is_list(Values) ->
- Values;
- {[{<<"$each">>, Value}]} ->
- [Value];
- Else ->
- [Else]
- end,
- NewProps = do_update_push_all(Props, [{Field, ToAdd}]),
- do_update_push(NewProps, Rest).
-
-do_update_bitwise(Props, []) ->
- Props;
-do_update_bitwise(Props, [{Field, Value} | Rest]) ->
- DoOp =
- case Value of
- {[{<<"and">>, Val}]} when is_integer(Val) ->
- fun(V) -> V band Val end;
- {[{<<"or">>, Val}]} when is_integer(Val) ->
- fun(V) -> V bor Val end;
- _ ->
- fun(V) -> V end
- end,
- NewProps =
- case get_field(Props, Field, fun is_number/1) of
- Value when is_number(Value) ->
- NewValue = DoOp(Value),
- set_field(Props, Field, NewValue);
- _ ->
- Props
- end,
- do_update_bitwise(NewProps, Rest).
-
-do_update_to_insert([], Doc) ->
- Doc;
-do_update_to_insert([{<<"$setOnInsert">>, {FieldProps}}], Doc) ->
- lists:foldl(
- fun({Field, Value}, DocAcc) ->
- set_field(DocAcc, Field, Value)
- end,
- Doc,
- FieldProps
- );
-do_update_to_insert([{_, _} | Rest], Doc) ->
- do_update_to_insert(Rest, Doc).
-
-get_field(Props, Field) ->
- get_field(Props, Field, no_validation).
-
-get_field(Props, Field, Validator) when is_binary(Field) ->
- {ok, Path} = mango_util:parse_field(Field),
- get_field(Props, Path, Validator);
-get_field(Props, [], no_validation) ->
- Props;
-get_field(Props, [], Validator) ->
- case (catch Validator(Props)) of
- true ->
- Props;
- _ ->
- invalid_value
- end;
-get_field({Props}, [Name | Rest], Validator) ->
- case lists:keyfind(Name, 1, Props) of
- {Name, Value} ->
- get_field(Value, Rest, Validator);
- false ->
- not_found
- end;
-get_field(Values, [Name | Rest], Validator) when is_list(Values) ->
- % Name might be an integer index into an array
- try
- Pos = list_to_integer(binary_to_list(Name)),
- case Pos >= 0 andalso Pos < length(Values) of
- true ->
- % +1 because Erlang uses 1 based list indices
- Value = lists:nth(Pos + 1, Values),
- get_field(Value, Rest, Validator);
- false ->
- bad_path
- end
- catch
- error:badarg ->
- bad_path
- end;
-get_field(_, [_ | _], _) ->
- bad_path.
-
-rem_field(Props, Field) when is_binary(Field) ->
- {ok, Path} = mango_util:parse_field(Field),
- rem_field(Props, Path);
-rem_field({Props}, [Name]) ->
- case lists:keytake(Name, 1, Props) of
- {value, Value, NewProps} ->
- {NewProps, Value};
- false ->
- not_found
- end;
-rem_field({Props}, [Name | Rest]) ->
- case lists:keyfind(Name, 1, Props) of
- {Name, Value} ->
- case rem_field(Value, Rest) of
- {NewValue, Ret} ->
- NewObj = {lists:keystore(Name, 1, Props, {Name, NewValue})},
- {NewObj, Ret};
- Else ->
- Else
- end;
- false ->
- not_found
- end;
-rem_field(Values, [Name]) when is_list(Values) ->
- % Name might be an integer index into an array
- try
- Pos = list_to_integer(binary_to_list(Name)),
- case Pos >= 0 andalso Pos < length(Values) of
- true ->
- % +1 because Erlang uses 1 based list indices
- rem_elem(Pos + 1, Values);
- false ->
- bad_path
- end
- catch
- error:badarg ->
- bad_path
- end;
-rem_field(Values, [Name | Rest]) when is_list(Values) ->
- % Name might be an integer index into an array
- try
- Pos = list_to_integer(binary_to_list(Name)),
- case Pos >= 0 andalso Pos < length(Values) of
- true ->
- % +1 because Erlang uses 1 based list indices
- Value = lists:nth(Pos + 1, Values),
- case rem_field(Value, Rest) of
- {NewValue, Ret} ->
- {set_elem(Pos + 1, Values, NewValue), Ret};
- Else ->
- Else
- end;
- false ->
- bad_path
- end
- catch
- error:badarg ->
- bad_path
- end;
-rem_field(_, [_ | _]) ->
- bad_path.
-
-set_field(Props, Field, Value) when is_binary(Field) ->
- {ok, Path} = mango_util:parse_field(Field),
- set_field(Props, Path, Value);
-set_field({Props}, [Name], Value) ->
- {lists:keystore(Name, 1, Props, {Name, Value})};
-set_field({Props}, [Name | Rest], Value) ->
- case lists:keyfind(Name, 1, Props) of
- {Name, Elem} ->
- Result = set_field(Elem, Rest, Value),
- {lists:keystore(Name, 1, Props, {Name, Result})};
- false ->
- Nested = make_nested(Rest, Value),
- {lists:keystore(Name, 1, Props, {Name, Nested})}
- end;
-set_field(Values, [Name], Value) when is_list(Values) ->
- % Name might be an integer index into an array
- try
- Pos = list_to_integer(binary_to_list(Name)),
- case Pos >= 0 andalso Pos < length(Values) of
- true ->
- % +1 because Erlang uses 1 based list indices
- set_elem(Pos, Values, Value);
- false ->
- Values
- end
- catch
- error:badarg ->
- Values
- end;
-set_field(Values, [Name | Rest], Value) when is_list(Values) ->
- % Name might be an integer index into an array
- try
- Pos = list_to_integer(binary_to_list(Name)),
- case Pos >= 0 andalso Pos < length(Values) of
- true ->
- % +1 because Erlang uses 1 based list indices
- Elem = lists:nth(Pos + 1, Values),
- Result = set_field(Elem, Rest, Value),
- set_elem(Pos, Values, Result);
- false ->
- Values
- end
- catch
- error:badarg ->
- Values
- end;
-set_field(Value, [_ | _], _) ->
- Value.
-
-make_nested([], Value) ->
- Value;
-make_nested([Name | Rest], Value) ->
- {[{Name, make_nested(Rest, Value)}]}.
-
-rem_elem(1, [Value | Rest]) ->
- {Rest, Value};
-rem_elem(I, [Item | Rest]) when I > 1 ->
- {Tail, Value} = rem_elem(I + 1, Rest),
- {[Item | Tail], Value}.
-
-set_elem(1, [_ | Rest], Value) ->
- [Value | Rest];
-set_elem(I, [Item | Rest], Value) when I > 1 ->
- [Item | set_elem(I - 1, Rest, Value)].