Quellcode durchsuchen

fix(core): Fix only_full_group_by issues in MySQL search

Michael Bromley vor 5 Jahren
Ursprung
Commit
188cfaa4f3

+ 27 - 21
packages/core/src/plugin/default-search-plugin/search-strategy/mysql-search-strategy.ts

@@ -26,7 +26,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
         const facetValuesQb = this.connection
             .getRepository(SearchIndexItem)
             .createQueryBuilder('si')
-            .select(['productId', 'productVariantId'])
+            .select(['MIN(productId)', 'MIN(productVariantId)'])
             .addSelect('GROUP_CONCAT(facetValueIds)', 'facetValues');
 
         this.applyTermAndFilters(ctx, facetValuesQb, input);
@@ -51,7 +51,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
         const qb = this.connection
             .getRepository(SearchIndexItem)
             .createQueryBuilder('si')
-            .select(this.createMysqlsSelect(!!input.groupByProduct));
+            .select(this.createMysqlSelect(!!input.groupByProduct));
         if (input.groupByProduct) {
             qb.addSelect('MIN(price)', 'minPrice')
                 .addSelect('MAX(price)', 'maxPrice')
@@ -61,10 +61,10 @@ export class MysqlSearchStrategy implements SearchStrategy {
         this.applyTermAndFilters(ctx, qb, input);
         if (sort) {
             if (sort.name) {
-                qb.addOrderBy('productName', sort.name);
+                qb.addOrderBy(input.groupByProduct ? 'MIN(productName)' : 'productName', sort.name);
             }
             if (sort.price) {
-                qb.addOrderBy('price', sort.price);
+                qb.addOrderBy(input.groupByProduct ? 'MIN(price)' : 'price', sort.price);
             }
         } else {
             if (input.term && input.term.length > this.minTermLength) {
@@ -88,7 +88,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
             this.connection
                 .getRepository(SearchIndexItem)
                 .createQueryBuilder('si')
-                .select(this.createMysqlsSelect(!!input.groupByProduct)),
+                .select(this.createMysqlSelect(!!input.groupByProduct)),
             input,
         );
         if (enabledOnly) {
@@ -110,26 +110,32 @@ export class MysqlSearchStrategy implements SearchStrategy {
     ): SelectQueryBuilder<SearchIndexItem> {
         const { term, facetValueIds, facetValueOperator, collectionId, collectionSlug } = input;
 
-        qb.where('1 = 1');
         if (term && term.length > this.minTermLength) {
-            qb.addSelect(`IF (sku LIKE :like_term, 10, 0)`, 'sku_score')
+            const termScoreQuery = this.connection
+                .getRepository(SearchIndexItem)
+                .createQueryBuilder('si_inner')
+                .select('si_inner.productId', 'inner_productId')
+                .addSelect('si_inner.productVariantId', 'inner_productVariantId')
+                .addSelect(`IF (sku LIKE :like_term, 10, 0)`, 'sku_score')
                 .addSelect(
-                    `
-                            (SELECT sku_score) +
-                            MATCH (productName) AGAINST (:term) * 2 +
-                            MATCH (productVariantName) AGAINST (:term) * 1.5 +
-                            MATCH (description) AGAINST (:term)* 1`,
+                    `(SELECT sku_score) +
+                     MATCH (productName) AGAINST (:term) * 2 +
+                     MATCH (productVariantName) AGAINST (:term) * 1.5 +
+                     MATCH (description) AGAINST (:term)* 1`,
                     'score',
                 )
-                .andWhere(
-                    new Brackets(qb1 => {
-                        qb1.where('sku LIKE :like_term')
-                            .orWhere('MATCH (productName) AGAINST (:term)')
-                            .orWhere('MATCH (productVariantName) AGAINST (:term)')
-                            .orWhere('MATCH (description) AGAINST (:term)');
-                    }),
-                )
+                .where('sku LIKE :like_term')
+                .orWhere('MATCH (productName) AGAINST (:term)')
+                .orWhere('MATCH (productVariantName) AGAINST (:term)')
+                .orWhere('MATCH (description) AGAINST (:term)')
                 .setParameters({ term, like_term: `%${term}%` });
+
+            qb.innerJoin(`(${termScoreQuery.getQuery()})`, 'term_result', 'inner_productId = si.productId')
+                .addSelect(input.groupByProduct ? 'MAX(term_result.score)' : 'term_result.score', 'score')
+                .andWhere('term_result.inner_productVariantId = si.productVariantId')
+                .setParameters(termScoreQuery.getParameters());
+        } else {
+            qb.addSelect('1 as score');
         }
         if (facetValueIds?.length) {
             qb.andWhere(
@@ -166,7 +172,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
      * 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 {
+    private createMysqlSelect(groupByProduct: boolean): string {
         return fieldsToSelect
             .map(col => {
                 const qualifiedName = `si.${col}`;