Skip to content

Commit 6efdb8a

Browse files
Merge pull request #91 from ankhers/ecto-2
[WIP] Support for Ecto 2.0
2 parents fa6b755 + 39c08cb commit 6efdb8a

12 files changed

Lines changed: 215 additions & 103 deletions

File tree

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ cache:
66
- ~/.mongodb
77

88
elixir:
9-
- 1.1
9+
- 1.2.6
1010

1111
otp_release:
12-
- 18.1
12+
- 18.2
1313

1414
before_install:
1515
- npm install -g mongodb-version-manager

config/test.exs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use Mix.Config
22

3-
config :logger, :handle_otp_reports, true
4-
config :logger, :handle_sasl_reports, true
3+
config :logger,
4+
level: :info,
5+
handle_otp_reports: true
6+
handle_sasl_reports: true

examples/simple/lib/simple.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ defmodule Simple.Repo do
1616
end
1717

1818
defmodule Weather do
19-
use Ecto.Model
19+
use Ecto.Schema
2020

2121
@primary_key {:id, :binary_id, autogenerate: true}
2222

@@ -38,4 +38,18 @@ defmodule Simple do
3838
select: w
3939
Simple.Repo.all(query)
4040
end
41+
42+
def sample_insert do
43+
%Weather{}
44+
|> Ecto.Changeset.change(%{})
45+
|> Simple.Repo.insert()
46+
end
47+
48+
def sample_update do
49+
{:ok, weather} = sample_insert()
50+
51+
weather
52+
|> Ecto.Changeset.change(%{city: "NYC"})
53+
|> Simple.Repo.update
54+
end
4155
end

examples/simple/mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule Simple.Mixfile do
99

1010
def application do
1111
[mod: {Simple.App, []},
12-
applications: [:mongodb_ecto, :ecto]]
12+
applications: [:mongodb_ecto, :ecto, :logger]]
1313
end
1414

1515
defp deps do

lib/mongo_ecto.ex

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -418,13 +418,36 @@ defmodule Mongo.Ecto do
418418
end
419419

420420
@doc false
421-
# TODO: handle date and time
421+
def ensure_all_started(repo, type) do
422+
{_, opts} = repo.__pool__
423+
with {:ok, pool} <- DBConnection.ensure_all_started(opts, type),
424+
{:ok, mongo} <- Application.ensure_all_started(:mongodb, type),
425+
do: {:ok, pool ++ mongo}
426+
end
427+
428+
@doc false
429+
def loaders(:time, type), do: [&load_time/1, type]
430+
def loaders(:date, type), do: [&load_date/1, type]
422431
def loaders(:datetime, type), do: [&load_datetime/1, type]
423432
def loaders(:binary_id, type), do: [&load_objectid/1, type]
424433
def loaders(:uuid, type), do: [&load_binary/1, type]
425434
def loaders(:binary, type), do: [&load_binary/1, type]
426435
def loaders(_base, type), do: [type]
427436

437+
defp load_time(%BSON.DateTime{} = time) do
438+
{{_,_,_}, time} = BSON.DateTime.to_datetime(time)
439+
{:ok, time}
440+
end
441+
defp load_time(_),
442+
do: :error
443+
444+
defp load_date(%BSON.DateTime{} = date) do
445+
{date, {_, _, _, _}} = BSON.DateTime.to_datetime(date)
446+
{:ok, date}
447+
end
448+
defp load_date(_),
449+
do: :error
450+
428451
defp load_datetime(%BSON.DateTime{} = datetime),
429452
do: {:ok, BSON.DateTime.to_datetime(datetime)}
430453
defp load_datetime(_),
@@ -446,13 +469,24 @@ defmodule Mongo.Ecto do
446469
defp load_objectid(_), do: :error
447470

448471
@doc false
449-
# TODO: handle date and time
472+
def dumpers(:time, type), do: [type, &dump_time/1]
473+
def dumpers(:date, type), do: [type, &dump_date/1]
450474
def dumpers(:datetime, type), do: [type, &dump_datetime/1]
451475
def dumpers(:binary_id, type), do: [type, &dump_objectid/1]
452476
def dumpers(:uuid, type), do: [type, &dump_binary(&1, :uuid)]
453477
def dumpers(:binary, type), do: [type, &dump_binary(&1, :generic)]
454478
def dumpers(_base, type), do: [type]
455479

480+
defp dump_time({_, _, _, _} = time),
481+
do: {:ok, BSON.DateTime.from_datetime({{0, 0, 0}, time})}
482+
defp dump_time(_),
483+
do: :error
484+
485+
defp dump_date({_, _, _} = date),
486+
do: {:ok, BSON.DateTime.from_datetime({date, {0, 0, 0, 0}})}
487+
defp dump_date(_),
488+
do: :error
489+
456490
defp dump_datetime({{_, _, _}, {_, _, _, _}} = datetime),
457491
do: {:ok, BSON.DateTime.from_datetime(datetime)}
458492
defp dump_datetime(_),
@@ -520,50 +554,31 @@ defmodule Mongo.Ecto do
520554
end
521555
end
522556

523-
@doc false
524-
def update(_repo, meta, _fields, _filter, {key, :id, _}, _returning, _opts) do
525-
raise ArgumentError,
526-
"MongoDB adapter does not support :id field type in models. " <>
527-
"The #{inspect key} field in #{inspect meta.model} is tagged as such."
528-
end
529-
530-
def update(_repo, meta, _fields, _filter, _autogen, [_|_] = returning, _opts) do
531-
raise ArgumentError,
532-
"MongoDB adapter does not support :read_after_writes in models. " <>
533-
"The following fields in #{inspect meta.model} are tagged as such: #{inspect returning}"
534-
end
535-
536-
def update(repo, meta, fields, filter, {pk, :binary_id, _value}, [], opts) do
537-
normalized = NormalizedQuery.update(meta, fields, filter, pk)
557+
def insert_all(repo, meta, fields, params, returning, opts) do
558+
normalized = NormalizedQuery.insert(meta, params)
538559

539-
Connection.update(repo, normalized, opts)
560+
case Connection.insert_all(repo, normalized, opts) do
561+
{:ok, _} ->
562+
{:ok, []}
563+
other ->
564+
other
565+
end
540566
end
541567

542-
def update(repo, meta, fields, filter, nil, [], opts) do
543-
normalized = NormalizedQuery.update(meta, fields, filter, nil)
568+
@doc false
569+
def update(repo, meta, fields, filters, returning, opts) do
570+
normalized = NormalizedQuery.update(meta, fields, filters)
544571

545572
Connection.update(repo, normalized, opts)
546573
end
547574

548575
@doc false
549-
def delete(_repo, meta, _filter, {key, :id, _}, _opts) do
550-
raise ArgumentError,
551-
"MongoDB adapter does not support :id field type in models. " <>
552-
"The #{inspect key} field in #{inspect meta.model} is tagged as such."
553-
end
554-
555-
def delete(repo, meta, filter, {pk, :binary_id, _value}, opts) do
556-
normalized = NormalizedQuery.delete(meta, filter, pk)
576+
def delete(repo, meta, filter, opts) do
577+
normalized = NormalizedQuery.delete(meta, filter)
557578

558579
Connection.delete(repo, normalized, opts)
559580
end
560581

561-
def delete(repo, meta, fields, filter, nil, [], opts) do
562-
normalized = NormalizedQuery.update(meta, fields, filter, nil)
563-
564-
Connection.update(repo, normalized, opts)
565-
end
566-
567582
defp process_document(document, %{fields: fields, pk: pk}, preprocess) do
568583
document = Conversions.to_ecto_pk(document, pk)
569584

lib/mongo_ecto/connection.ex

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,25 @@ defmodule Mongo.Ecto.Connection do
66
alias Mongo.Ecto.NormalizedQuery.CommandQuery
77
alias Mongo.Ecto.NormalizedQuery.CountQuery
88
alias Mongo.Ecto.NormalizedQuery.AggregateQuery
9+
alias Mongo.Query
910

1011
## Worker
1112

1213
def storage_down(opts) do
1314
opts = Keyword.put(opts, :pool, DBConnection.Connection)
1415

16+
{:ok, pid} = case Application.ensure_started(:mongodb) do
17+
:ok -> {:ok, nil}
18+
{:error, reason} -> Mongo.App.start(nil, nil)
19+
pid -> pid
20+
end
1521
{:ok, conn} = Mongo.start_link(opts)
1622

1723
try do
1824
Mongo.command!(conn, dropDatabase: 1)
1925
:ok
2026
after
27+
if pid, do: Supervisor.stop(pid)
2128
GenServer.stop(conn)
2229
end
2330
end
@@ -27,7 +34,8 @@ defmodule Mongo.Ecto.Connection do
2734
def read(repo, query, opts \\ [])
2835

2936
def read(repo, %ReadQuery{} = query, opts) do
30-
opts = [projection: query.projection, sort: query.order] ++ query.opts ++ opts
37+
projection = Map.put_new(query.projection, :_id, false)
38+
opts = [projection: projection, sort: query.order] ++ query.opts ++ opts
3139
coll = query.coll
3240
query = query.query
3341

@@ -80,8 +88,12 @@ defmodule Mongo.Ecto.Connection do
8088
opts = query.opts ++ opts
8189
query = query.query
8290

83-
%{modified_count: n} = query(repo, :update, [coll, query, command], opts)
84-
n
91+
case query(repo, :update_many, [coll, query, command], opts) do
92+
{:ok, %Mongo.UpdateResult{modified_count: m}} ->
93+
m
94+
{:error, error} ->
95+
check_constraint_errors(error)
96+
end
8597
end
8698

8799
def update(repo, %WriteQuery{} = query, opts) do
@@ -90,7 +102,7 @@ defmodule Mongo.Ecto.Connection do
90102
opts = query.opts ++ opts
91103
query = query.query
92104

93-
case query(repo, :update, [coll, query, command], opts) do
105+
case query(repo, :update_one, [coll, query, command], opts) do
94106
{:ok, %{modified_count: 1}} ->
95107
{:ok, []}
96108
{:ok, _} ->
@@ -111,6 +123,19 @@ defmodule Mongo.Ecto.Connection do
111123
end
112124
end
113125

126+
def insert_all(repo, %WriteQuery{} = query, opts) do
127+
coll = query.coll
128+
command = query.command
129+
opts = query.opts ++ opts
130+
131+
case query(repo, :insert_many, [coll, command], opts) do
132+
{:ok, %{inserted_ids: ids}} ->
133+
{Enum.count(ids), nil}
134+
{:error, error} ->
135+
check_constraint_errors(error)
136+
end
137+
end
138+
114139
def command(repo, %CommandQuery{} = query, opts) do
115140
command = query.command
116141
opts = query.opts ++ opts
@@ -126,17 +151,21 @@ defmodule Mongo.Ecto.Connection do
126151

127152
defp with_log(repo, opts) do
128153
case Keyword.pop(opts, :log, true) do
129-
{true, opts} -> [log: &log(repo, &1)] ++ opts
154+
{true, opts} -> [log: &log(repo, &1, opts)] ++ opts
130155
{false, opts} -> opts
131156
end
132157
end
133158

134-
defp log(repo, entry) do
159+
defp log(repo, entry, opts) do
135160
%{connection_time: query_time, decode_time: decode_time,
136-
pool_time: queue_time, result: result, query: query, params: params} = entry
161+
pool_time: queue_time, result: result,
162+
query: query, params: params} = entry
163+
source = Keyword.get(opts, :source)
164+
137165
repo.__log__(%Ecto.LogEntry{query_time: query_time, decode_time: decode_time,
138166
queue_time: queue_time, result: log_result(result),
139-
params: [], query: &format_query(&1, query, params)})
167+
params: [], query: format_query(query, params),
168+
source: source})
140169
end
141170

142171
defp log_result({:ok, _query, res}), do: {:ok, res}
@@ -160,48 +189,66 @@ defmodule Mongo.Ecto.Connection do
160189
end
161190
end
162191

163-
alias Mongo.Query
164-
165-
# TODO: fix logging
166-
defp format_query(_entry, %Query{action: :command}, [command]) do
192+
defp format_query(%Query{action: :command}, [command]) do
167193
["COMMAND " | inspect(command)]
168194
end
169-
defp format_query(_entry, :insert_one, [coll, doc, _opts]) do
170-
["INSERT", format_part("coll", coll), format_part("document", doc)]
171-
end
172-
defp format_query(_entry, :insert_many, [coll, docs, _opts]) do
173-
["INSERT", format_part("coll", coll), format_part("documents", docs)]
195+
defp format_query(%Query{action: :find, extra: coll}, [query, projection]) do
196+
["FIND",
197+
format_part("coll", coll),
198+
format_part("query", query),
199+
format_part("projection", projection)]
174200
end
175-
defp format_query(_entry, :delete_one, [coll, filter, _opts]) do
176-
["DELETE", format_part("coll", coll), format_part("filter", filter),
177-
format_part("many", false)]
201+
defp format_query(%Query{action: :insert_one, extra: coll}, [doc]) do
202+
["INSERT",
203+
format_part("coll", coll),
204+
format_part("document", doc)]
178205
end
179-
defp format_query(_entry, :delete_many, [coll, filter, _opts]) do
180-
["DELETE", format_part("coll", coll), format_part("filter", filter),
206+
defp format_query(%Query{action: :insert_many, extra: coll}, docs) do
207+
["INSERT",
208+
format_part("coll", coll),
209+
format_part("documents", docs),
181210
format_part("many", true)]
182211
end
183-
defp format_query(_entry, :replace_one, [coll, filter, doc, _opts]) do
184-
["REPLACE", format_part("coll", coll), format_part("filter", filter),
185-
format_part("document", doc)]
212+
defp format_query(%Query{action: :update_one, extra: coll}, [filter, update]) do
213+
["UPDATE",
214+
format_part("coll", coll),
215+
format_part("filter", filter),
216+
format_part("update", update)]
186217
end
187-
defp format_query(_entry, :update_one, [coll, filter, update, _opts]) do
188-
["UPDATE", format_part("coll", coll), format_part("filter", filter),
189-
format_part("update", update), format_part("many", false)]
218+
defp format_query(%Query{action: :update_many, extra: coll}, [filter, update]) do
219+
["UPDATE",
220+
format_part("coll", coll),
221+
format_part("filter", filter),
222+
format_part("update", update),
223+
format_part("many", true)]
190224
end
191-
defp format_query(_entry, :update_many, [coll, filter, update, _opts]) do
192-
["UPDATE", format_part("coll", coll), format_part("filter", filter),
193-
format_part("update", update), format_part("many", true)]
225+
defp format_query(%Query{action: :delete_one, extra: coll}, [filter]) do
226+
["DELETE",
227+
format_part("coll", coll),
228+
format_part("filter", filter)]
194229
end
195-
defp format_query(_entry, :find, [coll, query, projection, _opts]) do
196-
["FIND", format_part("coll", coll), format_part("query", query),
197-
format_part("projection", projection)]
230+
defp format_query(%Query{action: :delete_many, extra: coll}, [filter]) do
231+
["DELETE",
232+
format_part("coll", coll),
233+
format_part("filter", filter),
234+
format_part("many", true)]
198235
end
199-
defp format_query(_entry, :find_rest, [coll, cursor, _opts]) do
236+
defp format_query(%Query{action: :replace_one, extra: coll}, [filter, doc]) do
237+
["REPLACE", format_part("coll", coll), format_part("filter", filter),
238+
format_part("document", doc)]
239+
end
240+
defp format_query(%Query{action: :get_more, extra: coll}, [cursor]) do
200241
["GET_MORE", format_part("coll", coll), format_part("cursor_id", cursor)]
201242
end
202-
defp format_query(_entry, :kill_cursors, [cursors, _opts]) do
243+
defp format_query(%Query{action: :get_more, extra: coll}, []) do
244+
["GET_MORE", format_part("coll", coll), format_part("cursor_id", "")]
245+
end
246+
defp format_query(%Query{action: :kill_cursors, extra: coll}, [cursors]) do
203247
["KILL_CURSORS", format_part("cursor_ids", cursors)]
204248
end
249+
defp format_query(%Query{action: :kill_cursors, extra: coll}, []) do
250+
["KILL_CURSORS", format_part("cursor_ids", "")]
251+
end
205252

206253
defp format_part(name, value) do
207254
[" ", name, "=" | inspect(value)]

0 commit comments

Comments
 (0)