Current section

Files

Jump to
gleam_mongo src mongo@crud.erl
Raw

src/mongo@crud.erl

-module(mongo@crud).
-compile([no_auto_import, nowarn_unused_vars]).
-export([count_all/1, count/2, find_many/3, find_one/3, find_by_id/2, find_all/2, update_one/4, update_many/4, delete_one/2, delete_many/2, insert_many/2, insert_one/2]).
-export_type([insert_result/0, update_result/0]).
-type insert_result() :: {insert_result, integer(), list(bson@value:value())}.
-type update_result() :: {update_result, integer(), integer()} |
{upsert_result, integer(), bson@value:value()}.
-spec count_all(mongo@client:collection()) -> {ok, integer()} |
{error, mongo@utils:mongo_error()}.
count_all(Collection) ->
case begin
_pipe = Collection,
mongo@client:execute(
_pipe,
{document,
[{<<"count"/utf8>>, {str, erlang:element(3, Collection)}}]}
)
end of
{ok, [{<<"n"/utf8>>, {integer, N}}, {<<"ok"/utf8>>, Ok}]} ->
case Ok of
{double, 1.0} ->
{ok, N};
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{error, {Code, Msg}} ->
{error, {mongo_error, Code, Msg, null}}
end.
-spec validate_doc(bson@value:value()) -> boolean().
validate_doc(Candidate) ->
case Candidate of
{document, _} ->
true;
_ ->
false
end.
-spec count(mongo@client:collection(), bson@value:value()) -> {ok, integer()} |
{error, mongo@utils:mongo_error()}.
count(Collection, Filter) ->
gleam@bool:guard(
not validate_doc(Filter),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
case begin
_pipe = Collection,
mongo@client:execute(
_pipe,
{document,
[{<<"count"/utf8>>,
{str, erlang:element(3, Collection)}},
{<<"query"/utf8>>, Filter}]}
)
end of
{ok, [{<<"n"/utf8>>, {integer, N}}, {<<"ok"/utf8>>, Ok}]} ->
case Ok of
{double, 1.0} ->
{ok, N};
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{error, {Code, Msg}} ->
{error, {mongo_error, Code, Msg, null}}
end
end
).
-spec find(
mongo@client:collection(),
bson@value:value(),
list(mongo@utils:find_option())
) -> {ok, list(bson@value:value())} | {error, mongo@utils:mongo_error()}.
find(Collection, Filter, Options) ->
gleam@bool:guard(
not validate_doc(Filter),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
Body = gleam@list:fold(
Options,
[{<<"find"/utf8>>, {str, erlang:element(3, Collection)}},
{<<"filter"/utf8>>, Filter}],
fun(Acc, Opt) -> case Opt of
{sort, {document, Sort}} ->
gleam@list:key_set(
Acc,
<<"sort"/utf8>>,
{document, Sort}
);
{projection, {document, Projection}} ->
gleam@list:key_set(
Acc,
<<"projection"/utf8>>,
{document, Projection}
);
{skip, Skip} ->
gleam@list:key_set(
Acc,
<<"skip"/utf8>>,
{integer, Skip}
);
{limit, Limit} ->
gleam@list:key_set(
Acc,
<<"limit"/utf8>>,
{integer, Limit}
)
end end
),
case begin
_pipe = Collection,
mongo@client:execute(_pipe, {document, Body})
end of
{ok, Result} ->
[{<<"cursor"/utf8>>, {document, Result@1}},
{<<"ok"/utf8>>, Ok}] = Result,
_assert_subject = gleam@list:key_find(
Result@1,
<<"firstBatch"/utf8>>
),
{ok, {array, Docs}} = case _assert_subject of
{ok, {array, _}} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"find"/utf8>>,
line => 240})
end,
case Ok of
{double, 1.0} ->
{ok, Docs};
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{error, {Code, Msg}} ->
{error, {mongo_error, Code, Msg, null}}
end
end
).
-spec find_many(
mongo@client:collection(),
bson@value:value(),
list(mongo@utils:find_option())
) -> {ok, list(bson@value:value())} | {error, mongo@utils:mongo_error()}.
find_many(Collection, Filter, Options) ->
_pipe = Collection,
find(_pipe, Filter, Options).
-spec find_one(
mongo@client:collection(),
bson@value:value(),
bson@value:value()
) -> {ok, bson@value:value()} | {error, mongo@utils:mongo_error()}.
find_one(Collection, Filter, Projection) ->
gleam@bool:guard(
not validate_doc(Filter),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
gleam@bool:guard(
not validate_doc(Projection),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
case begin
_pipe = Collection,
find_many(
_pipe,
Filter,
[{limit, 1}, {projection, Projection}]
)
end of
{ok, []} ->
{ok, null};
{ok, [Doc]} ->
{ok, Doc};
{error, Error} ->
{error, Error}
end
end
)
end
).
-spec find_by_id(mongo@client:collection(), binary()) -> {ok,
bson@value:value()} |
{error, mongo@utils:mongo_error()}.
find_by_id(Collection, Id) ->
case bson@object_id:from_string(Id) of
{ok, Id@1} ->
_pipe = Collection,
find_one(
_pipe,
{document, [{<<"_id"/utf8>>, {object_id, Id@1}}]},
{document, []}
);
{error, nil} ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end.
-spec find_all(mongo@client:collection(), list(mongo@utils:find_option())) -> {ok,
list(bson@value:value())} |
{error, mongo@utils:mongo_error()}.
find_all(Collection, Options) ->
_pipe = Collection,
find(_pipe, {document, []}, Options).
-spec update(
mongo@client:collection(),
bson@value:value(),
bson@value:value(),
list(mongo@utils:update_option()),
boolean()
) -> {ok, update_result()} | {error, mongo@utils:mongo_error()}.
update(Collection, Filter, Change, Options, Multi) ->
gleam@bool:guard(
not validate_doc(Filter),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
gleam@bool:guard(
not validate_doc(Change),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
Update = [{<<"q"/utf8>>, Filter},
{<<"u"/utf8>>, Change},
{<<"multi"/utf8>>, {boolean, Multi}}],
Update@1 = begin
_pipe = gleam@list:fold(
Options,
Update,
fun(Acc, Opt) -> case Opt of
upsert ->
gleam@list:key_set(
Acc,
<<"upsert"/utf8>>,
{boolean, true}
);
{array_filters, Filters} ->
gleam@list:key_set(
Acc,
<<"arrayFilters"/utf8>>,
{array, Filters}
)
end end
),
{document, _pipe}
end,
case begin
_pipe@1 = Collection,
mongo@client:execute(
_pipe@1,
{document,
[{<<"update"/utf8>>,
{str, erlang:element(3, Collection)}},
{<<"updates"/utf8>>, {array, [Update@1]}}]}
)
end of
{ok,
[{<<"n"/utf8>>, {integer, N}},
{<<"nModified"/utf8>>, {integer, Modified}},
{<<"ok"/utf8>>, Ok}]} ->
case Ok of
{double, 1.0} ->
{ok, {update_result, N, Modified}};
_ ->
{error,
{mongo_error, -16, <<""/utf8>>, null}}
end;
{ok,
[{<<"n"/utf8>>, {integer, N@1}},
{<<"upserted"/utf8>>,
{array,
[{document,
[{<<"index"/utf8>>, _},
{<<"_id"/utf8>>, Upserted}]}]}},
{<<"nModified"/utf8>>, _},
{<<"ok"/utf8>>, Ok@1}]} ->
case Ok@1 of
{double, 1.0} ->
{ok, {upsert_result, N@1, Upserted}};
_ ->
{error,
{mongo_error, -16, <<""/utf8>>, null}}
end;
{ok,
[{<<"n"/utf8>>, _},
{<<"writeErrors"/utf8>>, {array, Errors}},
{<<"nModified"/utf8>>, _},
{<<"ok"/utf8>>, Ok@2}]} ->
case Ok@2 of
{double, 1.0} ->
_assert_subject = gleam@list:first(Errors),
{ok, Error} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"update"/utf8>>,
line => 316}
)
end,
case Error of
{document,
[{<<"index"/utf8>>, _},
{<<"code"/utf8>>,
{integer, Code}},
{<<"keyPattern"/utf8>>, _},
{<<"keyValue"/utf8>>, Source},
{<<"errmsg"/utf8>>, {str, Msg}}]} ->
{error,
{mongo_error, Code, Msg, Source}};
_ ->
{error,
{mongo_error,
-16,
<<""/utf8>>,
null}}
end;
_ ->
{error,
{mongo_error, -16, <<""/utf8>>, null}}
end;
{error, {Code@1, Msg@1}} ->
{error, {mongo_error, Code@1, Msg@1, null}}
end
end
)
end
).
-spec update_one(
mongo@client:collection(),
bson@value:value(),
bson@value:value(),
list(mongo@utils:update_option())
) -> {ok, update_result()} | {error, mongo@utils:mongo_error()}.
update_one(Collection, Filter, Change, Options) ->
_pipe = Collection,
update(_pipe, Filter, Change, Options, false).
-spec update_many(
mongo@client:collection(),
bson@value:value(),
bson@value:value(),
list(mongo@utils:update_option())
) -> {ok, update_result()} | {error, mongo@utils:mongo_error()}.
update_many(Collection, Filter, Change, Options) ->
_pipe = Collection,
update(_pipe, Filter, Change, Options, true).
-spec delete(mongo@client:collection(), bson@value:value(), boolean()) -> {ok,
integer()} |
{error, mongo@utils:mongo_error()}.
delete(Collection, Filter, Multi) ->
gleam@bool:guard(
not validate_doc(Filter),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
case begin
_pipe = Collection,
mongo@client:execute(
_pipe,
{document,
[{<<"delete"/utf8>>,
{str, erlang:element(3, Collection)}},
{<<"deletes"/utf8>>,
{array,
[{document,
[{<<"q"/utf8>>, Filter},
{<<"limit"/utf8>>,
{integer, case Multi of
true ->
0;
false ->
1
end}}]}]}}]}
)
end of
{ok, [{<<"n"/utf8>>, {integer, N}}, {<<"ok"/utf8>>, Ok}]} ->
case Ok of
{double, 1.0} ->
{ok, N};
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{ok,
[{<<"n"/utf8>>, _},
{<<"writeErrors"/utf8>>, {array, Errors}},
{<<"ok"/utf8>>, Ok@1}]} ->
case Ok@1 of
{double, 1.0} ->
_assert_subject = gleam@list:first(Errors),
{ok, Error} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"delete"/utf8>>,
line => 367})
end,
case Error of
{document,
[{<<"index"/utf8>>, _},
{<<"code"/utf8>>, {integer, Code}},
{<<"keyPattern"/utf8>>, _},
{<<"keyValue"/utf8>>, Source},
{<<"errmsg"/utf8>>, {str, Msg}}]} ->
{error, {mongo_error, Code, Msg, Source}};
_ ->
{error,
{mongo_error, -16, <<""/utf8>>, null}}
end;
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{error, {Code@1, Msg@1}} ->
{error, {mongo_error, Code@1, Msg@1, null}}
end
end
).
-spec delete_one(mongo@client:collection(), bson@value:value()) -> {ok,
integer()} |
{error, mongo@utils:mongo_error()}.
delete_one(Collection, Filter) ->
_pipe = Collection,
delete(_pipe, Filter, false).
-spec delete_many(mongo@client:collection(), bson@value:value()) -> {ok,
integer()} |
{error, mongo@utils:mongo_error()}.
delete_many(Collection, Filter) ->
_pipe = Collection,
delete(_pipe, Filter, true).
-spec validate_all_docs(list(bson@value:value())) -> boolean().
validate_all_docs(Candidates) ->
gleam@list:all(Candidates, fun validate_doc/1).
-spec insert_many(mongo@client:collection(), list(bson@value:value())) -> {ok,
insert_result()} |
{error, mongo@utils:mongo_error()}.
insert_many(Collection, Docs) ->
gleam@bool:guard(
not validate_all_docs(Docs),
{error, {mongo_error, -16, <<""/utf8>>, null}},
fun() ->
Docs@1 = gleam@list:map(Docs, fun(D) -> case D of
{document, Fields} ->
case gleam@list:find(
Fields,
fun(Kv) ->
gleam@pair:first(Kv) =:= <<"_id"/utf8>>
end
) of
{ok, _} ->
D;
{error, nil} ->
Id = bson@object_id:new(),
Fields@1 = gleam@list:prepend(
Fields,
{<<"_id"/utf8>>, {object_id, Id}}
),
{document, Fields@1}
end;
_ ->
D
end end),
Inserted_ids = gleam@list:map(Docs@1, fun(D@1) -> case D@1 of
{document, Fields@2} ->
case gleam@list:find(
Fields@2,
fun(Kv@1) ->
gleam@pair:first(Kv@1) =:= <<"_id"/utf8>>
end
) of
{ok, {_, Id@1}} ->
Id@1;
_ ->
{str, <<""/utf8>>}
end;
_ ->
{str, <<""/utf8>>}
end end),
case begin
_pipe = Collection,
mongo@client:execute(
_pipe,
{document,
[{<<"insert"/utf8>>,
{str, erlang:element(3, Collection)}},
{<<"documents"/utf8>>, {array, Docs@1}}]}
)
end of
{ok, [{<<"n"/utf8>>, {integer, N}}, {<<"ok"/utf8>>, Ok}]} ->
case Ok of
{double, 1.0} ->
{ok, {insert_result, N, Inserted_ids}};
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{ok,
[{<<"n"/utf8>>, _},
{<<"writeErrors"/utf8>>, {array, Errors}},
{<<"ok"/utf8>>, Ok@1}]} ->
case Ok@1 of
{double, 1.0} ->
_assert_subject = gleam@list:first(Errors),
{ok, Error} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"insert_many"/utf8>>,
line => 192})
end,
case Error of
{document,
[{<<"index"/utf8>>, _},
{<<"code"/utf8>>, {integer, Code}},
{<<"keyPattern"/utf8>>, _},
{<<"keyValue"/utf8>>, Source},
{<<"errmsg"/utf8>>, {str, Msg}}]} ->
{error, {mongo_error, Code, Msg, Source}};
_ ->
{error,
{mongo_error, -16, <<""/utf8>>, null}}
end;
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end;
{error, {Code@1, Msg@1}} ->
{error, {mongo_error, Code@1, Msg@1, null}}
end
end
).
-spec insert_one(mongo@client:collection(), bson@value:value()) -> {ok,
bson@value:value()} |
{error, mongo@utils:mongo_error()}.
insert_one(Collection, Doc) ->
case begin
_pipe = Collection,
insert_many(_pipe, [Doc])
end of
{ok, {insert_result, _, [Id]}} ->
{ok, Id};
{error, Error} ->
{error, Error}
end.