Implemented "virtual" Collection for "latest"
refs https://github.com/TryGhost/Arch/issues/95 Rather than storing all of the relations between the latest collection and posts, we know that it contains all posts. This means we don't have to keep the collections posts in sync. Instead we can fetch them from the posts table. This saves a lot of work during recalculation.
This commit is contained in:
parent
45c1a82909
commit
9b2a94f931
@ -108,6 +108,7 @@ type QueryOptions = {
|
||||
|
||||
interface PostsRepository {
|
||||
getAll(options: QueryOptions): Promise<CollectionPost[]>;
|
||||
getAllIds(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export class CollectionsService {
|
||||
@ -128,8 +129,8 @@ export class CollectionsService {
|
||||
this.slugService = deps.slugService;
|
||||
}
|
||||
|
||||
private toDTO(collection: Collection): CollectionDTO {
|
||||
return {
|
||||
private async toDTO(collection: Collection): Promise<CollectionDTO> {
|
||||
const dto = {
|
||||
id: collection.id,
|
||||
title: collection.title,
|
||||
slug: collection.slug,
|
||||
@ -144,6 +145,14 @@ export class CollectionsService {
|
||||
sort_order: index
|
||||
}))
|
||||
};
|
||||
if (collection.slug === 'latest') {
|
||||
const allPostIds = await this.postsRepository.getAllIds();
|
||||
dto.posts = allPostIds.map((id, index) => ({
|
||||
id,
|
||||
sort_order: index
|
||||
}));
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -580,11 +589,9 @@ export class CollectionsService {
|
||||
async getAll(options?: QueryOptions): Promise<{data: CollectionDTO[], meta: any}> {
|
||||
const collections = await this.collectionsRepository.getAll(options);
|
||||
|
||||
const collectionsDTOs: CollectionDTO[] = [];
|
||||
|
||||
for (const collection of collections) {
|
||||
collectionsDTOs.push(this.toDTO(collection));
|
||||
}
|
||||
const collectionsDTOs: CollectionDTO[] = await Promise.all(
|
||||
collections.map(collection => this.toDTO(collection))
|
||||
);
|
||||
|
||||
return {
|
||||
data: collectionsDTOs,
|
||||
@ -602,14 +609,13 @@ export class CollectionsService {
|
||||
}
|
||||
async getCollectionsForPost(postId: string): Promise<CollectionDTO[]> {
|
||||
const collections = await this.collectionsRepository.getAll({
|
||||
filter: `posts:${postId}`
|
||||
filter: `posts:${postId},slug:latest`
|
||||
});
|
||||
|
||||
return collections.map(collection => this.toDTO(collection))
|
||||
.sort((a, b) => {
|
||||
// NOTE: sorting is here to keep DB engine ordering consistent
|
||||
return a.slug.localeCompare(b.slug);
|
||||
});
|
||||
return Promise.all(collections.sort((a, b) => {
|
||||
// NOTE: sorting is here to keep DB engine ordering consistent
|
||||
return a.slug.localeCompare(b.slug);
|
||||
}).map(collection => this.toDTO(collection)));
|
||||
}
|
||||
|
||||
async destroy(id: string): Promise<Collection | null> {
|
||||
|
@ -166,6 +166,19 @@ describe('CollectionsService', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('latest collection', function () {
|
||||
it('Includes all posts when fetched directly', async function () {
|
||||
await collectionsService.createCollection({
|
||||
title: 'Latest',
|
||||
slug: 'latest',
|
||||
type: 'automatic',
|
||||
filter: ''
|
||||
});
|
||||
const collection = await collectionsService.getBySlug('latest');
|
||||
assert(collection?.posts.length === 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit', function () {
|
||||
it('Can edit existing collection', async function () {
|
||||
const savedCollection = await collectionsService.createCollection({
|
||||
|
@ -10,4 +10,9 @@ export class PostsRepositoryInMemory extends InMemoryRepository<string, Collecti
|
||||
tags: entity.tags.map(tag => tag.slug)
|
||||
};
|
||||
}
|
||||
|
||||
async getAllIds() {
|
||||
const posts = await this.getAll();
|
||||
return posts.map(post => post.id);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ class PostsRepository {
|
||||
this.moment = moment;
|
||||
}
|
||||
|
||||
async getAllIds() {
|
||||
const rows = await this.models.Post.query().select('id').where('type', 'post');
|
||||
|
||||
return rows.map(row => row.id);
|
||||
}
|
||||
async getAll({filter, transaction}) {
|
||||
const {data: models} = await this.models.Post.findPage({
|
||||
filter: `(${filter})+type:post`,
|
||||
|
@ -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, 12);
|
||||
assert.equal(collectionRelatedQueries.length, 7);
|
||||
}
|
||||
|
||||
await agent
|
||||
|
Loading…
Reference in New Issue
Block a user