lib/mongodb: generate native object ids for document without the field "_id"
[nit.git] / lib / mongodb / native_mongodb.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2015 Alexandre Terrasa <alexandre@moz-code.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Native wrapper for the MongoDB C Driver
18 #
19 # See [mongoc](http://api.mongodb.org/c/1.1.4/index.html).
20 module native_mongodb is pkgconfig "libmongoc-1.0"
21
22 import c
23
24 in "C header" `{
25 #include <mongoc.h>
26 `}
27
28 # Wrapper for `bson_t`.
29 #
30 # All data manipulated by `mongoc` are BSON formated.
31 #
32 # The `bson_t` structure represents a BSON document.
33 # This structure manages the underlying BSON encoded buffer.
34 # For mutable documents, it can append new data to the document.
35 #
36 # See [`bson_t`](http://api.mongodb.org/libbson/current/bson_t.html).
37 extern class NativeBSON `{ bson_t * `}
38
39 # Wrapper for `bson_new()`.
40 #
41 # The `bson_new()` function shall create a new `bson_t` structure on the heap.
42 # It should be freed with `bson_destroy()` when it is no longer in use.
43 new `{ return bson_new(); `}
44
45 # Wrapper for `bson_new_from_json()`.
46 #
47 # The `bson_new_from_json()` function allocates and initialize a new `bson_t`
48 # by parsing the JSON found in `data`.
49 # Only a single JSON object may exist in data or an error will be set and
50 # `NULL` returned.
51 new from_json_string(data: NativeString) import set_mongoc_error `{
52 bson_error_t error;
53 bson_t *bson;
54 bson = bson_new_from_json(data, -1, &error);
55 if(!bson) {
56 NativeBSON_set_mongoc_error(bson, &error);
57 return NULL;
58 }
59 return bson;
60 `}
61
62 # Wrapper for `bson_as_json()`.
63 #
64 # The `bson_as_json()` function shall encode bson as a JSON encoded UTF-8 string.
65 # The caller is responsible for freeing the resulting UTF-8 encoded string
66 # by calling `bson_free()` with the result.
67 fun to_native_string: NativeString `{ return bson_as_json(self, NULL); `}
68
69 # Wrapper for `bson_destroy()`.
70 #
71 # The `bson_destroy()` function shall free an allocated `bson_t` structure.
72 # This function should always be called when you are done with a `bson_t`
73 # unless otherwise specified.
74 #
75 # This instance should not be used beyond this point!
76 fun destroy `{ bson_destroy(self); `}
77
78 # Utility method to set `Sys.last_mongoc_error`.
79 fun set_mongoc_error(err: BSONError) do sys.last_mongoc_error = err
80 end
81
82 # Wrapper for `bson_error_t`.
83 #
84 # The `bson_error_t` structure is used to encapsulate information about an error.
85 #
86 # See [`bson_error_t`](http://api.mongodb.org/libbson/current/bson_error_t.html).
87 extern class BSONError `{ bson_error_t * `}
88
89 # Wrapper for `error.domain`.
90 #
91 # The `error.domain` field contains the logical domain within a library that
92 # created the error.
93 fun domain: Int `{ return self->domain; `}
94
95 # Wrapper for `error.code`.
96 #
97 # The `error.code` field contains the domain specific error code.
98 fun code: Int `{ return self->code; `}
99
100 # Wrapper for `error.message`.
101 #
102 # The `error.message` field contains a human printable error message.
103 fun message: NativeString `{ return self->message; `}
104 end
105
106 # Wrapper for `bson_oid_t`.
107 #
108 # The `bson_oid_t` structure contains the 12-byte ObjectId notation defined by the
109 # [BSON ObjectID specificiation](http://docs.mongodb.org/manual/reference/object-id/).
110 #
111 # ObjectId is a 12-byte BSON type, constructed using:
112 # * a 4-byte value representing the seconds since the Unix epoch (in Big Endian)
113 # * a 3-byte machine identifier
114 # * a 2-byte process id (Big Endian), and
115 # * a 3-byte counter (Big Endian), starting with a random value.
116 extern class BSONObjectId `{ bson_oid_t * `}
117 # Object id.
118 fun id: String import NativeString.to_s_with_copy `{
119 char str[25];
120 bson_oid_to_string(self, str);
121 return NativeString_to_s_with_copy(str);
122 `}
123 end
124
125 redef class Sys
126 # Last error raised by `monogdb::MongoClient`.
127 #
128 # See `MongoClient::last_error`.
129 var last_mongoc_error: nullable BSONError = null
130
131 # Last auto generated id if any.
132 #
133 # See `MongoCollection::insert`.
134 var last_mongoc_id: nullable BSONObjectId = null is writable
135 end
136
137 # Wrapper for `char**`.
138 #
139 # Used to handle array of NativeString returned by MongoDB.
140 redef class NativeCStringArray
141 # Frees `self`.
142 #
143 # This instance should not be used beyond this point!
144 fun destroy `{ free(self); `}
145 end
146
147 # Wrapper for `mongoc_client_t`.
148 #
149 # `mongoc_client_t` is an opaque type that provides access to a MongoDB node,
150 # replica-set, or sharded-cluster.
151 # It maintains management of underlying sockets and routing to individual nodes.
152 #
153 # See [`mongoc_client_t`](http://api.mongodb.org/c/current/mongoc_client_t.html).
154 extern class NativeMongoClient `{ mongoc_client_t * `}
155
156 # Wrapper for `mongoc_client_new()`.
157 #
158 # Creates a new `mongoc_client_t` using the `uri` string provided.
159 new(uri: NativeString) `{
160 mongoc_init();
161 return mongoc_client_new(uri);
162 `}
163
164 # Wrapper for `mongoc_client_get_server_status()`.
165 #
166 # Queries the server for the current server status.
167 # Returns `null` if an error occured.
168 fun server_status: nullable NativeBSON import set_mongoc_error, NativeBSON.as nullable `{
169 bson_error_t error;
170 bson_t *reply = bson_new();
171 if(!mongoc_client_get_server_status(self, NULL, reply, &error)){
172 NativeMongoClient_set_mongoc_error(self, &error);
173 return null_NativeBSON();
174 }
175 return NativeBSON_as_nullable(reply);
176 `}
177
178 # Wrapper for `mongoc_client_get_database_names()`.
179 #
180 # This function queries the MongoDB server for a list of known databases.
181 # Returns `null` if an error occured.
182 fun database_names: nullable NativeCStringArray
183 import set_mongoc_error, NativeCStringArray, NativeCStringArray.as nullable `{
184 bson_error_t error;
185 char **strv;
186 if(strv = mongoc_client_get_database_names(self, &error)) {
187 return NativeCStringArray_as_nullable(strv);
188 }
189 NativeMongoClient_set_mongoc_error(self, &error);
190 return null_NativeCStringArray();
191 `}
192
193 # Wrapper for `mongoc_client_destroy()`.
194 #
195 # This instance should not be used beyond this point!
196 fun destroy `{
197 mongoc_client_destroy(self);
198 mongoc_cleanup();
199 `}
200
201 # Utility method to set `Sys.last_mongoc_error`.
202 fun set_mongoc_error(err: BSONError) do sys.last_mongoc_error = err
203 end
204
205 # Wrapper for `mongoc_database_t`.
206 #
207 # `mongoc_database_t` provides access to a MongoDB database.
208 # This handle is useful for actions a particular database object.
209 # It is not a container for `mongoc_collection_t` structures.
210 #
211 # See [`mongoc_database_t`](http://api.mongodb.org/c/current/mongoc_database_t.html).
212 extern class NativeMongoDb `{ mongoc_database_t * `}
213
214 # Wrapper for `mongoc_client_get_database()`.
215 #
216 # Get a newly allocated `mongoc_database_t` for the database named name.
217 #
218 # Database are automatically created on the MongoDB server upon insertion of
219 # the first document into a collection.
220 # There is no need to create a database manually.
221 new(client: NativeMongoClient, db_name: NativeString) `{
222 return mongoc_client_get_database(client, db_name);
223 `}
224
225 # Wrapper for `mongoc_database_get_collection_names()`.
226 #
227 # Fetches a `NULL` terminated array of `NULL-byte` terminated `char*` strings
228 # containing the names of all of the collections in database.
229 fun collection_names: nullable NativeCStringArray
230 import set_mongoc_error, NativeCStringArray, NativeCStringArray.as nullable `{
231 bson_error_t error;
232 char **strv;
233 if(strv = mongoc_database_get_collection_names(self, &error)) {
234 return NativeCStringArray_as_nullable(strv);
235 }
236 NativeMongoDb_set_mongoc_error(self, &error);
237 return null_NativeCStringArray();
238 `}
239
240 # Wrapper for `mongoc_database_get_collection()`.
241 #
242 # Allocates a new `mongoc_collection_t` structure for the collection named
243 # `name` in database.
244 fun collection(name: NativeString): NativeMongoCollection `{
245 return mongoc_database_get_collection(self, name);
246 `}
247
248 # Wrapper for `mongoc_database_has_collection()`.
249 #
250 # This function checks to see if a collection exists on the MongoDB server
251 # within database.
252 fun has_collection(name: NativeString): Bool import set_mongoc_error `{
253 bson_error_t error;
254 if(!mongoc_database_has_collection(self, name, &error)) {
255 NativeMongoDb_set_mongoc_error(self, &error);
256 return false;
257 }
258 return true;
259 `}
260
261 # Wrapper for `mongoc_database_drop()`.
262 #
263 # This function attempts to drop a database on the MongoDB server.
264 fun drop: Bool import set_mongoc_error `{
265 bson_error_t error;
266 if(!mongoc_database_drop(self, &error)) {
267 NativeMongoDb_set_mongoc_error(self, &error);
268 return false;
269 }
270 return true;
271 `}
272
273 # Wrapper for `mongoc_database_destroy()`.
274 #
275 # This instance should not be used beyond this point!
276 fun destroy `{ mongoc_database_destroy(self); `}
277
278 # Utility method to set `Sys.last_mongoc_error`.
279 fun set_mongoc_error(err: BSONError) do sys.last_mongoc_error = err
280 end
281
282 # Wrapper for `mongoc_collection_t`.
283 #
284 # `mongoc_collection_t` provides access to a MongoDB collection.
285 # This handle is useful for actions for most CRUD operations,
286 # I.e. insert, update, delete, find, etc.
287 #
288 # It is an error to call `mongoc_collection_destroy()` on a collection that has
289 # operations pending.
290 # It is required that you release `mongoc_cursor_t` structures before calling
291 # `mongoc_collection_destroy()`.
292 #
293 # See [`mongoc_collection_t`](http://api.mongodb.org/c/current/mongoc_collection_t.html).
294 extern class NativeMongoCollection `{ mongoc_collection_t * `}
295
296 # Wrapper for `mongoc_client_get_collection()`.
297 #
298 # Get a newly allocated `mongoc_collection_t` for the collection named
299 # `collection` in the database named `db`.
300 #
301 # Collections are automatically created on the MongoDB server upon insertion
302 # of the first document.
303 # There is no need to create a collection manually.
304 new(client: NativeMongoClient, db, collection: NativeString) `{
305 return mongoc_client_get_collection(client, db, collection);
306 `}
307
308 # Wrapper for `mongoc_collection_insert()`.
309 #
310 # This function shall insert `document` into the collection.
311 # If no `_id` element is found in document, then a `bson_oid_t` will be
312 # generated locally and added to the document.
313 #
314 # You can retrieve a generated `_id` from `sys.last_mongoc_id`.
315 fun insert(document: NativeBSON): Bool import set_mongoc_error, set_mongoc_last_id `{
316 bson_oid_t oid;
317 if(!bson_has_field(document, "_id")) {
318 bson_oid_init (&oid, NULL);
319 BSON_APPEND_OID (document, "_id", &oid);
320 NativeMongoCollection_set_mongoc_last_id(self, &oid);
321 }
322 bson_error_t error;
323 if(!mongoc_collection_insert(self, MONGOC_INSERT_NONE, document, NULL, &error)) {
324 NativeMongoCollection_set_mongoc_error(self, &error);
325 return false;
326 }
327 return true;
328 `}
329
330 # Wrapper for `mongoc_collection_save()`.
331 #
332 # This function shall save a document into the collection.
333 # If the document has an `_id` field it will be updated.
334 # Otherwise it will be inserted.
335 #
336 # You can retrieve a generated `_id` from `sys.last_mongoc_id`.
337 fun save(document: NativeBSON): Bool import set_mongoc_error, set_mongoc_last_id `{
338 bson_oid_t oid;
339 if(!bson_has_field(document, "_id")) {
340 bson_oid_init (&oid, NULL);
341 BSON_APPEND_OID (document, "_id", &oid);
342 NativeMongoCollection_set_mongoc_last_id(self, &oid);
343 }
344 bson_error_t error;
345 if(!mongoc_collection_save(self, document, NULL, &error)) {
346 NativeMongoCollection_set_mongoc_error(self, &error);
347 return false;
348 }
349 return true;
350 `}
351
352 # Wrapper for `mongoc_collection_remove(MONGOC_REMOVE_SINGLE_REMOVE)`.
353 #
354 # This function shall remove the first document in the collection that matches
355 # `selector`.
356 # The bson selector is not validated, simply passed along as appropriate to the server.
357 fun remove(selector: NativeBSON): Bool import set_mongoc_error `{
358 bson_error_t error;
359 if(!mongoc_collection_remove(self, MONGOC_REMOVE_SINGLE_REMOVE, selector, NULL, &error)) {
360 NativeMongoCollection_set_mongoc_error(self, &error);
361 return false;
362 }
363 return true;
364 `}
365
366 # Wrapper for `mongoc_collection_remove(MONGOC_REMOVE_NONE)`.
367 #
368 # This function shall remove documents in the collection that match `selector`.
369 fun remove_all(selector: NativeBSON): Bool import set_mongoc_error `{
370 bson_error_t error;
371 if(!mongoc_collection_remove(self, MONGOC_REMOVE_NONE, selector, NULL, &error)) {
372 NativeMongoCollection_set_mongoc_error(self, &error);
373 return false;
374 }
375 return true;
376 `}
377
378 # Wrapper for `mongoc_collection_update(MONGOC_UPDATE_NONE)`.
379 #
380 # This function shall update the first document in the collection that
381 # matches `selector`.
382 fun update(selector, update: NativeBSON): Bool import set_mongoc_error `{
383 bson_error_t error;
384 if(!mongoc_collection_update(self, MONGOC_UPDATE_NONE, selector, update, NULL, &error)) {
385 NativeMongoCollection_set_mongoc_error(self, &error);
386 return false;
387 }
388 return true;
389 `}
390
391 # Wrapper for `mongoc_collection_update(MONGOC_UPDATE_MULTI_UPDATE)`.
392 #
393 # This function shall update documents in the collection that match `selector`.
394 fun update_all(selector, update: NativeBSON): Bool import set_mongoc_error `{
395 bson_error_t error;
396 if(!mongoc_collection_update(self, MONGOC_UPDATE_MULTI_UPDATE, selector, update, NULL, &error)) {
397 NativeMongoCollection_set_mongoc_error(self, &error);
398 return false;
399 }
400 return true;
401 `}
402
403 # Wrapper for `mongoc_collection_count()`.
404 #
405 # This function shall execute a count `query` on the underlying collection.
406 fun count(query: NativeBSON): Int import set_mongoc_error `{
407 bson_error_t error;
408 int64_t count = mongoc_collection_count(self, MONGOC_QUERY_NONE, query, 0, 0, NULL, &error);
409 if(count < 0) {
410 NativeMongoCollection_set_mongoc_error(self, &error);
411 return -1;
412 }
413 return count;
414 `}
415
416 # Wrapper for `mongoc_collection_find()`.
417 #
418 # This function shall execute a `query` on the underlying collection.
419 #
420 # If no options are necessary, `query` can simply contain a query such as `{a:1}`.
421 #
422 # If you would like to specify options such as a sort order,
423 # the query must be placed inside of `{"$query": {}}`.
424 fun find(query: NativeBSON): nullable NativeMongoCursor import
425 NativeMongoCursor.as nullable, set_mongoc_error `{
426 bson_error_t error;
427 mongoc_cursor_t *cursor;
428 cursor = mongoc_collection_find(self, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL);
429
430 if (mongoc_cursor_error(cursor, &error)) {
431 NativeMongoCollection_set_mongoc_error(self, &error);
432 return null_NativeMongoCursor();
433 }
434
435 return NativeMongoCursor_as_nullable(cursor);
436 `}
437
438 # Wrapper for `mongoc_collection_stats()`.
439 #
440 # This function is a helper to retrieve statistics about the collection.
441 fun stats: nullable NativeBSON import set_mongoc_error, NativeBSON.as nullable `{
442 bson_error_t error;
443 bson_t *reply = bson_new();
444 if(!mongoc_collection_stats(self, NULL, reply, &error)){
445 NativeMongoCollection_set_mongoc_error(self, &error);
446 return null_NativeBSON();
447 }
448 return NativeBSON_as_nullable(reply);
449 `}
450
451 # Wrapper for `mongoc_collection_drop()`.
452 #
453 # This function requests that the `collection` be dropped,
454 # including all indexes associated with the collection.
455 fun drop: Bool import set_mongoc_error `{
456 bson_error_t error;
457 if(!mongoc_collection_drop(self, &error)) {
458 NativeMongoCollection_set_mongoc_error(self, &error);
459 return false;
460 }
461 return true;
462 `}
463
464 # Wrapper for `mongoc_collection_rename()`.
465 #
466 # This function is a helper to rename an existing collection on a MongoDB server.
467 # The name of the collection will also be updated internally so it is safe
468 # to continue using this collection after the rename.
469 # Additional operations will occur on renamed collection.
470 fun rename(new_database, new_name: NativeString): Bool `{
471 bson_error_t error;
472 if(!mongoc_collection_rename(self, new_database, new_name, false, &error)){
473 NativeMongoCollection_set_mongoc_error(self, &error);
474 return false;
475 }
476 return true;
477 `}
478
479 # Wrapper for `mongoc_collection_destroy()`.
480 #
481 # This instance should not be used beyond this point!
482 fun destroy `{ mongoc_collection_destroy(self); `}
483
484 # Utility method to set `Sys.last_mongoc_last_id`.
485 fun set_mongoc_last_id(id: BSONObjectId) do sys.last_mongoc_id = id
486
487 # Utility method to set `Sys.last_mongoc_error`.
488 fun set_mongoc_error(err: BSONError) do sys.last_mongoc_error = err
489 end
490
491 # Wrapper for `mongoc_cursor_t`.
492 #
493 # `mongoc_cursor_t` provides access to a MongoDB query cursor.
494 # It wraps up the wire protocol negotation required to initiate a query and
495 # retreive an unknown number of documents.
496 #
497 # Cursors are lazy, meaning that no network traffic occurs until the first call
498 # to mongoc_cursor_next().
499 #
500 # At that point we can:
501 # * Retreive more records with repeated calls to `mongoc_cursor_next()`.
502 # * Test for more records with `mongoc_cursor_more()`.
503 # * Retrieve the document under the cursor with `mongoc_cursor_current()`.
504 #
505 # See [`mongoc_cursor_t`](http://api.mongodb.org/c/current/mongoc_cursor_t.html).
506 extern class NativeMongoCursor `{ mongoc_cursor_t* `}
507
508 # Wrapper for `mongoc_cursor_current()`.
509 #
510 # Fetches the cursors current document or NULL if there has been an error.
511 fun current: NativeBSON `{ return (bson_t*) mongoc_cursor_current(self); `}
512
513 # Wrapper for `mongoc_cursor_next()`.
514 #
515 # This function shall iterate the underlying cursor, setting `current` to the next
516 # document.
517 #
518 # This function is a blocking function.
519 fun next: Bool `{
520 const bson_t *doc;
521 return mongoc_cursor_next(self, &doc);
522 `}
523
524 # Wrapper for `mongoc_cursor_more()`.
525 #
526 # This function shall indicate if there is more data to be read from the cursor.
527 fun more: Bool `{ return mongoc_cursor_more(self); `}
528
529 # Wrapper for `mongoc_cursor_destroy()`.
530 #
531 # This instance should not be used beyond this point!
532 fun destroy `{ mongoc_cursor_destroy(self); `}
533 end