Skip to content

Commit

Permalink
fix(core): Fix only_full_group_by issues in MySQL search
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley committed Sep 1, 2020
1 parent 2ccf9d5 commit 188cfaa
Showing 1 changed file with 27 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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')
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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(
Expand Down Expand Up @@ -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}`;
Expand Down

0 comments on commit 188cfaa

Please sign in to comment.