Sfoglia il codice sorgente

fix(core): Fix MySQL error in DefaultSearchPlugin

Fixes #331, Fixes #342
Michael Bromley 5 anni fa
parent
commit
9eb39a2518

+ 41 - 5
packages/core/src/plugin/default-search-plugin/search-strategy/mysql-search-strategy.ts

@@ -7,6 +7,7 @@ import { RequestContext } from '../../../api/common/request-context';
 import { SearchIndexItem } from '../search-index-item.entity';
 
 import { SearchStrategy } from './search-strategy';
+import { fieldsToSelect } from './search-strategy-common';
 import { createFacetIdCountMap, mapToSearchResult } from './search-strategy-utils';
 
 /**
@@ -47,7 +48,10 @@ export class MysqlSearchStrategy implements SearchStrategy {
         const take = input.take || 25;
         const skip = input.skip || 0;
         const sort = input.sort;
-        const qb = this.connection.getRepository(SearchIndexItem).createQueryBuilder('si');
+        const qb = this.connection
+            .getRepository(SearchIndexItem)
+            .createQueryBuilder('si')
+            .select(this.createMysqlsSelect(!!input.groupByProduct));
         if (input.groupByProduct) {
             qb.addSelect('MIN(price)', 'minPrice')
                 .addSelect('MAX(price)', 'maxPrice')
@@ -75,13 +79,16 @@ export class MysqlSearchStrategy implements SearchStrategy {
             .take(take)
             .skip(skip)
             .getRawMany()
-            .then(res => res.map(r => mapToSearchResult(r, ctx.channel.currencyCode)));
+            .then((res) => res.map((r) => mapToSearchResult(r, ctx.channel.currencyCode)));
     }
 
     async getTotalCount(ctx: RequestContext, input: SearchInput, enabledOnly: boolean): Promise<number> {
         const innerQb = this.applyTermAndFilters(
             ctx,
-            this.connection.getRepository(SearchIndexItem).createQueryBuilder('si'),
+            this.connection
+                .getRepository(SearchIndexItem)
+                .createQueryBuilder('si')
+                .select(this.createMysqlsSelect(!!input.groupByProduct)),
             input,
         );
         if (enabledOnly) {
@@ -93,7 +100,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
             .select('COUNT(*) as total')
             .from(`(${innerQb.getQuery()})`, 'inner')
             .setParameters(innerQb.getParameters());
-        return totalItemsQb.getRawOne().then(res => res.total);
+        return totalItemsQb.getRawOne().then((res) => res.total);
     }
 
     private applyTermAndFilters(
@@ -115,7 +122,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
                     'score',
                 )
                 .andWhere(
-                    new Brackets(qb1 => {
+                    new Brackets((qb1) => {
                         qb1.where('sku LIKE :like_term')
                             .orWhere('MATCH (productName) AGAINST (:term)')
                             .orWhere('MATCH (productVariantName) AGAINST (:term)')
@@ -141,4 +148,33 @@ export class MysqlSearchStrategy implements SearchStrategy {
         }
         return qb;
     }
+    /**
+     * When a select statement includes a GROUP BY clause,
+     * then all selected columns must be aggregated. So we just apply the
+     * "MIN" function in this case to all other columns than the productId.
+     */
+    private createMysqlsSelect(groupByProduct: boolean): string {
+        return fieldsToSelect
+            .map((col) => {
+                const qualifiedName = `si.${col}`;
+                const alias = `si_${col}`;
+                if (groupByProduct && col !== 'productId') {
+                    if (
+                        col === 'facetIds' ||
+                        col === 'facetValueIds' ||
+                        col === 'collectionIds' ||
+                        col === 'channelIds'
+                    ) {
+                        return `GROUP_CONCAT(${qualifiedName}) as "${alias}"`;
+                    } else if (col === 'enabled') {
+                        return `MAX(${qualifiedName}) as "${alias}"`;
+                    } else {
+                        return `MIN(${qualifiedName}) as "${alias}"`;
+                    }
+                } else {
+                    return `${qualifiedName} as "${alias}"`;
+                }
+            })
+            .join(', ');
+    }
 }

+ 6 - 28
packages/core/src/plugin/default-search-plugin/search-strategy/postgres-search-strategy.ts

@@ -1,12 +1,12 @@
 import { SearchInput, SearchResult } from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
-import { unique } from '@vendure/common/lib/unique';
 import { Brackets, Connection, SelectQueryBuilder } from 'typeorm';
 
 import { RequestContext } from '../../../api/common/request-context';
 import { SearchIndexItem } from '../search-index-item.entity';
 
 import { SearchStrategy } from './search-strategy';
+import { fieldsToSelect } from './search-strategy-common';
 import { createFacetIdCountMap, mapToSearchResult } from './search-strategy-utils';
 
 /**
@@ -81,7 +81,7 @@ export class PostgresSearchStrategy implements SearchStrategy {
             .take(take)
             .skip(skip)
             .getRawMany()
-            .then(res => res.map(r => mapToSearchResult(r, ctx.channel.currencyCode)));
+            .then((res) => res.map((r) => mapToSearchResult(r, ctx.channel.currencyCode)));
     }
 
     async getTotalCount(ctx: RequestContext, input: SearchInput, enabledOnly: boolean): Promise<number> {
@@ -101,7 +101,7 @@ export class PostgresSearchStrategy implements SearchStrategy {
             .select('COUNT(*) as total')
             .from(`(${innerQb.getQuery()})`, 'inner')
             .setParameters(innerQb.getParameters());
-        return totalItemsQb.getRawOne().then(res => res.total);
+        return totalItemsQb.getRawOne().then((res) => res.total);
     }
 
     private applyTermAndFilters(
@@ -130,7 +130,7 @@ export class PostgresSearchStrategy implements SearchStrategy {
                 'score',
             )
                 .andWhere(
-                    new Brackets(qb1 => {
+                    new Brackets((qb1) => {
                         qb1.where('to_tsvector(si.sku) @@ to_tsquery(:term)')
                             .orWhere('to_tsvector(si.productName) @@ to_tsquery(:term)')
                             .orWhere('to_tsvector(si.productVariantName) @@ to_tsquery(:term)')
@@ -164,30 +164,8 @@ export class PostgresSearchStrategy implements SearchStrategy {
      * "MIN" function in this case to all other columns than the productId.
      */
     private createPostgresSelect(groupByProduct: boolean): string {
-        return [
-            'sku',
-            'enabled',
-            'slug',
-            'price',
-            'priceWithTax',
-            'productVariantId',
-            'languageCode',
-            'productId',
-            'productName',
-            'productVariantName',
-            'description',
-            'facetIds',
-            'facetValueIds',
-            'collectionIds',
-            'channelIds',
-            'productAssetId',
-            'productPreview',
-            'productPreviewFocalPoint',
-            'productVariantAssetId',
-            'productVariantPreview',
-            'productVariantPreviewFocalPoint',
-        ]
-            .map(col => {
+        return fieldsToSelect
+            .map((col) => {
                 const qualifiedName = `si.${col}`;
                 const alias = `si_${col}`;
                 if (groupByProduct && col !== 'productId') {

+ 23 - 0
packages/core/src/plugin/default-search-plugin/search-strategy/search-strategy-common.ts

@@ -0,0 +1,23 @@
+export const fieldsToSelect = [
+    'sku',
+    'enabled',
+    'slug',
+    'price',
+    'priceWithTax',
+    'productVariantId',
+    'languageCode',
+    'productId',
+    'productName',
+    'productVariantName',
+    'description',
+    'facetIds',
+    'facetValueIds',
+    'collectionIds',
+    'channelIds',
+    'productAssetId',
+    'productPreview',
+    'productPreviewFocalPoint',
+    'productVariantAssetId',
+    'productVariantPreview',
+    'productVariantPreviewFocalPoint',
+];