Skip to content

Commit b43ee0f

Browse files
committed
added collection.ex from Yildun project
1 parent b18f424 commit b43ee0f

File tree

2 files changed

+128
-12
lines changed

2 files changed

+128
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## 0.7.5
22
* Enhancements
33
* replica set connection: faster topology update if the primary is down (thanks to p-mongo)
4+
* Added custom mongo.encoder protocol (thanks to esse)
5+
* added collection from yildun project
46

57
## 0.7.4
68
* Enhancements

README.md

Lines changed: 126 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,23 @@
88

99
## Features
1010

11-
- Supports MongoDB versions 3.2, 3.4, 3.6, 4.x, 5.x
12-
- Connection pooling ([through DBConnection 2.x](https://github.com/elixir-ecto/db_connection))
13-
- Streaming cursors
14-
- Performant ObjectID generation
15-
- Aggregation pipeline
16-
- Replica sets
17-
- Support for SCRAM-SHA-256 (MongoDB 4.x)
18-
- Support for GridFS ([See](https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst))
19-
- Support for change streams api ([See](https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst))
20-
- Support for bulk writes ([See](https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#write))
11+
- supports MongoDB versions 3.2, 3.4, 3.6, 4.x, 5.x
12+
- connection pooling ([through DBConnection 2.x](https://github.com/elixir-ecto/db_connection))
13+
- streaming cursors
14+
- performant ObjectID generation
15+
- aggregation pipeline
16+
- replica sets
17+
- support for SCRAM-SHA-256 (MongoDB 4.x)
18+
- support for GridFS ([See](https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst))
19+
- support for change streams api ([See](https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst))
20+
- support for bulk writes ([See](https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#write))
2121
- support for driver sessions ([See](https://github.com/mongodb/specifications/blob/master/source/sessions/driver-sessions.rst))
2222
- support for driver transactions ([See](https://github.com/mongodb/specifications/blob/master/source/transactions/transactions.rst))
2323
- support for command monitoring ([See](https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst))
2424
- support for retryable reads ([See](https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst))
2525
- support for retryable writes ([See](https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst))
26+
- support for simple structs using the Mongo.Encoder protocol
27+
- support for complex and nested documents using the `Mongo.Collection` macros
2628

2729
## Data Representation
2830

@@ -44,7 +46,7 @@
4446
max key :BSON_max
4547
decimal128 Decimal{}
4648

47-
Since BSON documents are ordered Elixir maps cannot be used to fully represent them. This driver chose to accept both maps and lists of key-value pairs when encoding but will only decode documents to lists. This has the side-effect that it's impossible to discern empty arrays from empty documents. Additionally the driver will accept both atoms and strings for document keys but will only decode to strings.
49+
Since BSON documents are ordered Elixir maps cannot be used to fully represent them. This driver chose to accept both maps and lists of key-value pairs when encoding but will only decode documents to lists. This has the side-effect that it's impossible to discern empty arrays from empty documents. Additionally, the driver will accept both atoms and strings for document keys but will only decode to strings.
4850

4951
BSON symbols can only be decoded.
5052

@@ -91,6 +93,118 @@ it will be written to database, as:
9193
}
9294
```
9395

96+
## Collections
97+
98+
While using the `Mongo.Encoder` protocol give you the possibility to encode your structs into maps the opposite way to decode those maps into structs is missing. To handle it you can use the `Mongo.Collection` which provides some boilerplate code for a better support of structs while using the MongoDB driver
99+
100+
* automatic load and dump function
101+
* reflection functions
102+
* type specification
103+
* support for embedding one and many structs
104+
* support for `after load` function
105+
* support for `before dump` function
106+
* support for id generation
107+
* support for default values
108+
* support for derived values
109+
110+
When using the MongoDB driver only maps and keyword lists are used to represent documents.
111+
If you would prefere to use structs instead of the maps to give the document a stronger meaning or to emphasize
112+
its importance, you have to create a `defstruct` and fill it from the map manually:
113+
114+
```elixir
115+
defmodule Label do
116+
defstruct name: "warning", color: "red"
117+
end
118+
119+
iex> label_map = Mongo.find_one(:mongo, "labels", %{})
120+
%{"name" => "warning", "color" => "red"}
121+
iex> label = %Label{name: label_map["name"], color: label_map["color"]}
122+
```
123+
124+
We have defined a module `Label` as `defstruct`, then we get the first label document
125+
the collection `labels`. The function `find_one` returns a map. We convert the map manually and
126+
get the desired struct.
127+
128+
If we want to save a new structure, we have to do the reverse. We convert the struct into a map:
129+
130+
```elixir
131+
iex> label = %Label{}
132+
iex> label_map = %{"name" => label.name, "color" => label.color}
133+
iex> {:ok, _} = Mongo.insert_one(:mongo, "labels", label_map)
134+
```
135+
136+
Alternatively, you can also remove the `__struct__` key from `label`. The MongoDB driver automatically
137+
converts the atom keys into strings.
138+
139+
```elixir
140+
iex> Map.drop(label, [:__struct__])
141+
%{color: :red, name: "warning"}
142+
```
143+
If you use nested structures, the work becomes a bit more complex. In this case, you have to use the inner structures
144+
convert manually, too. If you take a closer look at the necessary work, two basic functions can be derived:
145+
146+
* `load` Conversion of the map into a struct.
147+
* `dump` Conversion of the struct into a map.
148+
149+
`Mongo.Collection` provides the necessary macros to automate this boilerplate code. The above example can be rewritten as follows:
150+
151+
```elixir
152+
defmodule Label do
153+
use Collection
154+
155+
document do
156+
attribute :name, String.t(), default: "warning"
157+
attribute :color, String.t(), default: :red
158+
end
159+
end
160+
```
161+
This results in the following module:
162+
163+
```elixir
164+
defmodule Label do
165+
166+
defstruct [name: "warning", color: "red"]
167+
168+
@type t() :: %Label{String.t(), String.t()}
169+
170+
def new()...
171+
def load(map)...
172+
def dump(%Label{})...
173+
def __collection__(:attributes)...
174+
def __collection__(:types)...
175+
def __collection__(:collection)...
176+
def __collection__(:id)...
177+
178+
end
179+
```
180+
181+
You can now create new structs with the default values and use the conversion functions between map and structs:
182+
183+
```elixir
184+
iex(1)> x = Label.new()
185+
%Label{color: :red, name: "warning"}
186+
iex(2)> m = Label.dump(x)
187+
%{color: :red, name: "warning"}
188+
iex(3)> Label.load(m, true)
189+
%Label{color: :red, name: "warning"}
190+
```
191+
192+
The `load/2` function distinguishes between keys of type binarys `load(map, false)` and keys of type atoms `load(map, true)`. The default is `load(map, false)`:
193+
194+
```elixir
195+
iex(1)> m = %{"color" => :red, "name" => "warning"}
196+
iex(2)> Label.load(m)
197+
%Label{color: :red, name: "warning"}
198+
```
199+
If you would now expect atoms as keys, the result of the conversion is not correct in this case:
200+
201+
```elixir
202+
iex(3)> Label.load(m, true)
203+
%Label{color: nil, name: nil}
204+
```
205+
206+
The background is that MongoDB always returns binarys as keys and structs use atoms as keys. For more information look at the module documentation [Mongo.Collection](https://hexdocs.pm/mongodb_driver/Mongo.Collection.html#content)
207+
94208
## Usage
95209

96210
### Installation
@@ -173,7 +287,7 @@ cursor
173287
|> IO.inspect
174288
```
175289

176-
If you're using pooling it is recommend to add it to your application supervisor:
290+
If you're using pooling it is recommended to add it to your application supervisor:
177291
178292
```elixir
179293
def start(_type, _args) do

0 commit comments

Comments
 (0)