Skip to content

Commit

Permalink
refactor(core): Refactor collection updates based on product data cha…
Browse files Browse the repository at this point in the history
…nges
  • Loading branch information
michaelbromley committed Feb 14, 2025
1 parent f81a908 commit 07a9254
Showing 1 changed file with 67 additions and 48 deletions.
115 changes: 67 additions & 48 deletions packages/core/src/service/services/collection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ROOT_COLLECTION_NAME } from '@vendure/common/lib/shared-constants';
import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
import { unique } from '@vendure/common/lib/unique';
import { merge } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { debounceTime, filter } from 'rxjs/operators';
import { In, IsNull } from 'typeorm';

import { RequestContext } from '../../api/common/request-context';
Expand Down Expand Up @@ -77,6 +77,7 @@ export type ApplyCollectionFiltersJobData = {
export class CollectionService implements OnModuleInit {
private rootCollection: Translated<Collection> | undefined;
private applyFiltersQueue: JobQueue<ApplyCollectionFiltersJobData>;
private applyAllFiltersOnProductUpdates = true;

constructor(
private connection: TransactionalConnection,
Expand All @@ -103,21 +104,22 @@ export class CollectionService implements OnModuleInit {
const variantEvents$ = this.eventBus.ofType(ProductVariantEvent);

merge(productEvents$, variantEvents$)
.pipe(debounceTime(50))
.pipe(
filter(() => {
if (!this.applyAllFiltersOnProductUpdates) {
Logger.debug(
`Detected product data change, but skipping applyCollectionFilters because applyAllFiltersOnProductUpdates = false`,
);
return false;
} else {
return true;
}
}),
debounceTime(50),
)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
.subscribe(async event => {
await this.applyFiltersQueue.add(
{
ctx: {
channelToken: event.ctx.channel.token,
languageCode: event.ctx.languageCode,
},
// Passing an empty array means that all collections will be updated
collectionIds: [],
applyToChangedVariantsOnly: true,
},
{ ctx: event.ctx },
);
await this.triggerApplyFiltersJob(event.ctx);
});

this.applyFiltersQueue = await this.jobQueueService.createQueue({
Expand Down Expand Up @@ -181,6 +183,7 @@ export class CollectionService implements OnModuleInit {
}
}
}
return { processedCollections: completed };
},
});
}
Expand Down Expand Up @@ -497,16 +500,9 @@ export class CollectionService implements OnModuleInit {
input,
collection,
);
await this.applyFiltersQueue.add(
{
ctx: {
languageCode: ctx.languageCode,
channelToken: ctx.channel.token,
},
collectionIds: [collection.id],
},
{ ctx },
);
await this.triggerApplyFiltersJob(ctx, {
collectionIds: [collection.id],
});
await this.eventBus.publish(new CollectionEvent(ctx, collectionWithRelations, 'created', input));
return assertFound(this.findOne(ctx, collection.id));
}
Expand All @@ -528,17 +524,10 @@ export class CollectionService implements OnModuleInit {
});
await this.customFieldRelationService.updateRelations(ctx, Collection, input, collection);
if (input.filters) {
await this.applyFiltersQueue.add(
{
ctx: {
languageCode: ctx.languageCode,
channelToken: ctx.channel.token,
},
collectionIds: [collection.id],
applyToChangedVariantsOnly: false,
},
{ ctx },
);
await this.triggerApplyFiltersJob(ctx, {
collectionIds: [collection.id],
applyToChangedVariantsOnly: false,
});
} else {
const affectedVariantIds = await this.getCollectionProductVariantIds(collection);
await this.eventBus.publish(new CollectionModificationEvent(ctx, collection, affectedVariantIds));
Expand Down Expand Up @@ -609,17 +598,55 @@ export class CollectionService implements OnModuleInit {
siblings = moveToIndex(input.index, target, siblings);

await this.connection.getRepository(ctx, Collection).save(siblings);
await this.triggerApplyFiltersJob(ctx, {
collectionIds: [target.id],
});
return assertFound(this.findOne(ctx, input.collectionId));
}

/**
* @description
* By default, whenever product data is updated (as determined by subscribing to the
* {@link ProductEvent} and {@link ProductVariantEvent} events), the CollectionFilters are re-applied
* to all Collections.
*
* In certain scenarios, such as when a large number of products are updated at once due to
* bulk data import, this can be inefficient. In such cases, you can disable this behaviour
* for the duration of the import process by calling this method with `false`, and then
* re-enable it by calling with `true`.
*
* Afterward, you can call the `triggerApplyFiltersJob` method to manually re-apply the filters.
*
* @since 3.1.3
*/
setApplyAllFiltersOnProductUpdates(applyAllFiltersOnProductUpdates: boolean) {
this.applyAllFiltersOnProductUpdates = applyAllFiltersOnProductUpdates;
}

/**
* @description
* Triggers the creation of an `apply-collection-filters` job which will cause the contents
* of the specified collections to be re-evaluated against their filters.
*
* If no `collectionIds` option is passed, then all collections will be re-evaluated.
*
* @since 3.1.3
*/
async triggerApplyFiltersJob(
ctx: RequestContext,
options?: { collectionIds?: ID[]; applyToChangedVariantsOnly?: boolean },
) {
await this.applyFiltersQueue.add(
{
ctx: {
languageCode: ctx.languageCode,
channelToken: ctx.channel.token,
},
collectionIds: [target.id],
applyToChangedVariantsOnly: options?.applyToChangedVariantsOnly,
collectionIds: options?.collectionIds ?? [],
},
{ ctx },
);
return assertFound(this.findOne(ctx, input.collectionId));
}

private getCollectionFiltersFromInput(
Expand Down Expand Up @@ -902,17 +929,9 @@ export class CollectionService implements OnModuleInit {
([] as ID[]).concat(...collectionsToAssign.map(c => c.assets.map(a => a.assetId))),
);
await this.assetService.assignToChannel(ctx, { channelId: input.channelId, assetIds });

await this.applyFiltersQueue.add(
{
ctx: {
languageCode: ctx.languageCode,
channelToken: ctx.channel.token,
},
collectionIds: collectionsToAssign.map(collection => collection.id),
},
{ ctx },
);
await this.triggerApplyFiltersJob(ctx, {
collectionIds: collectionsToAssign.map(collection => collection.id),
});

return this.connection
.findByIdsInChannel(
Expand Down

0 comments on commit 07a9254

Please sign in to comment.