Skip to content

Commit

Permalink
fix(core): Fix MySQL error in DefaultSearchPlugin
Browse files Browse the repository at this point in the history
Fixes #331, Fixes #342
  • Loading branch information
michaelbromley committed May 18, 2020
1 parent ca6c30f commit 9eb39a2
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand All @@ -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)')
Expand All @@ -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(', ');
}
}
Original file line number Diff line number Diff line change
@@ -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';

/**
Expand Down Expand Up @@ -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> {
Expand All @@ -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(
Expand Down Expand Up @@ -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)')
Expand Down Expand Up @@ -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') {
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
];

0 comments on commit 9eb39a2

Please sign in to comment.