X-Git-Url: http://nitlanguage.org diff --git a/lib/mongodb/mongodb.nit b/lib/mongodb/mongodb.nit index 4a0718f..78cd75d 100644 --- a/lib/mongodb/mongodb.nit +++ b/lib/mongodb/mongodb.nit @@ -40,8 +40,6 @@ # var res = col.find(query) # assert res["bar"] == "bar" # ~~~ -# -# TODO Get last Object_ID module mongodb import json @@ -57,16 +55,11 @@ in "C header" `{ # * [Binary JSON spec](http://bsonspec.org/) # * [Libbson](http://api.mongodb.org/libbson/1.1.4/)# private class BSON - super Finalizable + super FinalizableOnce # Native instance pointer. var native: NativeBSON - # Is the native instance valid? - # - # This is set to false if the `native` is destroyed. - var is_alive = true - # Returns a new BSON object initialized from the content of `json`. # # ~~~ @@ -97,7 +90,6 @@ private class BSON end redef fun to_s do - assert is_alive var ns = native.to_native_string var res = ns.to_s_with_copy ns.free # manual free of gc allocated NativeString @@ -116,16 +108,15 @@ private class BSON # assert json["ELS"].as(JsonArray).is_empty # ~~~ fun to_json: JsonObject do - assert is_alive - return to_s.parse_json.as(JsonObject) - end - - redef fun finalize do - if is_alive then - native.destroy - is_alive = false + var json = to_s.parse_json + if json isa JsonParseError then + print json.message + sys.exit 1 end + return json.as(JsonObject) end + + redef fun finalize_once do native.destroy end redef class JsonObject @@ -146,26 +137,14 @@ class MongoError private var native: BSONError - # Is the native instance valid? - # - # This is set to false if the `native` is destroyed. - private var is_alive = true - # Logical domain within a library that created the error. - fun domain: Int do - assert is_alive - return native.domain - end + fun domain: Int do return native.domain # Domain specific error code. - fun code: Int do - assert is_alive - return native.code - end + fun code: Int do return native.code # Human readable error message. fun message: String do - assert is_alive var ns = native.message var res = ns.to_s_with_copy ns.free @@ -175,6 +154,34 @@ class MongoError redef fun to_s do return "{message} (code: {code})" end +# MongoDB Object ID representation. +# +# For ObjectIDs, MongoDB uses the `ObjectId("hash")` notation. +# This notation is replicated by the `to_s` service. +# +# Since the MongoDB notation is not JSON complient, the mongoc wrapper uses +# a JSON based notation like `{"$oid": "hash"}`. +# This is the notation returned by the `to_json` service. +private class MongoObjectId + + var native: BSONObjectId + + # The unique ID as an MongoDB Object ID string. + fun id: String do return native.id + + # Internal JSON representation of this Object ID. + # + # Something like `{"$oid": "5578e5dcf344225cc2378051"}`. + fun to_json: JsonObject do + var obj = new JsonObject + obj["$oid"] = id + return obj + end + + # Formatted as `ObjectId("5578e5dcf344225cc2378051")` + redef fun to_s do return "ObjectId({id})" +end + # The MongoClient is used to connect to the mongo server and send queries. # # Usage: @@ -185,21 +192,14 @@ end # assert client.server_uri == uri # ~~~ class MongoClient - super Finalizable + super FinalizableOnce # Server URI. var server_uri: String private var native: NativeMongoClient is noinit - # Is the native instance valid? - # - # This is set to false if the `native` is destroyed. - private var is_alive = true - - init do - native = new NativeMongoClient(server_uri.to_cstring) - end + init do native = new NativeMongoClient(server_uri.to_cstring) # Gets server data. # @@ -210,7 +210,6 @@ class MongoClient # assert client.server_status["process"] == "mongod" # ~~~ fun server_status: nullable JsonObject do - assert is_alive var nbson = native.server_status if nbson == null then return null var bson = new BSON(nbson) @@ -227,9 +226,9 @@ class MongoClient # assert client.database_names.has("test") # ~~~ fun database_names: Array[String] do - assert is_alive var res = new Array[String] var nas = native.database_names + if nas == null then return res var i = 0 var name = nas[i] while not name.address_is_null do @@ -251,25 +250,14 @@ class MongoClient # var client = new MongoClient("mongodb://localhost:27017/") # assert client.database("test").name == "test" # ~~~ - fun database(name: String): MongoDb do - assert is_alive - return new MongoDb(self, name) - end + fun database(name: String): MongoDb do return new MongoDb(self, name) # Close the connexion and destroy the instance. # # The reference should not be used beyond this point! - fun close do - assert is_alive - finalize - end + fun close do finalize_once - redef fun finalize do - if is_alive then - native.destroy - is_alive = false - end - end + redef fun finalize_once do native.destroy # Last error raised by mongoc. fun last_error: nullable MongoError do @@ -277,6 +265,22 @@ class MongoClient if last_error == null then return null return new MongoError(last_error) end + + # Last auto generated id. + private fun last_id: nullable MongoObjectId do + var last_id = sys.last_mongoc_id + if last_id == null then return null + return new MongoObjectId(last_id) + end + + # Set the last generated id or `null` to unset once used. + private fun last_id=(id: nullable MongoObjectId) do + if id == null then + sys.last_mongoc_id = null + else + sys.last_mongoc_id = id.native + end + end end # A MongoDb database. @@ -285,7 +289,7 @@ end # first document into a collection. # There is no need to create a database manually. class MongoDb - super Finalizable + super FinalizableOnce # `MongoClient` used to load this database. var client: MongoClient @@ -295,14 +299,7 @@ class MongoDb private var native: NativeMongoDb is noinit - # Is the native instance valid? - # - # This is set to false if the `native` is destroyed. - private var is_alive = true - - init do - native = new NativeMongoDb(client.native, name.to_cstring) - end + init do native = new NativeMongoDb(client.native, name.to_cstring) # Lists available collection names. # @@ -315,9 +312,9 @@ class MongoDb # assert db.collection_names.has("test") # ~~~ fun collection_names: Array[String] do - assert is_alive var res = new Array[String] var nas = native.collection_names + if nas == null then return res var i = 0 var name = nas[i] while not name.address_is_null do @@ -338,7 +335,6 @@ class MongoDb # assert col.name == "test" # ~~~ fun collection(name: String): MongoCollection do - assert is_alive return new MongoCollection(self, name) end @@ -350,23 +346,14 @@ class MongoDb # assert not db.has_collection("qwerty") # ~~~ fun has_collection(name: String): Bool do - assert is_alive # TODO handle error return native.has_collection(name.to_cstring) end # Drop `self`, returns false if an error occured. - fun drop: Bool do - assert is_alive - return native.drop - end + fun drop: Bool do return native.drop - redef fun finalize do - if is_alive then - native.destroy - is_alive = false - end - end + redef fun finalize_once do native.destroy end # A Mongo collection. @@ -375,7 +362,7 @@ end # the first document. # There is no need to create a database manually. class MongoCollection - super Finalizable + super FinalizableOnce # Database that collection belongs to. var database: MongoDb @@ -385,11 +372,6 @@ class MongoCollection private var native: NativeMongoCollection is noinit - # Is the native instance valid? - # - # This is set to false if the `native` is destroyed. - private var is_alive = true - # Loads a collection. # # Call `MongoDb::collection` instead. @@ -400,6 +382,15 @@ class MongoCollection name.to_cstring) end + # Set the autogenerated last id if the `doc` does not contain one already. + private fun set_id(doc: JsonObject) do + var last_id = database.client.last_id + if last_id != null then + doc["_id"] = last_id.to_json + database.client.last_id = null + end + end + # Inserts a new document in the collection. # # If no _id element is found in document, then a new one be generated locally @@ -415,17 +406,18 @@ class MongoCollection # doc["bar"] = "bar" # doc["baz"] = new JsonArray # assert col.insert(doc) + # assert doc.has_key("_id") # ~~~ fun insert(doc: JsonObject): Bool do - assert is_alive - return native.insert(doc.to_bson.native) + var res = native.insert(doc.to_bson.native) + if res then set_id(doc) + return res end # Inserts multiple documents in the collection. # # See `insert`. fun insert_all(docs: Collection[JsonObject]): Bool do - assert is_alive var res = true for doc in docs do res = insert(doc) and res return res @@ -441,15 +433,27 @@ class MongoCollection # ~~~ # var client = new MongoClient("mongodb://localhost:27017/") # var col = client.database("test").collection("test") + # # var doc = new JsonObject # doc["foo"] = 10 # doc["bar"] = "bar" # doc["baz"] = new JsonArray + # # assert col.save(doc) # will be inserted + # assert doc.has_key("_id") + # + # var id = doc["_id"] + # assert col.save(doc) # will be updated + # assert doc["_id"] == id # ~~~ fun save(doc: JsonObject): Bool do - assert is_alive - return native.save(doc.to_bson.native) + var bson = doc.to_bson + var nat = bson.native + var res = native.save(nat) + if res then set_id(doc) + assert nat != self #FIXME used to avoid GC crashes + assert bson != self #FIXME used to avoid GC crashes + return res end # Removes the first document that matches `selector`. @@ -464,7 +468,6 @@ class MongoCollection # assert col.remove(sel) # ~~~ fun remove(selector: JsonObject): Bool do - assert is_alive return native.remove(selector.to_bson.native) end @@ -472,7 +475,6 @@ class MongoCollection # # See `remove`. fun remove_all(selector: JsonObject): Bool do - assert is_alive return native.remove_all(selector.to_bson.native) end @@ -490,7 +492,6 @@ class MongoCollection # assert col.update(sel, upd) # ~~~ fun update(selector: JsonObject, update: JsonObject): Bool do - assert is_alive return native.update( selector.to_bson.native, update.to_bson.native) @@ -517,12 +518,15 @@ class MongoCollection # assert col.count(query) > 0 # ~~~ fun count(query: JsonObject): Int do - assert is_alive return native.count(query.to_bson.native) end # Finds the first document that matches `query`. # + # Params: + # * `skip` number of documents to skip + # * `limit` number of documents to return + # # Returns `null` if an error occured. See `Sys::last_mongoc_error`. # # ~~~ @@ -531,24 +535,30 @@ class MongoCollection # var query = new JsonObject # query["foo"] = 10 # var doc = col.find(query) - # print doc or else "nullllllllllllllllll" - # print doc.to_json # assert doc["foo"] == 10 # ~~~ - fun find(query: JsonObject): nullable JsonObject do - assert is_alive - var c = native.find(query.to_bson.native) + fun find(query: JsonObject, skip, limit: nullable Int): nullable JsonObject do + var q = new NativeBSON.from_json_string(query.to_json.to_cstring) + var s = skip or else 0 + var l = limit or else 0 + var c = native.find(q, s, l) + q.destroy if c == null then return null var cursor = new MongoCursor(c) - if cursor.is_ok then - cursor.next - return cursor.item + if not cursor.is_ok then + return null end - return null + var item = cursor.item + assert cursor != self + return item end # Finds all the documents matching the `query`. # + # Params: + # * `skip` number of documents to skip + # * `limit` number of documents to return + # # ~~~ # var client = new MongoClient("mongodb://localhost:27017/") # var col = client.database("test").collection("test") @@ -556,13 +566,17 @@ class MongoCollection # query["foo"] = 10 # assert col.find_all(query).length > 0 # ~~~ - fun find_all(query: JsonObject): Array[JsonObject] do - assert is_alive + fun find_all(query: JsonObject, skip, limit: nullable Int): Array[JsonObject] do + var s = skip or else 0 + var l = limit or else 0 var res = new Array[JsonObject] - var c = native.find(query.to_bson.native) + var c = native.find(query.to_bson.native, s, l) if c == null then return res var cursor = new MongoCursor(c) - for item in cursor do res.add item + while cursor.is_ok do + res.add cursor.item + cursor.next + end return res end @@ -576,17 +590,13 @@ class MongoCollection # assert col.stats["ns"] == "test.test" # ~~~ fun stats: nullable JsonObject do - assert is_alive var bson = native.stats if bson == null then return null return new JsonObject.from_bson(new BSON(bson)) end # Drops `self`, returns false if an error occured. - fun drop: Bool do - assert is_alive - return native.drop - end + fun drop: Bool do return native.drop # Moves `self` to another `database`. # @@ -594,7 +604,6 @@ class MongoCollection # this collection after the move. # Additional operations will occur on moved collection. fun move(database: MongoDb): Bool do - assert is_alive self.database = database return native.rename(database.name.to_cstring, name.to_cstring) end @@ -605,17 +614,11 @@ class MongoCollection # to continue using this collection after the rename. # Additional operations will occur on renamed collection. fun rename(name: String): Bool do - assert is_alive self.name = name return native.rename(database.name.to_cstring, name.to_cstring) end - redef fun finalize do - if is_alive then - native.destroy - is_alive = false - end - end + redef fun finalize_once do native.destroy end # A MongoDB query cursor. @@ -623,37 +626,20 @@ end # It wraps up the wire protocol negotation required to initiate a query and # retreive an unknown number of documents. class MongoCursor - super Finalizable + super FinalizableOnce super Iterator[JsonObject] private var native: NativeMongoCursor - # Is the native instance valid? - # - # This is set to false if the `native` is destroyed. - private var is_alive = true - init do next - redef fun is_ok do - assert is_alive - return native.more - end + redef var is_ok = true - redef fun next do - assert is_alive - native.next - end + redef fun next do is_ok = native.next redef fun item do - assert is_alive return new JsonObject.from_bson(new BSON(native.current)) end - redef fun finalize do - if is_alive then - native.destroy - is_alive = false - end - end + redef fun finalize_once do native.destroy end