summary refs log tree commit diff stats
path: root/lib/impure/db_mongo.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/impure/db_mongo.nim')
-rw-r--r--lib/impure/db_mongo.nim213
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)