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 unwrap_doc(
bson@value:value(),
fun((list({binary(), bson@value:value()})) -> {ok, HMG} |
{error, mongo@utils:mongo_error()})
) -> {ok, HMG} | {error, mongo@utils:mongo_error()}.
unwrap_doc(Candidate, Rest) ->
case Candidate of
{document, Doc} ->
Rest(Doc);
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end.
-spec count(mongo@client:collection(), bson@value:value()) -> {ok, integer()} |
{error, mongo@utils:mongo_error()}.
count(Collection, Filter) ->
unwrap_doc(
Filter,
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) ->
unwrap_doc(
Filter,
fun(Doc) ->
Body = case Doc of
[] ->
[{<<"find"/utf8>>, {str, erlang:element(3, Collection)}}];
_ ->
[{<<"find"/utf8>>, {str, erlang:element(3, Collection)}},
{<<"filter"/utf8>>, Filter}]
end,
Body@1 = gleam@list:fold(Options, Body, 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@1})
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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"find"/utf8>>,
line => 247})
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) ->
unwrap_doc(
Filter,
fun(_) ->
unwrap_doc(
Projection,
fun(Doc) ->
Options = case Doc of
[] ->
[{limit, 1}];
_ ->
[{limit, 1}, {projection, Projection}]
end,
case begin
_pipe = Collection,
find_many(_pipe, Filter, Options)
end of
{ok, []} ->
{ok, null};
{ok, [Doc@1]} ->
{ok, Doc@1};
{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) ->
unwrap_doc(
Filter,
fun(_) ->
unwrap_doc(
Change,
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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"update"/utf8>>,
line => 323}
)
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) ->
unwrap_doc(
Filter,
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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"delete"/utf8>>,
line => 374})
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 unwrap_all_docs(
list(bson@value:value()),
fun((list(list({binary(), bson@value:value()}))) -> {ok, HUE} |
{error, mongo@utils:mongo_error()})
) -> {ok, HUE} | {error, mongo@utils:mongo_error()}.
unwrap_all_docs(Candidates, Rest) ->
case gleam@list:try_fold(
Candidates,
[],
fun(Acc, Candidate) ->
unwrap_doc(
Candidate,
fun(Doc) -> {ok, gleam@list:append(Acc, [Doc])} end
)
end
) of
{ok, Docs} ->
Rest(Docs);
_ ->
{error, {mongo_error, -16, <<""/utf8>>, null}}
end.
-spec insert_many(mongo@client:collection(), list(bson@value:value())) -> {ok,
insert_result()} |
{error, mongo@utils:mongo_error()}.
insert_many(Collection, Docs) ->
unwrap_all_docs(
Docs,
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 => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mongo/crud"/utf8>>,
function => <<"insert_many"/utf8>>,
line => 195})
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.