| % 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_crud). |
| |
| -export([ |
| insert/3, |
| find/5, |
| update/4, |
| delete/3, |
| explain/3 |
| ]). |
| |
| -export([ |
| collect_cb/2 |
| ]). |
| |
| |
| -include_lib("couch/include/couch_db.hrl"). |
| -include("mango.hrl"). |
| |
| |
| insert(Db, #doc{}=Doc, Opts) -> |
| insert(Db, [Doc], Opts); |
| insert(Db, {_}=Doc, Opts) -> |
| insert(Db, [Doc], Opts); |
| insert(Db, Docs, Opts0) when is_list(Docs) -> |
| Opts1 = maybe_add_user_ctx(Db, Opts0), |
| Opts2 = maybe_int_to_str(w, Opts1), |
| case fabric:update_docs(Db, Docs, Opts2) of |
| {ok, Results0} -> |
| {ok, lists:zipwith(fun result_to_json/2, Docs, Results0)}; |
| {accepted, Results0} -> |
| {ok, lists:zipwith(fun result_to_json/2, Docs, Results0)}; |
| {aborted, Errors} -> |
| {error, lists:map(fun result_to_json/1, Errors)} |
| end. |
| |
| |
| find(Db, Selector, Callback, UserAcc, Opts0) -> |
| Opts1 = maybe_add_user_ctx(Db, Opts0), |
| Opts2 = maybe_int_to_str(r, Opts1), |
| {ok, Cursor} = mango_cursor:create(Db, Selector, Opts2), |
| mango_cursor:execute(Cursor, Callback, UserAcc). |
| |
| |
| update(Db, Selector, Update, Options) -> |
| Upsert = proplists:get_value(upsert, Options), |
| case collect_docs(Db, Selector, Options) of |
| {ok, []} when Upsert -> |
| InitDoc = mango_doc:update_as_insert(Update), |
| case mango_doc:has_operators(InitDoc) of |
| true -> |
| ?MANGO_ERROR(invalid_upsert_with_operators); |
| false -> |
| % Probably need to catch and rethrow errors from |
| % this function. |
| Doc = couch_doc:from_json_obj(InitDoc), |
| NewDoc = case Doc#doc.id of |
| <<"">> -> |
| Doc#doc{id=couch_uuids:new(), revs={0, []}}; |
| _ -> |
| Doc |
| end, |
| insert(Db, NewDoc, Options) |
| end; |
| {ok, Docs} -> |
| NewDocs = lists:map(fun(Doc) -> |
| mango_doc:apply_update(Doc, Update) |
| end, Docs), |
| insert(Db, NewDocs, Options); |
| Else -> |
| Else |
| end. |
| |
| |
| delete(Db, Selector, Options) -> |
| case collect_docs(Db, Selector, Options) of |
| {ok, Docs} -> |
| NewDocs = lists:map(fun({Props}) -> |
| {[ |
| {<<"_id">>, proplists:get_value(<<"_id">>, Props)}, |
| {<<"_rev">>, proplists:get_value(<<"_rev">>, Props)}, |
| {<<"_deleted">>, true} |
| ]} |
| end, Docs), |
| insert(Db, NewDocs, Options); |
| Else -> |
| Else |
| end. |
| |
| |
| explain(Db, Selector, Opts0) -> |
| Opts1 = maybe_add_user_ctx(Db, Opts0), |
| Opts2 = maybe_int_to_str(r, Opts1), |
| {ok, Cursor} = mango_cursor:create(Db, Selector, Opts2), |
| mango_cursor:explain(Cursor). |
| |
| |
| maybe_add_user_ctx(Db, Opts) -> |
| case lists:keyfind(user_ctx, 1, Opts) of |
| {user_ctx, _} -> |
| Opts; |
| false -> |
| [{user_ctx, Db#db.user_ctx} | Opts] |
| end. |
| |
| |
| maybe_int_to_str(_Key, []) -> |
| []; |
| maybe_int_to_str(Key, [{Key, Val} | Rest]) when is_integer(Val) -> |
| [{Key, integer_to_list(Val)} | maybe_int_to_str(Key, Rest)]; |
| maybe_int_to_str(Key, [KV | Rest]) -> |
| [KV | maybe_int_to_str(Key, Rest)]. |
| |
| |
| result_to_json(#doc{id=Id}, Result) -> |
| result_to_json(Id, Result); |
| result_to_json({Props}, Result) -> |
| Id = couch_util:get_value(<<"_id">>, Props), |
| result_to_json(Id, Result); |
| result_to_json(DocId, {ok, NewRev}) -> |
| {[ |
| {id, DocId}, |
| {rev, couch_doc:rev_to_str(NewRev)} |
| ]}; |
| result_to_json(DocId, {accepted, NewRev}) -> |
| {[ |
| {id, DocId}, |
| {rev, couch_doc:rev_to_str(NewRev)}, |
| {accepted, true} |
| ]}; |
| result_to_json(DocId, Error) -> |
| % chttpd:error_info/1 because this is coming from fabric |
| % and not internal mango operations. |
| {_Code, ErrorStr, Reason} = chttpd:error_info(Error), |
| {[ |
| {id, DocId}, |
| {error, ErrorStr}, |
| {reason, Reason} |
| ]}. |
| |
| |
| % This is for errors because for some reason we |
| % need a different return value for errors? Blargh. |
| result_to_json({{Id, Rev}, Error}) -> |
| {_Code, ErrorStr, Reason} = chttpd:error_info(Error), |
| {[ |
| {id, Id}, |
| {rev, couch_doc:rev_to_str(Rev)}, |
| {error, ErrorStr}, |
| {reason, Reason} |
| ]}. |
| |
| |
| collect_docs(Db, Selector, Options) -> |
| Cb = fun ?MODULE:collect_cb/2, |
| case find(Db, Selector, Cb, [], Options) of |
| {ok, Docs} -> |
| {ok, lists:reverse(Docs)}; |
| Else -> |
| Else |
| end. |
| |
| |
| collect_cb({row, Doc}, Acc) -> |
| {ok, [Doc | Acc]}. |
| |