mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-28 01:26:16 +01:00
* fix: #8954, clear purged replies and toPids * fix: redis test
This commit is contained in:
committed by
GitHub
parent
39dae0aaff
commit
5bb5ec4618
@@ -149,7 +149,7 @@ module.exports = function (module) {
|
||||
};
|
||||
|
||||
module.deleteObjectFields = async function (key, fields) {
|
||||
if (!key || !Array.isArray(fields) || !fields.length) {
|
||||
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
|
||||
return;
|
||||
}
|
||||
fields = fields.filter(Boolean);
|
||||
@@ -162,8 +162,12 @@ module.exports = function (module) {
|
||||
field = helpers.fieldToString(field);
|
||||
data[field] = '';
|
||||
});
|
||||
if (Array.isArray(key)) {
|
||||
await module.client.collection('objects').updateMany({ _key: { $in: key } }, { $unset: data });
|
||||
} else {
|
||||
await module.client.collection('objects').updateOne({ _key: key }, { $unset: data });
|
||||
}
|
||||
|
||||
await module.client.collection('objects').updateOne({ _key: key }, { $unset: data });
|
||||
cache.del(key);
|
||||
};
|
||||
|
||||
|
||||
@@ -247,20 +247,26 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
|
||||
};
|
||||
|
||||
module.deleteObjectFields = async function (key, fields) {
|
||||
if (!key || !Array.isArray(fields) || !fields.length) {
|
||||
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await module.pool.query({
|
||||
name: 'deleteObjectFields',
|
||||
text: `
|
||||
UPDATE "legacy_hash"
|
||||
SET "data" = COALESCE((SELECT jsonb_object_agg("key", "value")
|
||||
FROM jsonb_each("data")
|
||||
WHERE "key" <> ALL ($2::TEXT[])), '{}')
|
||||
WHERE "_key" = $1::TEXT`,
|
||||
values: [key, fields],
|
||||
});
|
||||
async function delKey(key, fields) {
|
||||
await module.pool.query({
|
||||
name: 'deleteObjectFields',
|
||||
text: `
|
||||
UPDATE "legacy_hash"
|
||||
SET "data" = COALESCE((SELECT jsonb_object_agg("key", "value")
|
||||
FROM jsonb_each("data")
|
||||
WHERE "key" <> ALL ($2::TEXT[])), '{}')
|
||||
WHERE "_key" = $1::TEXT`,
|
||||
values: [key, fields],
|
||||
});
|
||||
}
|
||||
if (Array.isArray(key)) {
|
||||
await Promise.all(key.map(k => delKey(k, fields)));
|
||||
} else {
|
||||
await delKey(key, fields);
|
||||
}
|
||||
};
|
||||
|
||||
module.incrObjectField = async function (key, field) {
|
||||
|
||||
@@ -150,14 +150,21 @@ module.exports = function (module) {
|
||||
};
|
||||
|
||||
module.deleteObjectFields = async function (key, fields) {
|
||||
if (!key || !Array.isArray(fields) || !fields.length) {
|
||||
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
|
||||
return;
|
||||
}
|
||||
fields = fields.filter(Boolean);
|
||||
if (!fields.length) {
|
||||
return;
|
||||
}
|
||||
await module.client.async.hdel(key, fields);
|
||||
if (Array.isArray(key)) {
|
||||
const batch = module.client.batch();
|
||||
key.forEach(k => batch.hdel(k, fields));
|
||||
await helpers.execBatch(batch);
|
||||
} else {
|
||||
await module.client.async.hdel(key, fields);
|
||||
}
|
||||
|
||||
cache.del(key);
|
||||
};
|
||||
|
||||
|
||||
@@ -122,13 +122,18 @@ module.exports = function (Posts) {
|
||||
}
|
||||
|
||||
async function deletePostFromReplies(postData) {
|
||||
if (!parseInt(postData.toPid, 10)) {
|
||||
return;
|
||||
const replyPids = await db.getSortedSetMembers('pid:' + postData.pid + ':replies');
|
||||
const promises = [
|
||||
db.deleteObjectFields(
|
||||
replyPids.map(pid => 'post:' + pid), ['toPid']
|
||||
),
|
||||
db.delete('pid:' + postData.pid + ':replies'),
|
||||
];
|
||||
if (parseInt(postData.toPid, 10)) {
|
||||
promises.push(db.sortedSetRemove('pid:' + postData.toPid + ':replies', postData.pid));
|
||||
promises.push(db.decrObjectField('post:' + postData.toPid, 'replies'));
|
||||
}
|
||||
await Promise.all([
|
||||
db.sortedSetRemove('pid:' + postData.toPid + ':replies', postData.pid),
|
||||
db.decrObjectField('post:' + postData.toPid, 'replies'),
|
||||
]);
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async function deletePostFromGroups(postData) {
|
||||
|
||||
33
src/upgrades/1.15.4/clear_purged_replies.js
Normal file
33
src/upgrades/1.15.4/clear_purged_replies.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const db = require('../../database');
|
||||
|
||||
const batch = require('../../batch');
|
||||
|
||||
module.exports = {
|
||||
name: 'Clear purged replies and toPid',
|
||||
timestamp: Date.UTC(2020, 10, 26),
|
||||
method: async function () {
|
||||
const progress = this.progress;
|
||||
|
||||
await batch.processSortedSet('posts:pid', async function (pids) {
|
||||
progress.incr(pids.length);
|
||||
let postData = await db.getObjects(pids.map(pid => 'post:' + pid));
|
||||
postData = postData.filter(p => p && parseInt(p.toPid, 10));
|
||||
if (!postData.length) {
|
||||
return;
|
||||
}
|
||||
const toPids = postData.map(p => p.toPid);
|
||||
const exists = await db.exists(toPids.map(pid => 'post:' + pid));
|
||||
const pidsToDelete = postData.filter((p, index) => !exists[index]).map(p => p.pid);
|
||||
await db.deleteObjectFields(pidsToDelete.map(pid => 'post:' + pid), ['toPid']);
|
||||
|
||||
const repliesToDelete = _.uniq(toPids.filter((pid, index) => !exists[index]));
|
||||
await db.deleteAll(repliesToDelete.map(pid => 'pid:' + pid + ':replies'));
|
||||
}, {
|
||||
progress: progress,
|
||||
batchSize: 500,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -415,6 +415,16 @@ describe('Hash methods', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete multiple fields of multiple objects', async function () {
|
||||
await db.setObject('deleteFields1', { foo: 'foo1', baz: '2' });
|
||||
await db.setObject('deleteFields2', { foo: 'foo2', baz: '3' });
|
||||
await db.deleteObjectFields(['deleteFields1', 'deleteFields2'], ['baz']);
|
||||
const obj1 = await db.getObject('deleteFields1');
|
||||
const obj2 = await db.getObject('deleteFields2');
|
||||
assert.deepStrictEqual(obj1, { foo: 'foo1' });
|
||||
assert.deepStrictEqual(obj2, { foo: 'foo2' });
|
||||
});
|
||||
|
||||
it('should not error if fields is empty array', async () => {
|
||||
await db.deleteObjectFields('someKey', []);
|
||||
});
|
||||
|
||||
@@ -190,6 +190,22 @@ describe('Topic\'s', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete nested relies properly', async function () {
|
||||
const result = await topics.post({ uid: fooUid, title: 'nested test', content: 'main post', cid: topic.categoryId });
|
||||
const reply1 = await topics.reply({ uid: fooUid, content: 'reply post 1', tid: result.topicData.tid });
|
||||
const reply2 = await topics.reply({ uid: fooUid, content: 'reply post 2', tid: result.topicData.tid, toPid: reply1.pid });
|
||||
let replies = await socketPosts.getReplies({ uid: fooUid }, reply1.pid);
|
||||
assert.strictEqual(replies.length, 1);
|
||||
assert.strictEqual(replies[0].content, 'reply post 2');
|
||||
let toPid = await posts.getPostField(reply2.pid, 'toPid');
|
||||
assert.strictEqual(parseInt(toPid, 10), parseInt(reply1.pid, 10));
|
||||
await posts.purge(reply1.pid, fooUid);
|
||||
replies = await socketPosts.getReplies({ uid: fooUid }, reply1.pid);
|
||||
assert.strictEqual(replies.length, 0);
|
||||
toPid = await posts.getPostField(reply2.pid, 'toPid');
|
||||
assert.strictEqual(toPid, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get methods', function () {
|
||||
|
||||
Reference in New Issue
Block a user