Skip to content

Instantly share code, notes, and snippets.

@davidlondono
Last active July 12, 2018 13:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidlondono/10924678cf6307960583f8c080c5972c to your computer and use it in GitHub Desktop.
Save davidlondono/10924678cf6307960583f8c080c5972c to your computer and use it in GitHub Desktop.
Paginate Redis
/**
* Created by david on 9/27/16.
*/
import redis from './connectionRedis';
import ZList from './zlist';
import Promise from 'bluebird';
export default class ModelList {
constructor(name) {
this.name = name;
this.list = new ZList(name);
}
plugin(plugin, opts) {
plugin(this, opts);
}
getScoreById(id) {
return redis.zscore(`${this.name}`, id);
}
memberisOfType({ member, type }) {
return redis.sismember(`${this.name}:${type}`, member);
}
add({ type, id, score }) {
return Promise.all([
this.list.add({ score, member: id }),
redis.sadd(`${this.name}:${type}`, id),
]);
}
}
/**
* Created by david on 9/27/16.
*/
/**
*
* @param {ModelList} model
*/
import Promise from 'bluebird';
export default (model) => {
model.paginate = async function({
sinceId, maxId, limit = 1, select, where = {},
keyPaginated = '_id', reverse = false,
filter,
} = {}, callback) {
const range = {};
if (sinceId) {
range.min = await model.getScoreById(sinceId);
}
if (maxId) {
range.max = await model.getScoreById(maxId);
}
const limitRange = {
count: limit + 1,
};
const objectsFirstFound = await model.list.rangeByScore({
range,
reverse, limit: limitRange });
if (!objectsFirstFound.length) {
return {
objectsFirstFound,
};
}
let objects = [];
if (filter) {
let objToFilter = objectsFirstFound;
do {
const objectsFiltered = await Promise.resolve(objToFilter).filter(filter);
objects = objects.concat(objectsFiltered);
// console.log('after filter', objectsFirstFound);
const lastScore = objToFilter[objToFilter.length - 1].score;
limitRange.count -= objectsFiltered.length;
range.max = parseInt(lastScore, 10) - 1;
objToFilter = await model.list.rangeByScore({
range,
reverse, limit: limitRange });
} while (limitRange.count > 0 && objToFilter.length > 0);
}
let nextMaxId;
if (objects.length >= limit) {
const last = objectsFirstFound.pop();
nextMaxId = last.member;
}
return {
objects,
nextMaxId,
};
};
};
/**
* Created by James on 26/09/16.
*/
import redis from './connectionRedis';
/* import _debug from 'debug';
const debug = _debug('feedModule:zlist');*/
function castToObject(array) {
const objects = [];
for (let i = 0; i < array.length; i += 2) {
objects.push(
{ member: array[i],
score: array[i + 1] });
}
return objects;
}
class ZList {
constructor(name) {
this.name = name;
}
async add({ score, member }) {
return await redis.zadd(this.name, score, member);
}
async remove(member) {
return await redis.zrem(this.name, member);
}
async scan(cursor) {
const [pointer, listOfKeys] = await redis.zscan(this.name, cursor);
return {
pointer,
data: castToObject(listOfKeys),
};
}
async rangeByScore({
range: { min = '-inf', max = '+inf' } = {},
limit, reverse = false } = {}) {
const redisArgs = [];
if (limit) {
const { offset = 0, count = 1 } = limit;
redisArgs.push('LIMIT');
redisArgs.push(offset);
redisArgs.push(count);
}
redisArgs.push('WITHSCORES');
let promiseFind;
if (reverse) {
promiseFind = redis.zrevrangebyscore(this.name, max, min, ...redisArgs);
} else {
promiseFind = redis.zrangebyscore(this.name, min, max, ...redisArgs);
}
const memberArrays = await promiseFind;
return castToObject(memberArrays);
}
}
export default ZList;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment