Created
October 6, 2018 14:39
-
-
Save nmajor/e772d0dd166c8c3bbffb2bee00faa8a2 to your computer and use it in GitHub Desktop.
redis.js - Class RedisModel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import shortid from 'shortid'; | |
import _ from 'lodash'; | |
function getRedisClient() { | |
// IMPLEMENT YOUR OWN METHOD OF GETTING THE REDIS CLIENT, | |
} | |
function expSeconds(days = 30) { | |
const secondsPerDay = 86400; | |
return days * secondsPerDay; | |
} | |
function buildId() { return shortid.generate(); } | |
const stringify = { | |
string: data => data, | |
number: data => data.toString(), | |
date: data => new Date(data).toISOString(), | |
time: data => new Date(data).toISOString(), | |
object: data => JSON.stringify(data), | |
}; | |
const parsify = { | |
string: data => data, | |
number: data => parseInt(data, 10), | |
date: data => new Date(data), | |
time: data => new Date(data), | |
object: data => JSON.parse(data), | |
}; | |
export default class RedisModel { | |
constructor(schema) { | |
this.props = { | |
schema, | |
client: getRedisClient(), | |
}; | |
} | |
_buildHashValues(data) { | |
const { schema: { attributes } } = this.props; | |
const hashValues = []; | |
_.each(data, (value, key) => { | |
const type = (attributes[key] || {}).kind; | |
const stringValue = stringify[type] ? stringify[type](value) : undefined; | |
if (!type || !stringValue) return null; | |
return hashValues.push(key, stringValue); | |
}); | |
return _.isEmpty(hashValues) ? undefined : hashValues; | |
} | |
_buildRedisKey(id) { | |
const { schema } = this.props; | |
return `${schema.namespace}:${id}`; | |
} | |
_buildIndexName(indexName) { | |
const { schema } = this.props; | |
return `${schema.namespace}:${indexName}`; | |
} | |
_unpackHashValues(data) { | |
const { schema: { attributes } } = this.props; | |
const obj = {}; | |
_.each(data, (value, key) => { | |
const type = (attributes[key] || {}).kind; | |
if (!type) return null; | |
obj[key] = parsify[type](value); | |
return null; | |
}); | |
return _.isEmpty(obj) ? undefined : obj; | |
} | |
_clearOldIndexes(indexName) { | |
const { client, schema } = this.props; | |
return new Promise((resolve, reject) => { | |
const now = new Date().getTime(); | |
const secondsPerDay = 86400; | |
const args = [ | |
this._buildIndexName(schema, indexName), | |
(now + (expSeconds() - secondsPerDay) * 1000), | |
'-inf', | |
]; | |
client.zremrangebyscore(args, (err, results) => { | |
if (err) return reject(err); | |
return resolve(results); | |
}); | |
}); | |
} | |
_getIndexedIds(indexName, offset, limit) { | |
const { client } = this.props; | |
return this._clearOldIndexes(indexName) | |
.then(() => new Promise((resolve, reject) => { | |
const args = [indexName, '+inf', '-inf', 'LIMIT', offset || 0, limit || 20]; | |
client.zrevrangebyscore(args, (err, results) => { | |
if (err) return reject(err); | |
return resolve(results); | |
}); | |
})); | |
} | |
create(data) { | |
const { schema, client } = this.props; | |
return new Promise((resolve, reject) => { | |
const multi = client.multi(); | |
const id = buildId(); | |
const redisKey = this._buildRedisKey(id); | |
// Handle the hash values | |
const hashValues = this._buildHashValues(data); | |
if (hashValues) { | |
multi.hmset(redisKey, hashValues); | |
multi.hmset(redisKey, 'EX', expSeconds()); | |
// Handle indexes | |
if (schema.indexes) { | |
_.each(schema.indexes, (index) => { | |
if (index.shouldIndex(data)) { | |
multi.zadd(this._buildIndexName(index.getName(data)), index.getValue(data), redisKey); | |
} | |
}); | |
} | |
multi.exec((err) => { | |
if (err) return reject(err); | |
return resolve({ key: redisKey, _id: id, ...data }); | |
}); | |
} else { | |
reject(new Error('Empty redis hash data')); | |
} | |
}); | |
} | |
update(id, data) { | |
const { client } = this.props; | |
return new Promise((resolve, reject) => { | |
const redisKey = this._buildRedisKey(id); | |
// Handle the hash values | |
const hashValues = this._buildHashValues(data); | |
if (hashValues) { | |
client.hmset(redisKey, hashValues, (err) => { | |
if (err) return reject(err); | |
return resolve(); | |
}); | |
} else resolve(); | |
}); | |
} | |
findOne(id) { | |
const { client } = this.props; | |
return new Promise((resolve, reject) => { | |
const redisKey = this._buildRedisKey(id); | |
client.hgetall(redisKey, (err, data) => { | |
if (err) return reject(err); | |
return resolve({ | |
key: redisKey, | |
_id: id, | |
...this._unpackHashValues(data), | |
}); | |
}); | |
}); | |
} | |
fromIndex(indexName) { | |
return this._getIndexedIds(indexName, undefined, 250) | |
.then(results => Promise.all(results.map(result => this.findOne(result)))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment