Handles MongoDB database transactions using the mongoskin module. It supports
a very simple failover mechanism where you can specify a “backup” connection
string to which the module will connect in case the main database is down.
If you prefer tp access Mongo directly, you can use the db property, for example:
expresser.database.db.collection(“mycollection”).findAndModify(args…).
class Database
lodash = require "lodash"
logger = require "./logger.coffee"
settings = require "./settings.coffee"
mongo = require "mongoskin"@property [Object] Database object (using mongoskin), will be set during init.
db: nullInit the databse module and test the connection straight away. @param [Object] options Database init options.
init: (options) =>
if settings.database.connString? and settings.database.connString isnt ""
@setDb settings.database.connString, settings.database.options
else
logger.debug "Database.init", "No connection string set.", "Database module won't work."Get data from the database. A collection and callback must be specified. The filter is optional.
Please note that if filter has an _id or id field, or if it’s a plain string or number, it will be used
to return documents by ID. Otherwise it’s used as keys-values object for filtering.
@param [String] collection The collection name.
@param [String, Object] filter Optional, if a string or number, assume it’s the document ID. Otherwise assume keys-values filter.
@param [Object] options Options to be passed to the query.
@option options [Integer] limit Limits the resultset to X documents.
@param [Method] callback Callback (err, result) when operation has finished.
get: (collection, filter, options, callback) =>
if not callback?
if lodash.isFunction options
callback = options
options = null
else if lodash.isFunction filter
callback = filter
filter = nullCallback is mandatory!
if not callback?
if settings.logger.autoLogErrors
logger.error "Database.get", "No callback specified. Abort!", collection, filter
throw new Error "Database.get: a callback (last argument) must be specified."No DB set? Throw exception.
if not @db?
if settings.logger.autoLogErrors
logger.error "Database.get", "The db is null or was not initialized. Abort!", collection, filter
return callback "Database.insert: the db was not initialized, please check database settings and call its 'init' method."Create the DB callback helper.
dbCallback = (err, result) =>
if callback?
result = @normalizeId result if settings.database.normalizeId
callback err, resultSet collection object.
dbCollection = @db.collection collectionParse ID depending on filter.
if filter?
if filter._id?
id = filter._id
else if filter.id? and settings.database.normalizeId
id = filter.id
else
t = typeof filter
id = filter if t is "string" or t is "integer"Get limit option.
if options?.limit?
limit = options.limit
else
limit = 0Find documents depending on filter and options.
If id is set, use the shorter findById.
if id?
dbCollection.findById id, dbCallbackCreate a params object for the find method.
else if filter?
findParams = {$query: filter}
findParams["$orderby"] = options.orderBy if options?.orderBy?
if limit > 0
dbCollection.find(findParams).limit(limit).toArray dbCallback
else
dbCollection.find(findParams).toArray dbCallbackSearch everything!
else
if limit > 0
dbCollection.find({}).limit(limit).toArray dbCallback
else
dbCollection.find({}).toArray dbCallback
if filter?
filterLog = filter
filterLog.password = "***" if filterLog.password?
filterLog.passwordHash = "***" if filterLog.passwordHash?
logger.debug "Database.get", collection, filterLog, options
else
logger.debug "Database.get", collection, "No filter.", optionsAdd new documents to the database.
The options parameter is optional.
@param [String] collection The collection name.
@param [Object] obj Document or array of documents to be added.
@param [Method] callback Callback (err, result) when operation has finished.
insert: (collection, obj, callback) =>
if not obj?
if settings.logger.autoLogErrors
logger.error "Database.insert", "No object specified. Abort!", collection
if callback?
callback "Database.insert: no object (second argument) was specified."
return falseNo DB set? Throw exception.
if not @db?
if settings.logger.autoLogErrors
logger.error "Database.insert", "The db is null or was not initialized. Abort!", collection
if callback?
callback "Database.insert: the db was not initialized, please check database settings and call its 'init' method."
return falseCreate the DB callback helper.
dbCallback = (err, result) =>
if callback?
result = @normalizeId(result) if settings.database.normalizeId
callback err, resultSet collection object.
dbCollection = @db.collection collectionExecute insert!
dbCollection.insert obj, dbCallback
logger.debug "Database.insert", collectionUpdate existing documents on the database.
The options parameter is optional.
@param [String] collection The collection name.
@param [Object] obj Document or data to be updated.
@param [Object] options Optional, options to control and filter the insert behaviour.
@option options [Object] filter Defines the query filter. If not specified, will try using the ID of the passed object.
@option options [Boolean] patch Default is false, if true replace only the specific properties of documents instead of the whole data, using $set.
@option options [Boolean] upsert Default is false, if true it will create documents if none was found.
@param [Method] callback Callback (err, result) when operation has finished.
update: (collection, obj, options, callback) =>
if not callback? and lodash.isFunction options
callback = options
options = {}Object or filter is mandatory.
if not obj?
if settings.logger.autoLogErrors
logger.error "Database.update", "No object specified. Abort!", collection
if callback?
callback "Database.update: no object (second argument) was specified."
return falseNo DB set? Throw exception.
if not @db?
if settings.logger.autoLogErrors
logger.error "Database.update", "The db is null or was not initialized. Abort!", collection
if callback?
callback "Database.update: the db was not initialized, please check database settings and call its 'init' method."
return falseCreate the DB callback helper.
dbCallback = (err, result) =>
if callback?
result = @normalizeId(result) if settings.database.normalizeId
callback err, resultSet collection object.
dbCollection = @db.collection collectionMake sure the ID is converted to ObjectID.
if obj._id?
id = mongo.ObjectID.createFromHexString obj._id.toString()
else if obj.id? and settings.database.normalizeId
id = mongo.ObjectID.createFromHexString obj.id.toString()Make sure options is valid.
options = {} if not options?If a filter option was set, use it as the query filter otherwise use the “_id” property.
if options.filter?
filter = options.filter
else
filter = {"_id": id}If options patch is set, replace specified document properties only instead of replacing the whole document.
if options.patch
docData = {$set: obj}
else
docData = objSet default options.
options = lodash.defaults options, {"new": true, "insert": false}Execute update!
dbCollection.update filter, docData, options, dbCallback
if id?
logger.debug "Database.update", collection, options, "ID: #{id}"
else
logger.debug "Database.update", collection, options, "New document."DEPRECATED! Alias for update, will be removed soon.
set: =>
console.warn "Database.set", "Method is deprecated, use .insert or .update instead!"
@update.apply this, argumentsDelete an object from the database. The obj argument can be either the document itself, or its integer/string ID.
@param [String] collection The collection name.
@param [String, Object] filter If a string or number, assume it’s the document ID. Otherwise assume the document itself.
@param [Method] callback Callback (err, result) when operation has finished.
remove: (collection, filter, callback) =>
if not callback? and lodash.isFunction options
callback = options
options = {}Filter is mandatory.
if not filter?
if settings.logger.autoLogErrors
logger.error "Database.remove", "No filter specified. Abort!", collection
if callback?
callback "Database.remove: no filter (second argument) was specified."
return falseNo DB set? Throw exception.
if not @db?
if settings.logger.autoLogErrors
logger.error "Database.remove", "The db is null or was not initialized. Abort!", collection
if callback?
callback "Database.remove: the db was not initialized, please check database settings and call its 'init' method."
return falseCheck it the obj is the model itself, or only the ID string / number.
if filter._id?
id = filter._id
else if filter.id and settings.database.normalizeId
id = filter.id
else
t = typeof filter
id = filter if t is "string" or t is "integer"Create the DB callback helper.
dbCallback = (err, result) =>
if callback?
result = @normalizeId(result) if settings.database.normalizeId
callback err, resultSet collection object and remove specified object from the database.
dbCollection = @db.collection collectionRemove object by ID or filter.
if id? and id isnt ""
dbCollection.removeById id, dbCallback
else
dbCollection.remove filter, dbCallback
logger.debug "Database.remove", collection, filterAlias for remove.
del: => @remove.apply this, argumentsCount documents from the database. A collection must be specified.
If no filter is not passed then count all documents.
@param [String] collection The collection name.
@param [Object] filter Optional, keys-values filter of documents to be counted.
@param [Method] callback Callback (err, result) when operation has finished.
count: (collection, filter, callback) =>
if not callback? and lodash.isFunction filter
callback = filter
filter = {}Callback is mandatory!
if not callback?
if settings.logger.autoLogErrors
logger.error "Database.count", "No callback specified. Abort!", collection, filter
throw new Error "Database.count: a callback (last argument) must be specified."Create the DB callback helper.
dbCallback = (err, result) =>
if callback?
logger.debug "Database.count", collection, filter, "Result #{result}"
callback err, resultMongoDB has a built-in count so use it.
dbCollection = @db.collection collection
dbCollection.count filter, dbCallbackHelper to transform MongoDB document “_id” to “id”. @param [Object] result The document or result to be normalized. @return [Object] Returns the normalized document.
normalizeId: (result) =>
return if not result?
isArray = lodash.isArray result or lodash.isArguments resultCheck if result is a collection / array or a single document.
if isArray
for obj in result
if obj["_id"]?
obj["id"] = obj["_id"].toString()
delete obj["_id"]
else if result["_id"]?
result["id"] = result["_id"].toString()
delete result["_id"]
return resultHelper to set the current DB object. Can be called externally but ideally you should control the connection string by updating your app settings.json file. @param [Object] connString The connection string, for example user:password@hostname/dbname. @param [Object] options Additional options to be passed when creating the DB connection object.
setDb: (connString, options) =>
@db = mongo.db connString, optionsSafe logging, strip username and password.
sep = connString.indexOf "@"
connStringSafe = connString
connStringSafe = connStringSafe.substring sep if sep > 0
logger.debug "Database.setDb", connStringSafe, optionsDatabase.getInstance = ->
@instance = new Database() if not @instance?
return @instance
module.exports = exports = Database.getInstance()