Skip to content
This repository
Newer
Older
100644 225 lines (178 sloc) 10.342 kb
9a019050 »
2010-11-23 mongo.erl is top-level interface
1 % Top-level client interface to MongoDB
2 -module (mongo).
3
4 -export_type ([maybe/1]).
5 -export_type ([host/0, connection/0, cursor/0]).
6 -export_type ([action/1, write_mode/0, read_mode/0, failure/0]).
7 -export_type ([db/0, collection/0, selector/0, projector/0, skip/0, batchsize/0, modifier/0]).
8 -export_type ([command/0]).
9
10 -export ([connect/1, disconnect/1]).
11 -export ([do/5]).
12 -export ([insert/2, insert_all/2]).
13 -export ([save/2, replace/3, repsert/3, modify/3]).
14 -export ([delete/2, delete_one/2]).
15 -export ([find_one/2, find_one/3, find_one/4]).
16 -export ([find/2, find/3, find/4, find/5]).
17 -export ([next/1, rest/1, close_cursor/1]).
18 -export ([count/2, count/3]).
19 -export ([command/1]).
20
21 -include ("mongo_protocol.hrl").
22
23 -type reason() :: any().
24
25 -type host() :: inet:hostname() | inet:ip_address() | mongo_connect:host().
26 % Hostname or ip address with or without port. Port defaults to 27017 if port missing.
27 % Eg. "localhost" or {"localhost", 27017}
28
29 -type connection() :: mongo_connect:connection().
30
31 -spec connect (host()) -> {ok, connection()} | {error, reason()}. % IO
32 % Connect to given MongoDB server
33 connect ({Address, Port}) -> mongo_connect:connect ({Address, Port});
34 connect (Address) -> mongo_connect:connect ({Address, 27017}).
35
36 -spec disconnect (connection()) -> ok. % IO
37 % Close connection to server
38 disconnect (Conn) -> mongo_connect:close (Conn).
39
40 -type action(A) :: fun (() -> A).
41 % IO, reads process dict {mongo_action_context, #context{}}, and throws failure()
42
43 -type failure() ::
44 mongo_connect:failure() |
45 write_failure() |
46 mongo_cursor:expired().
47
48 -record (context, {
49 write_mode :: write_mode(),
50 read_mode :: read_mode(),
51 dbconn :: mongo_connect:dbconnection() }).
52
53 -spec do (write_mode(), read_mode(), connection(), db(), action(A)) -> {ok, A} | {error, failure()}. % IO
54 % Execute mongo action under given write_mode, read_mode, connection, and db. Return action result or failure.
55 do (WriteMode, ReadMode, Connection, Database, Action) ->
56 PrevContext = get (mongo_action_context),
57 put (mongo_action_context, #context {write_mode = WriteMode, read_mode = ReadMode, dbconn = {Database, Connection}}),
58 try Action() of
59 Result -> {ok, Result}
60 catch
61 throw: E = {connection_failure, _, _} -> {error, E};
62 throw: E = {write_failure, _, _} -> {error, E};
63 throw: E = {cursor_expired, _} -> {error, E}
64 after
65 case PrevContext of undefined -> erase (mongo_action_context); _ -> put (mongo_action_context, PrevContext) end
66 end.
67
68 % Write %
69
70 -type write_mode() :: unsafe | safe | {safe, mongo_query:getlasterror_request()}.
71 % Every write inside an action() will use this write mode.
72 % unsafe = asynchronous write (no reply) and hence may silently fail;
73 % safe = synchronous write, wait for reply and fail if connection or write failure;
74 % {safe, Params} = same as safe but with extra params for getlasterror, see its documentation.
75
76 -type write_failure() :: {write_failure, error_code(), bson:utf8()}.
77 -type error_code() :: integer().
78
79 -spec write (mongo_query:write()) -> ok. % Action
80 % Do unsafe unacknowledged fast write or safe acknowledged slower write depending on our context. When safe, throw write_failure if acknowledgment (getlasterror) reports error.
81 write (Write) ->
82 Context = get (mongo_action_context),
83 case Context #context.write_mode of
84 unsafe -> mongo_query:write (Context #context.dbconn, Write);
85 SafeMode ->
86 Params = case SafeMode of safe -> []; {safe, Param} -> Param end,
87 Ack = mongo_query:write (Context #context.dbconn, Write, Params),
88 case bson:lookup (err, Ack) of
89 {} -> ok; {null} -> ok;
90 {String} -> throw ({write_failure, bson:at (code, Ack), String}) end end.
91
92 -spec insert (collection(), bson:document()) -> bson:value(). % Action
93 % Insert document into collection. Return its '_id' value, which is auto-generated if missing.
94 insert (Coll, Doc) -> [Value] = insert_all (Coll, [Doc]), Value.
95
96 -spec insert_all (collection(), [bson:document()]) -> [bson:value()]. % Action
97 % Insert documents into collection. Return their '_id' values, which are auto-generated if missing.
98 insert_all (Coll, Docs) ->
99 Docs1 = lists:map (fun assign_id/1, Docs),
100 write (#insert {collection = Coll, documents = Docs1}),
101 lists:map (fun (Doc) -> bson:at ('_id', Doc) end, Docs1).
102
103 -spec assign_id (bson:document()) -> bson:document(). % IO
104 % If doc has no '_id' field then generate a fresh object id for it
105 assign_id (Doc) -> case bson:lookup ('_id', Doc) of
106 {_Value} -> Doc;
107 {} -> ['_id', mongodb_app:gen_objectid() | Doc] end.
108
109 -spec save (collection(), bson:document()) -> ok. % Action
110 % If document has no '_id' field then insert it, otherwise update it and insert only if missing.
111 save (Coll, Doc) -> case bson:lookup ('_id', Doc) of
112 {} -> insert (Coll, Doc);
113 {Id} -> repsert (Coll, ['_id', Id], Doc) end.
114
115 -spec replace (collection(), selector(), bson:document()) -> ok. % Action
116 % Replace first document selected with given document.
117 replace (Coll, Selector, Doc) -> update (false, false, Coll, Selector, Doc).
118
119 -spec repsert (collection(), selector(), bson:document()) -> ok. % Action
120 % Replace first document selected with given document, or insert it if selection is empty.
121 repsert (Coll, Selector, Doc) -> update (true, false, Coll, Selector, Doc).
122
123 -spec modify (collection(), selector(), modifier()) -> ok. % Action
124 % Update all documents selected using modifier
125 modify (Coll, Selector, Mod) -> update (false, true, Coll, Selector, Mod).
126
127 -spec update (boolean(), boolean(), collection(), selector(), bson:document()) -> ok. % Action
128 update (Upsert, MultiUpdate, Coll, Sel, Doc) ->
129 write (#update {collection = Coll, upsert = Upsert, multiupdate = MultiUpdate, selector = Sel, updater = Doc}).
130
131 -spec delete (collection(), selector()) -> ok. % Action
132 % Delete selected documents
133 delete (Coll, Selector) ->
134 write (#delete {collection = Coll, singleremove = false, selector = Selector}).
135
136 -spec delete_one (collection(), selector()) -> ok. % Action
137 % Delete first selected document.
138 delete_one (Coll, Selector) ->
139 write (#delete {collection = Coll, singleremove = true, selector = Selector}).
140
141 % Read %
142
143 -type read_mode() :: master | slave_ok.
144 % Every query inside an action() will use this mode.
145 % master = Server must be master/primary so reads are consistent (read latest writes).
146 % 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.
147
148 slave_ok (#context {read_mode = slave_ok}) -> true;
149 slave_ok (#context {read_mode = master}) -> false.
150
151 -type maybe(A) :: {A} | {}.
152
153 -spec find_one (collection(), selector()) -> maybe (bson:document()). % Action
154 % Return first selected document, if any
155 find_one (Coll, Selector) -> find_one (Coll, Selector, []).
156
157 -spec find_one (collection(), selector(), projector()) -> maybe (bson:document()). % Action
158 % Return projection of first selected document, if any. Empty projection [] means full projection.
159 find_one (Coll, Selector, Projector) -> find_one (Coll, Selector, Projector, 0).
160
161 -spec find_one (collection(), selector(), projector(), skip()) -> maybe (bson:document()). % Action
162 % Return projection of Nth selected document, if any. Empty projection [] means full projection.
163 find_one (Coll, Selector, Projector, Skip) ->
164 Context = get (mongo_action_context),
165 Query = #'query' {
166 collection = Coll, selector = Selector, projector = Projector,
167 skip = Skip, slaveok = slave_ok (Context) },
168 mongo_query:find_one (Context #context.dbconn, Query).
169
170 -spec find (collection(), selector()) -> cursor(). % Action
171 % Return selected documents.
172 find (Coll, Selector) -> find (Coll, Selector, []).
173
174 -spec find (collection(), selector(), projector()) -> cursor(). % Action
175 % Return projection of selected documents. Empty projection [] means full projection.
176 find (Coll, Selector, Projector) -> find (Coll, Selector, Projector, 0).
177
178 -spec find (collection(), selector(), projector(), skip()) -> cursor(). % Action
179 % Return projection of selected documents starting from Nth document. Empty projection means full projection.
180 find (Coll, Selector, Projector, Skip) -> find (Coll, Selector, Projector, Skip, 0).
181
182 -spec find (collection(), selector(), projector(), skip(), batchsize()) -> cursor(). % Action
183 % 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.
184 find (Coll, Selector, Projector, Skip, BatchSize) ->
185 Context = get (mongo_action_context),
186 Query = #'query' {
187 collection = Coll, selector = Selector, projector = Projector,
188 skip = Skip, batchsize = BatchSize, slaveok = slave_ok (Context) },
189 mongo_query:find (Context #context.dbconn, Query).
190
191 -type cursor() :: mongo_cursor:cursor().
192
193 -spec next (cursor()) -> maybe (bson:document()). % IO throws mongo_connect:failure() & mongo_cursor:expired() (this is a subtype of Action)
194 % Return next document in query result cursor, if any.
195 next (Cursor) -> mongo_cursor:next (Cursor).
196
197 -spec rest (cursor()) -> [bson:document()]. % IO throws mongo_connect:failure() & mongo_cursor:expired() (this is a subtype of Action)
198 % Return remaining documents in query result cursor.
199 rest (Cursor) -> mongo_cursor:rest (Cursor).
200
201 -spec close_cursor (cursor()) -> ok. % IO (IO is a subtype of Action)
202 % Close cursor
203 close_cursor (Cursor) -> mongo_cursor:close (Cursor).
204
205 -spec count (collection(), selector()) -> integer(). % Action
206 % Count selected documents
207 count (Coll, Selector) -> count (Coll, Selector, 0).
208
209 -spec count (collection(), selector(), integer()) -> integer(). % Action
210 % Count selected documents up to given max number; 0 means no max. Ie. stops counting when max is reached to save processing time.
211 count (Coll, Selector, Limit) ->
212 LimitDoc = if Limit =< 0 -> []; true -> [limit, Limit] end,
213 Doc = command ([count, atom_to_binary (Coll, utf8), 'query', Selector | LimitDoc]),
214 trunc (bson:at (n, Doc)). % Server returns count as float
215
216 % Command %
217
218 -type command() :: mongo_query:command().
219
220 -spec command (command()) -> bson:document(). % Action
221 % Execute given MongoDB command and return its result.
222 command (Command) ->
223 Context = get (mongo_action_context),
224 mongo_query:command (Context #context.dbconn, Command, slave_ok (Context)).
Something went wrong with that request. Please try again.