mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
committed by
GitHub
parent
c279875aa6
commit
723fe8e8e0
@@ -466,6 +466,48 @@ module.exports = function (module) {
|
||||
}
|
||||
}
|
||||
|
||||
module.getSortedSetScan = async function (params) {
|
||||
const project = { _id: 0, value: 1 };
|
||||
if (params.withScores) {
|
||||
project.score = 1;
|
||||
}
|
||||
|
||||
let match = params.match;
|
||||
if (params.match.startsWith('*')) {
|
||||
match = match.substring(1);
|
||||
}
|
||||
if (params.match.endsWith('*')) {
|
||||
match = match.substring(0, match.length - 1);
|
||||
}
|
||||
match = utils.escapeRegexChars(match);
|
||||
if (!params.match.startsWith('*')) {
|
||||
match = '^' + match;
|
||||
}
|
||||
if (!params.match.endsWith('*')) {
|
||||
match += '$';
|
||||
}
|
||||
let regex;
|
||||
try {
|
||||
regex = new RegExp(match);
|
||||
} catch (err) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cursor = module.client.collection('objects').find({
|
||||
_key: params.key, value: { $regex: regex },
|
||||
}, { projection: project });
|
||||
|
||||
if (params.limit) {
|
||||
cursor.limit(params.limit);
|
||||
}
|
||||
|
||||
const data = await cursor.toArray();
|
||||
if (!params.withScores) {
|
||||
return data.map(d => d.value);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
module.processSortedSet = async function (setKey, processFn, options) {
|
||||
var done = false;
|
||||
var ids = [];
|
||||
|
||||
@@ -610,6 +610,35 @@ DELETE FROM "legacy_zset" z
|
||||
return q;
|
||||
}
|
||||
|
||||
module.getSortedSetScan = async function (params) {
|
||||
let match = params.match;
|
||||
if (match.startsWith('*')) {
|
||||
match = '%' + match.substring(1);
|
||||
}
|
||||
|
||||
if (match.endsWith('*')) {
|
||||
match = match.substring(0, match.length - 1) + '%';
|
||||
}
|
||||
|
||||
const res = await module.pool.query({
|
||||
text: `
|
||||
SELECT z."value",
|
||||
z."score"
|
||||
FROM "legacy_object_live" o
|
||||
INNER JOIN "legacy_zset" z
|
||||
ON o."_key" = z."_key"
|
||||
AND o."type" = z."type"
|
||||
WHERE o."_key" = $1::TEXT
|
||||
AND z."value" LIKE '${match}'
|
||||
LIMIT $2::INTEGER`,
|
||||
values: [params.key, params.limit],
|
||||
});
|
||||
if (!params.withScores) {
|
||||
return res.rows.map(r => r.value);
|
||||
}
|
||||
return res.rows.map(r => ({ value: r.value, score: parseFloat(r.score) }));
|
||||
};
|
||||
|
||||
module.processSortedSet = async function (setKey, process, options) {
|
||||
const client = await module.pool.connect();
|
||||
var batchSize = (options || {}).batch || 100;
|
||||
|
||||
@@ -48,11 +48,11 @@ module.exports = function (redisClient) {
|
||||
zrank: util.promisify(redisClient.zrank).bind(redisClient),
|
||||
zrevrank: util.promisify(redisClient.zrevrank).bind(redisClient),
|
||||
zincrby: util.promisify(redisClient.zincrby).bind(redisClient),
|
||||
|
||||
zrangebylex: util.promisify(redisClient.zrangebylex).bind(redisClient),
|
||||
zrevrangebylex: util.promisify(redisClient.zrevrangebylex).bind(redisClient),
|
||||
zremrangebylex: util.promisify(redisClient.zremrangebylex).bind(redisClient),
|
||||
zlexcount: util.promisify(redisClient.zlexcount).bind(redisClient),
|
||||
zscan: util.promisify(redisClient.zscan).bind(redisClient),
|
||||
|
||||
lpush: util.promisify(redisClient.lpush).bind(redisClient),
|
||||
rpush: util.promisify(redisClient.rpush).bind(redisClient),
|
||||
|
||||
@@ -276,4 +276,34 @@ module.exports = function (module) {
|
||||
}
|
||||
return await module.client.async[method](args);
|
||||
}
|
||||
|
||||
module.getSortedSetScan = async function (params) {
|
||||
let cursor = '0';
|
||||
|
||||
const returnData = [];
|
||||
let done = false;
|
||||
do {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
const res = await module.client.async.zscan(params.key, cursor, 'MATCH', params.match, 'COUNT', 100);
|
||||
cursor = res[0];
|
||||
done = cursor === '0';
|
||||
const data = res[1];
|
||||
|
||||
for (let i = 0; i < data.length; i += 2) {
|
||||
const value = data[i];
|
||||
const score = parseFloat(data[i + 1]);
|
||||
if (params.withScores) {
|
||||
returnData.push({ value: value, score: score });
|
||||
} else {
|
||||
returnData.push(value);
|
||||
}
|
||||
if (params.limit && returnData.length >= params.limit) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
return returnData;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -26,6 +26,71 @@ describe('Sorted Set methods', function () {
|
||||
], done);
|
||||
});
|
||||
|
||||
describe('sortedSetScan', function () {
|
||||
it('should find matches in sorted set containing substring', async () => {
|
||||
await db.sortedSetAdd('scanzset', [1, 2, 3, 4, 5, 6], ['aaaa', 'bbbb', 'bbcc', 'ddd', 'dddd', 'fghbc']);
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'scanzset',
|
||||
match: '*bc*',
|
||||
});
|
||||
assert(data.includes('bbcc'));
|
||||
assert(data.includes('fghbc'));
|
||||
});
|
||||
|
||||
it('should find matches in sorted set with scores', async () => {
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'scanzset',
|
||||
match: '*bc*',
|
||||
withScores: true,
|
||||
});
|
||||
data.sort((a, b) => a.score - b.score);
|
||||
assert.deepStrictEqual(data, [{ value: 'bbcc', score: 3 }, { value: 'fghbc', score: 6 }]);
|
||||
});
|
||||
|
||||
it('should find matches in sorted set with a limit', async () => {
|
||||
await db.sortedSetAdd('scanzset2', [1, 2, 3, 4, 5, 6], ['aaab', 'bbbb', 'bbcb', 'ddb', 'dddd', 'fghbc']);
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'scanzset2',
|
||||
match: '*b*',
|
||||
limit: 2,
|
||||
});
|
||||
assert.equal(data.length, 2);
|
||||
});
|
||||
|
||||
it('should work for special characters', async () => {
|
||||
await db.sortedSetAdd('scanzset3', [1, 2, 3, 4, 5], ['aaab{', 'bbbb', 'bbcb{', 'ddb', 'dddd']);
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'scanzset3',
|
||||
match: '*b{',
|
||||
limit: 2,
|
||||
});
|
||||
assert(data.includes('aaab{'));
|
||||
assert(data.includes('bbcb{'));
|
||||
});
|
||||
|
||||
it('should find everything starting with string', async () => {
|
||||
await db.sortedSetAdd('scanzset4', [1, 2, 3, 4, 5], ['aaab{', 'bbbb', 'bbcb', 'ddb', 'dddd']);
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'scanzset4',
|
||||
match: 'b*',
|
||||
limit: 2,
|
||||
});
|
||||
assert(data.includes('bbbb'));
|
||||
assert(data.includes('bbcb'));
|
||||
});
|
||||
|
||||
it('should find everything ending with string', async () => {
|
||||
await db.sortedSetAdd('scanzset5', [1, 2, 3, 4, 5, 6], ['aaab{', 'bbbb', 'bbcb', 'ddb', 'dddd', 'adb']);
|
||||
const data = await db.getSortedSetScan({
|
||||
key: 'scanzset5',
|
||||
match: '*db',
|
||||
});
|
||||
assert.equal(data.length, 2);
|
||||
assert(data.includes('ddb'));
|
||||
assert(data.includes('adb'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortedSetAdd()', function () {
|
||||
it('should add an element to a sorted set', function (done) {
|
||||
db.sortedSetAdd('sorted1', 1, 'value1', function (err) {
|
||||
|
||||
Reference in New Issue
Block a user