diff options
Diffstat (limited to 'lib/impure/db_mongo.nim')
-rw-r--r-- | lib/impure/db_mongo.nim | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/lib/impure/db_mongo.nim b/lib/impure/db_mongo.nim new file mode 100644 index 000000000..a2aebb6da --- /dev/null +++ b/lib/impure/db_mongo.nim @@ -0,0 +1,213 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a higher level wrapper for `mongodb`:idx:. Example: +## +## .. code-block:: nimrod +## +## import mongo, db_mongo, oids, json +## +## var conn = db_mongo.open() +## +## # construct JSON data: +## var data = %{"a": %13, "b": %"my string value", +## "inner": %{"i": %71} } +## +## var id = insertID(conn, "test.test", data) +## +## for v in find(conn, "test.test", "this.a == 13"): +## print v +## +## delete(conn, "test.test", id) +## close(conn) + +import mongo, oids, json + +type + EDb* = object of EIO ## exception that is raised if a database error occurs + TDbConn* = TMongo ## a database connection; alias for ``TMongo`` + +proc dbError*(db: TDbConn, msg: string) {.noreturn.} = + ## raises an EDb exception with message `msg`. + var e: ref EDb + new(e) + if db.errstr[0] != '\0': + e.msg = $db.errstr + else: + e.msg = $db.err & " " & msg + raise e + +proc Close*(db: var TDbConn) = + ## closes the database connection. + disconnect(db) + destroy(db) + +proc Open*(host: string = defaultHost, port: int = defaultPort): TDbConn = + ## opens a database connection. Raises `EDb` if the connection could not + ## be established. + init(result) + + let x = connect(result, host, port.cint) + if x != 0'i32: + dbError(result, "cannot open: " & host) + +proc jsonToBSon(b: var TBSon, key: string, j: PJsonNode) = + case j.kind + of JString: + add(b, key, j.str) + of JInt: + add(b, key, j.num) + of JFloat: + add(b, key, j.fnum) + of JBool: + addBool(b, key, ord(j.bval)) + of JNull: + addNull(b, key) + of JObject: + addStartObject(b, key) + for k, v in items(j.fields): + jsonToBSon(b, k, v) + addFinishObject(b) + of JArray: + addStartArray(b, key) + for i, e in pairs(j.elems): + jsonToBSon(b, $i, e) + addFinishArray(b) + +proc jsonToBSon*(j: PJsonNode, oid: TOid): TBSon = + ## converts a JSON value into the BSON format. The result must be + ## ``destroyed`` explicitely! + init(result) + assert j.kind == JObject + add(result, "_id", oid) + for key, val in items(j.fields): + jsonToBSon(result, key, val) + finish(result) + +proc `[]`*(obj: var TBSon, fieldname: cstring): TBSon = + ## retrieves the value belonging to `fieldname`. Raises `EInvalidKey` if + ## the attribute does not exist. + var it = initIter(obj) + let res = find(it, result, fieldname) + if res == bkEOO: + raise newException(EInvalidIndex, "key not in object") + +proc getId*(obj: var TBSon): TOid = + ## retrieves the ``_id`` attribute of `obj`. + var it = initIter(obj) + var b: TBSon + let res = find(it, b, "_id") + if res == bkOID: + result = oidVal(it)[] + else: + raise newException(EInvalidIndex, "_id not in object") + +proc insertID*(db: var TDbConn, namespace: string, data: PJsonNode): TOid = + ## converts `data` to BSON format and inserts it in `namespace`. Returns + ## the generated OID for the ``_id`` field. + result = genOid() + var x = jsonToBSon(data, result) + insert(db, namespace, x) + destroy(x) + +proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) = + ## converts `data` to BSON format and inserts it in `namespace`. + discard InsertID(db, namespace, data) + +proc update*(db: var TDbConn, namespace: string, obj: var TBSon) = + ## updates `obj` in `namespace`. + var cond: TBson + init(cond) + cond.add("_id", getId(obj)) + finish(cond) + update(db, namespace, cond, obj, ord(UPDATE_UPSERT)) + destroy(cond) + +proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) = + ## updates the data with `oid` to have the new data `obj`. + var a = jsonToBSon(obj, oid) + Update(db, namespace, a) + destroy(a) + +proc delete*(db: var TDbConn, namespace: string, oid: TOid) = + ## Deletes the object belonging to `oid`. + var cond: TBson + init(cond) + cond.add("_id", oid) + finish(cond) + discard remove(db, namespace, cond) + destroy(cond) + +proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) = + ## Deletes the object `obj`. + delete(db, namespace, getId(obj)) + +iterator find*(db: var TDbConn, namespace: string): var TBSon = + ## iterates over any object in `namespace`. + var cursor: TCursor + init(cursor, db, namespace) + while next(cursor) == mongo.OK: + yield bson(cursor)[] + destroy(cursor) + +iterator find*(db: var TDbConn, namespace: string, + query, fields: var TBSon): var TBSon = + ## yields the `fields` of any document that suffices `query`. + var cursor = find(db, namespace, query, fields, 0'i32, 0'i32, 0'i32) + if cursor != nil: + while next(cursor[]) == mongo.OK: + yield bson(cursor[])[] + destroy(cursor[]) + +proc setupFieldnames(fields: openArray[string]): TBSon = + init(result) + for x in fields: add(result, x, 1'i32) + finish(result) + +iterator find*(db: var TDbConn, namespace: string, + query: var TBSon, fields: openArray[string]): var TBSon = + ## yields the `fields` of any document that suffices `query`. If `fields` + ## is ``[]`` the whole document is yielded. + var f = setupFieldnames(fields) + var cursor = find(db, namespace, query, f, 0'i32, 0'i32, 0'i32) + if cursor != nil: + while next(cursor[]) == mongo.OK: + yield bson(cursor[])[] + destroy(cursor[]) + destroy(f) + +proc setupQuery(query: string): TBSon = + init(result) + add(result, "$where", query) + finish(result) + +iterator find*(db: var TDbConn, namespace: string, + query: string, fields: openArray[string]): var TBSon = + ## yields the `fields` of any document that suffices `query`. If `fields` + ## is ``[]`` the whole document is yielded. + var f = setupFieldnames(fields) + var q = setupQuery(query) + var cursor = find(db, namespace, q, f, 0'i32, 0'i32, 0'i32) + if cursor != nil: + while next(cursor[]) == mongo.OK: + yield bson(cursor[])[] + destroy(cursor[]) + destroy(q) + destroy(f) + +when false: + # this doesn't work this way; would require low level hacking + iterator fieldPairs*(obj: var TBSon): tuple[key: cstring, value: TBSon] = + ## iterates over `obj` and yields all (key, value)-Pairs. + var it = initIter(obj) + var v: TBSon + while next(it) != bkEOO: + let key = key(it) + discard init(v, value(it)) + yield (key, v) |