Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 285 lines (226 sloc) 12.512 kb
9a01905 mongo.erl is top-level interface
Tony Hannan authored
1 % Top-level client interface to MongoDB
2 -module (mongo).
3
4 -export_type ([maybe/1]).
5
f59460d mongo:create_index
Tony Hannan authored
6 -export_type ([host/0, connection/0]).
9a01905 mongo.erl is top-level interface
Tony Hannan authored
7 -export ([connect/1, disconnect/1]).
f59460d mongo:create_index
Tony Hannan authored
8
9 -export_type ([action/1, db/0, write_mode/0, read_mode/0, failure/0]).
9a01905 mongo.erl is top-level interface
Tony Hannan authored
10 -export ([do/5]).
f59460d mongo:create_index
Tony Hannan authored
11
12 -export_type ([collection/0, selector/0, projector/0, skip/0, batchsize/0, modifier/0]).
9a01905 mongo.erl is top-level interface
Tony Hannan authored
13 -export ([insert/2, insert_all/2]).
14 -export ([save/2, replace/3, repsert/3, modify/3]).
15 -export ([delete/2, delete_one/2]).
16 -export ([find_one/2, find_one/3, find_one/4]).
17 -export ([find/2, find/3, find/4, find/5]).
18 -export ([count/2, count/3]).
f59460d mongo:create_index
Tony Hannan authored
19
20 -export_type ([cursor/0]).
21 -export ([next/1, rest/1, close_cursor/1]).
22
23 -export_type ([command/0]).
9a01905 mongo.erl is top-level interface
Tony Hannan authored
24 -export ([command/1]).
25
f59460d mongo:create_index
Tony Hannan authored
26 -export_type ([key_order/0, index_uniqueness/0]).
27 -export ([create_index/2, create_index/3, create_index/4]).
28
9a01905 mongo.erl is top-level interface
Tony Hannan authored
29 -include ("mongo_protocol.hrl").
30
31 -type reason() :: any().
32
33 -type host() :: inet:hostname() | inet:ip_address() | mongo_connect:host().
34 % Hostname or ip address with or without port. Port defaults to 27017 if port missing.
35 % Eg. "localhost" or {"localhost", 27017}
36
37 -type connection() :: mongo_connect:connection().
38
39 -spec connect (host()) -> {ok, connection()} | {error, reason()}. % IO
40 % Connect to given MongoDB server
41 connect ({Address, Port}) -> mongo_connect:connect ({Address, Port});
42 connect (Address) -> mongo_connect:connect ({Address, 27017}).
43
44 -spec disconnect (connection()) -> ok. % IO
45 % Close connection to server
46 disconnect (Conn) -> mongo_connect:close (Conn).
47
48 -type action(A) :: fun (() -> A).
d6fd289 Fix wire protocol bit flags order. Catch not_master read and write error...
Tony Hannan authored
49 % An Action does IO, reads process dict {mongo_action_context, #context{}}, and throws failure()
9a01905 mongo.erl is top-level interface
Tony Hannan authored
50
51 -type failure() ::
d6fd289 Fix wire protocol bit flags order. Catch not_master read and write error...
Tony Hannan authored
52 mongo_connect:failure() | % thrown by read and safe write
53 mongo_query:not_master() | % throws by read and safe write
54 write_failure() | % throws by safe write
55 mongo_cursor:expired(). % thrown by cursor next/rest
9a01905 mongo.erl is top-level interface
Tony Hannan authored
56
57 -record (context, {
58 write_mode :: write_mode(),
59 read_mode :: read_mode(),
60 dbconn :: mongo_connect:dbconnection() }).
61
d6fd289 Fix wire protocol bit flags order. Catch not_master read and write error...
Tony Hannan authored
62 -spec do (write_mode(), read_mode(), connection(), db(), action(A)) -> {ok, A} | {failure, failure()}. % IO
9a01905 mongo.erl is top-level interface
Tony Hannan authored
63 % Execute mongo action under given write_mode, read_mode, connection, and db. Return action result or failure.
64 do (WriteMode, ReadMode, Connection, Database, Action) ->
65 PrevContext = get (mongo_action_context),
66 put (mongo_action_context, #context {write_mode = WriteMode, read_mode = ReadMode, dbconn = {Database, Connection}}),
67 try Action() of
68 Result -> {ok, Result}
69 catch
d6fd289 Fix wire protocol bit flags order. Catch not_master read and write error...
Tony Hannan authored
70 throw: E = {connection_failure, _, _} -> {failure, E};
71 throw: E = not_master -> {failure, E};
72 throw: E = {write_failure, _, _} -> {failure, E};
73 throw: E = {cursor_expired, _} -> {failure, E}
9a01905 mongo.erl is top-level interface
Tony Hannan authored
74 after
75 case PrevContext of undefined -> erase (mongo_action_context); _ -> put (mongo_action_context, PrevContext) end
76 end.
77
78 % Write %
79
80 -type write_mode() :: unsafe | safe | {safe, mongo_query:getlasterror_request()}.
81 % Every write inside an action() will use this write mode.
82 % unsafe = asynchronous write (no reply) and hence may silently fail;
83 % safe = synchronous write, wait for reply and fail if connection or write failure;
84 % {safe, Params} = same as safe but with extra params for getlasterror, see its documentation.
85
86 -type write_failure() :: {write_failure, error_code(), bson:utf8()}.
87 -type error_code() :: integer().
88
89 -spec write (mongo_query:write()) -> ok. % Action
90 % Do unsafe unacknowledged fast write or safe acknowledged slower write depending on our context. When safe, throw write_failure if acknowledgment (getlasterror) reports error.
91 write (Write) ->
92 Context = get (mongo_action_context),
93 case Context #context.write_mode of
94 unsafe -> mongo_query:write (Context #context.dbconn, Write);
95 SafeMode ->
0a8cc8c Documents are now tuples instead of lists
Tony Hannan authored
96 Params = case SafeMode of safe -> {}; {safe, Param} -> Param end,
9a01905 mongo.erl is top-level interface
Tony Hannan authored
97 Ack = mongo_query:write (Context #context.dbconn, Write, Params),
98 case bson:lookup (err, Ack) of
99 {} -> ok; {null} -> ok;
d6fd289 Fix wire protocol bit flags order. Catch not_master read and write error...
Tony Hannan authored
100 {String} -> case bson:at (code, Ack) of
101 10058 -> throw (not_master);
102 Code -> throw ({write_failure, Code, String}) end end end.
9a01905 mongo.erl is top-level interface
Tony Hannan authored
103
104 -spec insert (collection(), bson:document()) -> bson:value(). % Action
105 % Insert document into collection. Return its '_id' value, which is auto-generated if missing.
106 insert (Coll, Doc) -> [Value] = insert_all (Coll, [Doc]), Value.
107
108 -spec insert_all (collection(), [bson:document()]) -> [bson:value()]. % Action
109 % Insert documents into collection. Return their '_id' values, which are auto-generated if missing.
110 insert_all (Coll, Docs) ->
111 Docs1 = lists:map (fun assign_id/1, Docs),
112 write (#insert {collection = Coll, documents = Docs1}),
113 lists:map (fun (Doc) -> bson:at ('_id', Doc) end, Docs1).
114
115 -spec assign_id (bson:document()) -> bson:document(). % IO
116 % If doc has no '_id' field then generate a fresh object id for it
117 assign_id (Doc) -> case bson:lookup ('_id', Doc) of
118 {_Value} -> Doc;
0a8cc8c Documents are now tuples instead of lists
Tony Hannan authored
119 {} -> bson:append ({'_id', mongodb_app:gen_objectid()}, Doc) end.
9a01905 mongo.erl is top-level interface
Tony Hannan authored
120
121 -spec save (collection(), bson:document()) -> ok. % Action
122 % If document has no '_id' field then insert it, otherwise update it and insert only if missing.
123 save (Coll, Doc) -> case bson:lookup ('_id', Doc) of
124 {} -> insert (Coll, Doc);
0a8cc8c Documents are now tuples instead of lists
Tony Hannan authored
125 {Id} -> repsert (Coll, {'_id', Id}, Doc) end.
9a01905 mongo.erl is top-level interface
Tony Hannan authored
126
127 -spec replace (collection(), selector(), bson:document()) -> ok. % Action
128 % Replace first document selected with given document.
129 replace (Coll, Selector, Doc) -> update (false, false, Coll, Selector, Doc).
130
131 -spec repsert (collection(), selector(), bson:document()) -> ok. % Action
132 % Replace first document selected with given document, or insert it if selection is empty.
133 repsert (Coll, Selector, Doc) -> update (true, false, Coll, Selector, Doc).
134
135 -spec modify (collection(), selector(), modifier()) -> ok. % Action
136 % Update all documents selected using modifier
137 modify (Coll, Selector, Mod) -> update (false, true, Coll, Selector, Mod).
138
139 -spec update (boolean(), boolean(), collection(), selector(), bson:document()) -> ok. % Action
140 update (Upsert, MultiUpdate, Coll, Sel, Doc) ->
141 write (#update {collection = Coll, upsert = Upsert, multiupdate = MultiUpdate, selector = Sel, updater = Doc}).
142
143 -spec delete (collection(), selector()) -> ok. % Action
144 % Delete selected documents
145 delete (Coll, Selector) ->
146 write (#delete {collection = Coll, singleremove = false, selector = Selector}).
147
148 -spec delete_one (collection(), selector()) -> ok. % Action
149 % Delete first selected document.
150 delete_one (Coll, Selector) ->
151 write (#delete {collection = Coll, singleremove = true, selector = Selector}).
152
153 % Read %
154
155 -type read_mode() :: master | slave_ok.
156 % Every query inside an action() will use this mode.
157 % master = Server must be master/primary so reads are consistent (read latest writes).
158 % slave_ok = Server may be slave/secondary so reads are may not be consistent (may read stale data). But the slaves will eventually get the latest writes, so technically this is called eventually-consistent.
159
160 slave_ok (#context {read_mode = slave_ok}) -> true;
161 slave_ok (#context {read_mode = master}) -> false.
162
163 -type maybe(A) :: {A} | {}.
164
165 -spec find_one (collection(), selector()) -> maybe (bson:document()). % Action
166 % Return first selected document, if any
167 find_one (Coll, Selector) -> find_one (Coll, Selector, []).
168
169 -spec find_one (collection(), selector(), projector()) -> maybe (bson:document()). % Action
170 % Return projection of first selected document, if any. Empty projection [] means full projection.
171 find_one (Coll, Selector, Projector) -> find_one (Coll, Selector, Projector, 0).
172
173 -spec find_one (collection(), selector(), projector(), skip()) -> maybe (bson:document()). % Action
174 % Return projection of Nth selected document, if any. Empty projection [] means full projection.
175 find_one (Coll, Selector, Projector, Skip) ->
176 Context = get (mongo_action_context),
177 Query = #'query' {
178 collection = Coll, selector = Selector, projector = Projector,
179 skip = Skip, slaveok = slave_ok (Context) },
180 mongo_query:find_one (Context #context.dbconn, Query).
181
182 -spec find (collection(), selector()) -> cursor(). % Action
183 % Return selected documents.
184 find (Coll, Selector) -> find (Coll, Selector, []).
185
186 -spec find (collection(), selector(), projector()) -> cursor(). % Action
187 % Return projection of selected documents. Empty projection [] means full projection.
188 find (Coll, Selector, Projector) -> find (Coll, Selector, Projector, 0).
189
190 -spec find (collection(), selector(), projector(), skip()) -> cursor(). % Action
191 % Return projection of selected documents starting from Nth document. Empty projection means full projection.
192 find (Coll, Selector, Projector, Skip) -> find (Coll, Selector, Projector, Skip, 0).
193
194 -spec find (collection(), selector(), projector(), skip(), batchsize()) -> cursor(). % Action
195 % Return projection of selected documents starting from Nth document in batches of batchsize. 0 batchsize means default batch size. Negative batch size means one batch only. Empty projection means full projection.
196 find (Coll, Selector, Projector, Skip, BatchSize) ->
197 Context = get (mongo_action_context),
198 Query = #'query' {
199 collection = Coll, selector = Selector, projector = Projector,
200 skip = Skip, batchsize = BatchSize, slaveok = slave_ok (Context) },
201 mongo_query:find (Context #context.dbconn, Query).
202
203 -type cursor() :: mongo_cursor:cursor().
204
205 -spec next (cursor()) -> maybe (bson:document()). % IO throws mongo_connect:failure() & mongo_cursor:expired() (this is a subtype of Action)
206 % Return next document in query result cursor, if any.
207 next (Cursor) -> mongo_cursor:next (Cursor).
208
209 -spec rest (cursor()) -> [bson:document()]. % IO throws mongo_connect:failure() & mongo_cursor:expired() (this is a subtype of Action)
210 % Return remaining documents in query result cursor.
211 rest (Cursor) -> mongo_cursor:rest (Cursor).
212
213 -spec close_cursor (cursor()) -> ok. % IO (IO is a subtype of Action)
214 % Close cursor
215 close_cursor (Cursor) -> mongo_cursor:close (Cursor).
216
217 -spec count (collection(), selector()) -> integer(). % Action
218 % Count selected documents
219 count (Coll, Selector) -> count (Coll, Selector, 0).
220
221 -spec count (collection(), selector(), integer()) -> integer(). % Action
222 % Count selected documents up to given max number; 0 means no max. Ie. stops counting when max is reached to save processing time.
223 count (Coll, Selector, Limit) ->
0a8cc8c Documents are now tuples instead of lists
Tony Hannan authored
224 CollStr = atom_to_binary (Coll, utf8),
225 Command = if
226 Limit =< 0 -> {count, CollStr, 'query', Selector};
227 true -> {count, CollStr, 'query', Selector, limit, Limit} end,
228 Doc = command (Command),
9a01905 mongo.erl is top-level interface
Tony Hannan authored
229 trunc (bson:at (n, Doc)). % Server returns count as float
230
231 % Command %
232
233 -type command() :: mongo_query:command().
234
235 -spec command (command()) -> bson:document(). % Action
236 % Execute given MongoDB command and return its result.
237 command (Command) ->
238 Context = get (mongo_action_context),
239 mongo_query:command (Context #context.dbconn, Command, slave_ok (Context)).
f59460d mongo:create_index
Tony Hannan authored
240
241 % Administration %
242
243 -type key_order() :: bson:document().
244 % List keys and whether ascending (1) or descending (-1). Eg. {x,1, y,-1}
245
246 -spec create_index (collection(), key_order()) -> ok. % Action
247 % Create non-unique index on given keys in collection
248 create_index (Coll, KeyOrder) ->
249 create_index (Coll, KeyOrder, non_unique).
250
251 -type index_uniqueness() ::
252 non_unique | % Multiple docs with same index value allowed
253 unique | % At most one doc with same index value, index creation fails otherwise
254 unique_dropdups. % Same as unique, but deletes docs with duplicate index value on index creation
255
256 -spec create_index (collection(), key_order(), index_uniqueness()) -> ok. % Action
257 % Create index on given keys with given uniqueness
258 create_index (Coll, KeyOrder, Uniqueness) ->
259 create_index (Coll, KeyOrder, Uniqueness, gen_index_name (KeyOrder)).
260
261 -spec gen_index_name (key_order()) -> bson:utf8().
262 gen_index_name (KeyOrder) ->
263 AsName = fun (Label, Order, Name) -> <<
264 $_,
265 Name /binary,
266 (atom_to_binary (Label, utf8)) /binary,
267 $_,
268 (bson:utf8 (integer_to_list (Order))) /binary >> end,
269 bson:doc_foldl (AsName, <<>>, KeyOrder).
270
271 -spec create_index (collection(), key_order(), index_uniqueness(), bson:utf8()) -> ok. % Action
272 % Create index on given keys with given uniqueness and name
273 create_index (Coll, KeyOrder, Uniqueness, IndexName) ->
274 {Db, _} = (get (mongo_action_context)) #context.dbconn,
275 {Unique, DropDups} = case Uniqueness of
276 non_unique -> {false, false};
277 unique -> {true, false};
278 unique_dropdups -> {true, true} end,
279 insert ('system.indexes', {
280 ns, mongo_protocol:dbcoll (Db, Coll),
281 key, KeyOrder,
282 name, IndexName,
283 unique, Unique,
284 dropDups, DropDups}).
Something went wrong with that request. Please try again.