Skip to content

Instantly share code, notes, and snippets.

@nmajor nmajor/redis.js
Created Oct 6, 2018

Embed
What would you like to do?
redis.js - Class RedisModel
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
You can’t perform that action at this time.