فهرست منبع

feat(elasticsearch-plugin): Custom mappings can return lists & allow additional Product/variant relation hydration

Closes #1054, closes #1141
Artem Danilov 4 سال پیش
والد
کامیت
ee4709520b

+ 79 - 68
packages/elasticsearch-plugin/src/indexer.controller.ts

@@ -442,8 +442,13 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
 
 
         for (const productId of productIds) {
         for (const productId of productIds) {
             operations.push(...(await this.deleteProductOperations(productId)));
             operations.push(...(await this.deleteProductOperations(productId)));
+            const optionsProductRelations = this.options.additionalProductRelationsToFetchFromDB ?
+                this.options.additionalProductRelationsToFetchFromDB: [];
+            const optionsVariantRelations = this.options.additionalVariantRelationsToFetchFromDB ?
+                this.options.additionalVariantRelationsToFetchFromDB: [];
+
             const product = await this.connection.getRepository(Product).findOne(productId, {
             const product = await this.connection.getRepository(Product).findOne(productId, {
-                relations: productRelations,
+                relations: [...productRelations,...optionsProductRelations],
                 where: {
                 where: {
                     deletedAt: null,
                     deletedAt: null,
                 },
                 },
@@ -452,7 +457,7 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
                 const updatedProductVariants = await this.connection.getRepository(ProductVariant).findByIds(
                 const updatedProductVariants = await this.connection.getRepository(ProductVariant).findByIds(
                     product.variants.map(v => v.id),
                     product.variants.map(v => v.id),
                     {
                     {
-                        relations: variantRelations,
+                        relations: [...variantRelations,...optionsVariantRelations],
                         where: {
                         where: {
                             deletedAt: null,
                             deletedAt: null,
                         },
                         },
@@ -692,75 +697,81 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
         ctx: RequestContext,
         ctx: RequestContext,
         languageCode: LanguageCode,
         languageCode: LanguageCode,
     ): Promise<VariantIndexItem> {
     ): Promise<VariantIndexItem> {
-        const productAsset = v.product.featuredAsset;
-        const variantAsset = v.featuredAsset;
-        const productTranslation = this.getTranslation(v.product, languageCode);
-        const variantTranslation = this.getTranslation(v, languageCode);
-        const collectionTranslations = v.collections.map(c => this.getTranslation(c, languageCode));
-
-        const productCollectionTranslations = variants.reduce(
-            (translations, variant) => [
-                ...translations,
-                ...variant.collections.map(c => this.getTranslation(c, languageCode)),
-            ],
-            [] as Array<Translation<Collection>>,
-        );
-        const prices = variants.map(variant => variant.price);
-        const pricesWithTax = variants.map(variant => variant.priceWithTax);
-
-        const item: VariantIndexItem = {
-            channelId: ctx.channelId,
-            languageCode,
-            productVariantId: v.id,
-            sku: v.sku,
-            slug: productTranslation.slug,
-            productId: v.product.id,
-            productName: productTranslation.name,
-            productAssetId: productAsset ? productAsset.id : undefined,
-            productPreview: productAsset ? productAsset.preview : '',
-            productPreviewFocalPoint: productAsset ? productAsset.focalPoint || undefined : undefined,
-            productVariantName: variantTranslation.name,
-            productVariantAssetId: variantAsset ? variantAsset.id : undefined,
-            productVariantPreview: variantAsset ? variantAsset.preview : '',
-            productVariantPreviewFocalPoint: variantAsset ? variantAsset.focalPoint || undefined : undefined,
-            price: v.price,
-            priceWithTax: v.priceWithTax,
-            currencyCode: v.currencyCode,
-            description: productTranslation.description,
-            facetIds: this.getFacetIds([v]),
-            channelIds: v.channels.map(c => c.id),
-            facetValueIds: this.getFacetValueIds([v]),
-            collectionIds: v.collections.map(c => c.id.toString()),
-            collectionSlugs: collectionTranslations.map(c => c.slug),
-            enabled: v.enabled && v.product.enabled,
-            productEnabled: variants.some(variant => variant.enabled) && v.product.enabled,
-            productPriceMin: Math.min(...prices),
-            productPriceMax: Math.max(...prices),
-            productPriceWithTaxMin: Math.min(...pricesWithTax),
-            productPriceWithTaxMax: Math.max(...pricesWithTax),
-            productFacetIds: this.getFacetIds(variants),
-            productFacetValueIds: this.getFacetValueIds(variants),
-            productCollectionIds: unique(
-                variants.reduce(
-                    (ids, variant) => [...ids, ...variant.collections.map(c => c.id)],
-                    [] as ID[],
+        try {
+            const productAsset = v.product.featuredAsset;
+            const variantAsset = v.featuredAsset;
+            const productTranslation = this.getTranslation(v.product, languageCode);
+            const variantTranslation = this.getTranslation(v, languageCode);
+            const collectionTranslations = v.collections.map(c => this.getTranslation(c, languageCode));
+
+            const productCollectionTranslations = variants.reduce(
+                (translations, variant) => [
+                    ...translations,
+                    ...variant.collections.map(c => this.getTranslation(c, languageCode)),
+                ],
+                [] as Array<Translation<Collection>>,
+            );
+            const prices = variants.map(variant => variant.price);
+            const pricesWithTax = variants.map(variant => variant.priceWithTax);
+
+            const item: VariantIndexItem = {
+                channelId: ctx.channelId,
+                languageCode,
+                productVariantId: v.id,
+                sku: v.sku,
+                slug: productTranslation.slug,
+                productId: v.product.id,
+                productName: productTranslation.name,
+                productAssetId: productAsset ? productAsset.id : undefined,
+                productPreview: productAsset ? productAsset.preview : '',
+                productPreviewFocalPoint: productAsset ? productAsset.focalPoint || undefined : undefined,
+                productVariantName: variantTranslation.name,
+                productVariantAssetId: variantAsset ? variantAsset.id : undefined,
+                productVariantPreview: variantAsset ? variantAsset.preview : '',
+                productVariantPreviewFocalPoint: variantAsset ? variantAsset.focalPoint || undefined : undefined,
+                price: v.price,
+                priceWithTax: v.priceWithTax,
+                currencyCode: v.currencyCode,
+                description: productTranslation.description,
+                facetIds: this.getFacetIds([v]),
+                channelIds: v.channels.map(c => c.id),
+                facetValueIds: this.getFacetValueIds([v]),
+                collectionIds: v.collections.map(c => c.id.toString()),
+                collectionSlugs: collectionTranslations.map(c => c.slug),
+                enabled: v.enabled && v.product.enabled,
+                productEnabled: variants.some(variant => variant.enabled) && v.product.enabled,
+                productPriceMin: Math.min(...prices),
+                productPriceMax: Math.max(...prices),
+                productPriceWithTaxMin: Math.min(...pricesWithTax),
+                productPriceWithTaxMax: Math.max(...pricesWithTax),
+                productFacetIds: this.getFacetIds(variants),
+                productFacetValueIds: this.getFacetValueIds(variants),
+                productCollectionIds: unique(
+                    variants.reduce(
+                        (ids, variant) => [...ids, ...variant.collections.map(c => c.id)],
+                        [] as ID[],
+                    ),
                 ),
                 ),
-            ),
-            productCollectionSlugs: unique(productCollectionTranslations.map(c => c.slug)),
-            productChannelIds: v.product.channels.map(c => c.id),
-            inStock: 0 < (await this.productVariantService.getSaleableStockLevel(ctx, v)),
-            productInStock: await this.getProductInStockValue(ctx, variants),
-        };
-        const variantCustomMappings = Object.entries(this.options.customProductVariantMappings);
-        for (const [name, def] of variantCustomMappings) {
-            item[`variant-${name}`] = def.valueFn(v, languageCode);
-        }
+                productCollectionSlugs: unique(productCollectionTranslations.map(c => c.slug)),
+                productChannelIds: v.product.channels.map(c => c.id),
+                inStock: 0 < (await this.productVariantService.getSaleableStockLevel(ctx, v)),
+                productInStock: await this.getProductInStockValue(ctx, variants),
+            };
+            const variantCustomMappings = Object.entries(this.options.customProductVariantMappings);
+            for (const [name, def] of variantCustomMappings) {
+                item[`variant-${name}`] = def.valueFn(v, languageCode);
+            }
 
 
-        const productCustomMappings = Object.entries(this.options.customProductMappings);
-        for (const [name, def] of productCustomMappings) {
-            item[`product-${name}`] = def.valueFn(v.product, variants, languageCode);
+            const productCustomMappings = Object.entries(this.options.customProductMappings);
+            for (const [name, def] of productCustomMappings) {
+                item[`product-${name}`] = def.valueFn(v.product, variants, languageCode);
+            }
+            return item;
+        }
+        catch (err) {
+            Logger.error(err.toString());
+            throw Error(`Error while reindexing!`);
         }
         }
-        return item;
     }
     }
 
 
     private async getProductInStockValue(ctx: RequestContext, variants: ProductVariant[]): Promise<boolean> {
     private async getProductInStockValue(ctx: RequestContext, variants: ProductVariant[]): Promise<boolean> {

+ 12 - 0
packages/elasticsearch-plugin/src/options.ts

@@ -207,6 +207,16 @@ export interface ElasticsearchOptions {
     };
     };
     // TODO: docs
     // TODO: docs
     bufferUpdates?: boolean;
     bufferUpdates?: boolean;
+    /**
+     * @description
+     * Additional product relations that will be fetched from DB while reindexing.
+     */
+    additionalProductRelationsToFetchFromDB?: [string]|[];
+    /**
+     * @description
+     * Additional variant relations that will be fetched from DB while reindexing.
+     */
+    additionalVariantRelationsToFetchFromDB?: [string]|[];
 }
 }
 
 
 /**
 /**
@@ -421,6 +431,8 @@ export const defaultOptions: ElasticsearchRuntimeOptions = {
     customProductMappings: {},
     customProductMappings: {},
     customProductVariantMappings: {},
     customProductVariantMappings: {},
     bufferUpdates: false,
     bufferUpdates: false,
+    additionalProductRelationsToFetchFromDB:[],
+    additionalVariantRelationsToFetchFromDB:[],
 };
 };
 
 
 export function mergeWithDefaults(userOptions: ElasticsearchOptions): ElasticsearchRuntimeOptions {
 export function mergeWithDefaults(userOptions: ElasticsearchOptions): ElasticsearchRuntimeOptions {

+ 39 - 29
packages/elasticsearch-plugin/src/types.ts

@@ -46,30 +46,28 @@ export type IndexItemAssets = {
     productVariantPreviewFocalPoint: Coordinate | undefined;
     productVariantPreviewFocalPoint: Coordinate | undefined;
 };
 };
 
 
-export type VariantIndexItem = Omit<
-    SearchResult,
-    'score' | 'price' | 'priceWithTax' | 'productAsset' | 'productVariantAsset'
-> &
+export type VariantIndexItem = Omit<SearchResult,
+    'score' | 'price' | 'priceWithTax' | 'productAsset' | 'productVariantAsset'> &
     IndexItemAssets & {
     IndexItemAssets & {
-        channelId: ID;
-        languageCode: LanguageCode;
-        price: number;
-        priceWithTax: number;
-        collectionSlugs: string[];
-        productEnabled: boolean;
-        productPriceMin: number;
-        productPriceMax: number;
-        productPriceWithTaxMin: number;
-        productPriceWithTaxMax: number;
-        productFacetIds: ID[];
-        productFacetValueIds: ID[];
-        productCollectionIds: ID[];
-        productCollectionSlugs: string[];
-        productChannelIds: ID[];
-        [customMapping: string]: any;
-        inStock: boolean;
-        productInStock: boolean;
-    };
+    channelId: ID;
+    languageCode: LanguageCode;
+    price: number;
+    priceWithTax: number;
+    collectionSlugs: string[];
+    productEnabled: boolean;
+    productPriceMin: number;
+    productPriceMax: number;
+    productPriceWithTaxMin: number;
+    productPriceWithTaxMax: number;
+    productFacetIds: ID[];
+    productFacetValueIds: ID[];
+    productCollectionIds: ID[];
+    productCollectionSlugs: string[];
+    productChannelIds: ID[];
+    [customMapping: string]: any;
+    inStock: boolean;
+    productInStock: boolean;
+};
 
 
 export type ProductIndexItem = IndexItemAssets & {
 export type ProductIndexItem = IndexItemAssets & {
     sku: string;
     sku: string;
@@ -245,24 +243,36 @@ export type UpdateIndexQueueJobData =
     | RemoveVariantFromChannelJobData;
     | RemoveVariantFromChannelJobData;
 
 
 type CustomStringMapping<Args extends any[]> = CustomMappingDefinition<Args, 'String!', string>;
 type CustomStringMapping<Args extends any[]> = CustomMappingDefinition<Args, 'String!', string>;
+type CustomStringMappingList<Args extends any[]> = CustomMappingDefinition<Args, '[String!]', string[]>;
 type CustomStringMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'String', Maybe<string>>;
 type CustomStringMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'String', Maybe<string>>;
+type CustomStringMappingNullableList<Args extends any[]> = CustomMappingDefinition<Args, '[String]', Array<Maybe<string>>>;
 type CustomIntMapping<Args extends any[]> = CustomMappingDefinition<Args, 'Int!', number>;
 type CustomIntMapping<Args extends any[]> = CustomMappingDefinition<Args, 'Int!', number>;
+type CustomIntMappingList<Args extends any[]> = CustomMappingDefinition<Args, '[Int!]', number[]>;
 type CustomIntMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'Int', Maybe<number>>;
 type CustomIntMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'Int', Maybe<number>>;
+type CustomIntMappingNullableList<Args extends any[]> = CustomMappingDefinition<Args, '[Int]', Array<Maybe<number>>>;
 type CustomFloatMapping<Args extends any[]> = CustomMappingDefinition<Args, 'Float!', number>;
 type CustomFloatMapping<Args extends any[]> = CustomMappingDefinition<Args, 'Float!', number>;
+type CustomFloatMappingList<Args extends any[]> = CustomMappingDefinition<Args, '[Float!]', number[]>;
 type CustomFloatMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'Float', Maybe<number>>;
 type CustomFloatMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'Float', Maybe<number>>;
+type CustomFloatMappingNullableList<Args extends any[]> = CustomMappingDefinition<Args, '[Float]', Array<Maybe<number>>>;
 type CustomBooleanMapping<Args extends any[]> = CustomMappingDefinition<Args, 'Boolean!', boolean>;
 type CustomBooleanMapping<Args extends any[]> = CustomMappingDefinition<Args, 'Boolean!', boolean>;
-type CustomBooleanMappingNullable<Args extends any[]> = CustomMappingDefinition<
-    Args,
-    'Boolean',
-    Maybe<boolean>
->;
+type CustomBooleanMappingList<Args extends any[]> = CustomMappingDefinition<Args, '[Boolean!]', boolean[]>;
+type CustomBooleanMappingNullable<Args extends any[]> = CustomMappingDefinition<Args, 'Boolean', Maybe<boolean>>;
+type CustomBooleanMappingNullableList<Args extends any[]> = CustomMappingDefinition<Args, '[Boolean]', Array<Maybe<boolean>>>;
 
 
 export type CustomMapping<Args extends any[]> =
 export type CustomMapping<Args extends any[]> =
     | CustomStringMapping<Args>
     | CustomStringMapping<Args>
+    | CustomStringMappingList<Args>
     | CustomStringMappingNullable<Args>
     | CustomStringMappingNullable<Args>
+    | CustomStringMappingNullableList<Args>
     | CustomIntMapping<Args>
     | CustomIntMapping<Args>
+    | CustomIntMappingList<Args>
     | CustomIntMappingNullable<Args>
     | CustomIntMappingNullable<Args>
+    | CustomIntMappingNullableList<Args>
     | CustomFloatMapping<Args>
     | CustomFloatMapping<Args>
+    | CustomFloatMappingList<Args>
     | CustomFloatMappingNullable<Args>
     | CustomFloatMappingNullable<Args>
+    | CustomFloatMappingNullableList<Args>
     | CustomBooleanMapping<Args>
     | CustomBooleanMapping<Args>
-    | CustomBooleanMappingNullable<Args>;
+    | CustomBooleanMappingList<Args>
+    | CustomBooleanMappingNullable<Args>
+    | CustomBooleanMappingNullableList<Args>;