diff --git a/src/database/mongo/main.js b/src/database/mongo/main.js index 5a7b7a75c2..884be9d2f7 100644 --- a/src/database/mongo/main.js +++ b/src/database/mongo/main.js @@ -17,11 +17,14 @@ module.exports = function (module) { } if (Array.isArray(key)) { + if (!key.length) { + return []; + } const data = await module.client.collection('objects').find({ _key: { $in: key }, }, { _id: 0, _key: 1 }).toArray(); - const map = {}; + const map = Object.create(null); data.forEach((item) => { map[item._key] = true; }); diff --git a/src/database/postgres/main.js b/src/database/postgres/main.js index 444af9e5be..c0838b45a0 100644 --- a/src/database/postgres/main.js +++ b/src/database/postgres/main.js @@ -16,38 +16,59 @@ module.exports = function (module) { if (!key) { return; } - - // Redis/Mongo consider empty zsets as non-existent, match that behaviour - const type = await module.type(key); - if (type === 'zset') { - if (Array.isArray(key)) { - const members = await Promise.all(key.map(key => module.getSortedSetRange(key, 0, 0))); - return members.map(member => member.length > 0); - } - const members = await module.getSortedSetRange(key, 0, 0); - return members.length > 0; + const isArray = Array.isArray(key); + if (isArray && !key.length) { + return []; } - if (Array.isArray(key)) { + async function checkIfzSetsExist(keys) { + const members = await Promise.all( + keys.map(key => module.getSortedSetRange(key, 0, 0)) + ); + return members.map(member => member.length > 0); + } + + async function checkIfKeysExist(keys) { const res = await module.pool.query({ name: 'existsArray', text: ` SELECT o."_key" k FROM "legacy_object_live" o WHERE o."_key" = ANY($1::TEXT[])`, - values: [key], + values: [keys], }); - return key.map(k => res.rows.some(r => r.k === k)); + return keys.map(k => res.rows.some(r => r.k === k)); + } + + // Redis/Mongo consider empty zsets as non-existent, match that behaviour + if (isArray) { + const types = await Promise.all(key.map(module.type)); + const zsetKeys = key.filter((_key, i) => types[i] === 'zset'); + const otherKeys = key.filter((_key, i) => types[i] !== 'zset'); + const [zsetExits, otherExists] = await Promise.all([ + checkIfzSetsExist(zsetKeys), + checkIfKeysExist(otherKeys), + ]); + const existsMap = Object.create(null); + zsetKeys.forEach((k, i) => { existsMap[k] = zsetExits[i]; }); + otherKeys.forEach((k, i) => { existsMap[k] = otherExists[i]; }); + return key.map(k => existsMap[k]); + } + const type = await module.type(key); + if (type === 'zset') { + const members = await module.getSortedSetRange(key, 0, 0); + return members.length > 0; } const res = await module.pool.query({ name: 'exists', text: ` SELECT EXISTS(SELECT * FROM "legacy_object_live" - WHERE "_key" = $1::TEXT - LIMIT 1) e`, + WHERE "_key" = $1::TEXT + LIMIT 1) e`, values: [key], }); + return res.rows[0].e; }; diff --git a/src/database/redis/main.js b/src/database/redis/main.js index 8b79afb07c..b849361a8e 100644 --- a/src/database/redis/main.js +++ b/src/database/redis/main.js @@ -14,6 +14,9 @@ module.exports = function (module) { module.exists = async function (key) { if (Array.isArray(key)) { + if (!key.length) { + return []; + } const batch = module.client.batch(); key.forEach(key => batch.exists(key)); const data = await helpers.execBatch(batch); diff --git a/test/database/keys.js b/test/database/keys.js index fde4bbc442..984a5e7a66 100644 --- a/test/database/keys.js +++ b/test/database/keys.js @@ -64,12 +64,15 @@ describe('Key methods', () => { }); }); - it('should work for an array of keys', (done) => { - db.exists(['testKey', 'doesnotexist'], (err, exists) => { - assert.ifError(err); - assert.deepStrictEqual(exists, [true, false]); - done(); - }); + it('should work for an array of keys', async () => { + assert.deepStrictEqual( + await db.exists(['testKey', 'doesnotexist']), + [true, false] + ); + assert.deepStrictEqual( + await db.exists([]), + [] + ); }); describe('scan', () => {