0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Optimised the storage of collection posts relations

refs https://github.com/TryGhost/Arch/issues/95

Rather than a big nested loop to reconcile the in-memory vs. persisted
PostCollections we can instead use the events to know which rows we have to
delete and which we have to insert. This removes a tonne of work.

This implementation isn't perfect, and misses cases where the same post is
added and removed, our use-cases don't currently support that however.
This commit is contained in:
Fabien "egg" O'Carroll 2023-09-22 15:23:07 +07:00 committed by Fabien 'egg' O'Carroll
parent c6f2c985f1
commit db488b4124
2 changed files with 30 additions and 36 deletions

View file

@ -204,45 +204,39 @@ module.exports = class BookshelfCollectionsRepository {
transacting: options.transaction
});
const collectionPostsRelations = collection.posts.map((postId, index) => {
return {
id: (new ObjectID).toHexString(),
sort_order: collection.type === 'manual' ? index : 0,
collection_id: collection.id,
post_id: postId
};
});
const collectionPostRelationsToDeleteIds = [];
if (collection.type === 'manual') {
const collectionPostsRelations = collection.posts.map((postId, index) => {
return {
id: (new ObjectID).toHexString(),
sort_order: index,
collection_id: collection.id,
post_id: postId
};
});
await this.#relationModel.query().delete().where('collection_id', collection.id).transacting(options.transaction);
} else {
const collectionPostsOptions = {
transaction: options.transaction,
columns: ['id', 'post_id']
};
const existingRelations = await this.#fetchCollectionPostIds(collection.id, collectionPostsOptions);
for (const existingRelation of existingRelations) {
const found = collectionPostsRelations.find((thing) => {
return thing.post_id === existingRelation.post_id;
});
if (found) {
found.id = null;
} else {
collectionPostRelationsToDeleteIds.push(existingRelation.id);
}
if (collectionPostsRelations.length > 0) {
await this.#relationModel.query().insert(collectionPostsRelations).transacting(options.transaction);
}
}
} else {
const collectionPostsToDelete = collection.events.filter(event => event.type === 'CollectionPostRemoved').map((event) => {
return event.data.post_id;
});
const missingCollectionPostsRelations = collectionPostsRelations.filter(thing => thing.id !== null);
const collectionPostsToInsert = collection.events.filter(event => event.type === 'CollectionPostAdded').map((event) => {
return {
id: (new ObjectID).toHexString(),
sort_order: 0,
collection_id: collection.id,
post_id: event.data.post_id
};
});
if (missingCollectionPostsRelations.length > 0) {
await this.#relationModel.query().insert(missingCollectionPostsRelations).transacting(options.transaction);
}
if (collectionPostRelationsToDeleteIds.length > 0) {
await this.#relationModel.query().delete().whereIn('id', collectionPostRelationsToDeleteIds).transacting(options.transaction);
if (collectionPostsToDelete.length > 0) {
await this.#relationModel.query().delete().where('collection_id', collection.id).whereIn('post_id', collectionPostsToDelete).transacting(options.transaction);
}
if (collectionPostsToInsert.length > 0) {
await this.#relationModel.query().insert(collectionPostsToInsert).transacting(options.transaction);
}
}
options.transaction.executionPromise.then(() => {

View file

@ -572,7 +572,7 @@ describe('Collections API', function () {
}, this.skip.bind(this));
const collectionRelatedQueries = queries.filter(query => query.sql.includes('collection'));
assert.equal(collectionRelatedQueries.length, 13);
assert.equal(collectionRelatedQueries.length, 12);
}
await agent
@ -604,7 +604,7 @@ describe('Collections API', function () {
}, this.skip.bind(this));
const collectionRelatedQueries = queries.filter(query => query.sql.includes('collection'));
assert.equal(collectionRelatedQueries.length, 18);
assert.equal(collectionRelatedQueries.length, 16);
}
await agent