meshblu-core-datastore
Version:
200 lines (167 loc) • 7.22 kB
text/coffeescript
_ = require 'lodash'
async = require 'async'
crypto = require 'crypto'
stringify = require 'json-stable-stringify'
class Datastore
constructor: ({database,collection,,,,}) ->
throw new Error('Datastore: requires database') unless database?
throw new Error('Datastore: requires collection') unless collection?
?= 60 * 60
= database.collection collection
= database.collection "deleted-#{collection}"
find: (query, projection, options, callback) =>
if _.isFunction options
callback ?= options
options = undefined
if _.isFunction projection
callback ?= projection
projection = undefined
return callback new Error("Datastore: requires query") if _.isEmpty query
options ?= {}
projection ?= {}
unless _.isEmpty projection
falsey = _.some _.values(projection), (value) => value == false
unless falsey
_.each , (attribute) =>
projection[attribute] = true
projection._id = false
.find query, projection, options, (error, data) =>
return callback error if error?
{projection, data}, (error) =>
return callback error if error?
callback null, data
findAndUpdate: ({ query, update, projection }, callback) =>
return callback new Error("Datastore: requires query") if _.isEmpty query
projection ?= {}
unless _.isEmpty projection
falsey = _.some _.values(projection), (value) => value == false
unless falsey
_.each , (attribute) =>
projection[attribute] = true
projection._id = false
sort = {'_id': 1}
.findAndModify { query, sort, update, fields: projection }, (error, result) =>
return callback error if error?
{query}, (error) =>
return callback error if error?
callback null, result
findOne: (query, projection, callback) =>
if _.isFunction projection
callback ?= projection
projection = undefined
return callback new Error("Datastore: requires query") if _.isEmpty query
projection ?= {}
unless _.isEmpty projection
falsey = _.some _.values(projection), (value) => value == false
unless falsey
_.each , (attribute) =>
projection[attribute] = true
projection._id = false
{query, projection}, (error, data) =>
return callback error if error?
return callback null, data if data?
.findOne query, projection, (error, data) =>
return callback error if error?
{query, projection, data}, (error) =>
return callback error if error?
callback null, data
findOneRecycled: (query, callback) =>
return callback new Error("Datastore: requires query") if _.isEmpty query
.findOne query, callback
insert: (record, callback) =>
.insert record, (error) =>
callback error
recycle: (query, callback) =>
return callback new Error("Datastore: requires query") if _.isEmpty query
.find query, (error, records) =>
return callback error if error?
async.parallel [
async.apply , query
async.apply , records
], callback
remove: (query, callback) =>
return callback new Error("Datastore: requires query") if _.isEmpty query
.remove query, (error) =>
return callback error if error?
{query}, callback
update: (query, data, callback) =>
return callback new Error("Datastore: requires query") if _.isEmpty query
.update query, data, (error, result) =>
return callback error if error?
return callback null, updated: false if result.nModified == 0
{query}, (error) =>
return callback error if error?
return callback null, updated: true
upsert: (query, data, callback) =>
return callback new Error("Datastore: requires query") if _.isEmpty query
.update query, data, {upsert: true}, (error) =>
return callback error if error?
{query}, callback
_findCacheRecord: ({query, projection}, callback) =>
return callback() unless ?
cacheKey = {query}
return callback() unless cacheKey?
cacheField = {query, projection}
{cacheKey, cacheField}, callback
_logError: (error) =>
return unless error?
console.error 'Error: ', error.message
_updateCacheRecord: ({query, projection, data}, callback) =>
return callback() unless ?
cacheKey = {query}
return callback() unless cacheKey?
cacheField = {query, projection}
{cacheKey, cacheField, data}, callback
_updateCacheRecords: ({projection, data}, callback) =>
return callback() unless ?
records = {}
async.eachLimit data, 100, (record, done) =>
attributes = _.pick record,
recordCacheKey = {query: attributes}
recordCacheField = {query: attributes, projection: projection}
records[recordCacheKey] = recordCacheField if recordCacheKey?
{query: attributes, projection: projection, data: record}, done
, callback
_clearCacheRecord: ({query}, callback) =>
return callback() unless ?
cacheKey = {query}
return callback() unless cacheKey?
.del cacheKey, (error) =>
# ignore any redis return values
callback error
return # redis fix
_generateCacheField: ({query, projection}) =>
cacheField = stringify(query) + stringify(projection || '')
crypto.createHash('sha1').update(cacheField).digest('hex')
_generateCacheKey: ({query}) =>
return unless ?
attributes = _.pick query,
return unless _.size(_.keys(attributes)) == _.size
cacheKey = stringify(attributes)
crypto.createHash('sha1').update(cacheKey).digest('hex')
_getCacheRecord: ({cacheKey, cacheField}, callback) =>
return callback() unless ?
.hget cacheKey, cacheField, (error, data) =>
return callback error if error?
return callback() unless data?
data = data
{ cacheKey }, (error) =>
return callback error if error?
callback null, data
return # redis fix
_recycleRecords: (records, callback) =>
async.eachLimit records, 100, (record, next) =>
.insert record, next
, callback
_setCacheRecord: ({cacheKey, cacheField, data}, callback) =>
return callback() unless ?
.hset cacheKey, cacheField, JSON.stringify(data), (error) =>
return callback error if error?
{ cacheKey }, callback
return # redis fix
_setExpireRecord: ({cacheKey}, callback) =>
.expire cacheKey, , (error) => callback error
return # redis fix
_tryJSON: (str) =>
try return JSON.parse str
module.exports = Datastore