diff --git a/src/datasources/category-api.ts b/src/datasources/category-api.ts index 019b4f5..e5a0b03 100644 --- a/src/datasources/category-api.ts +++ b/src/datasources/category-api.ts @@ -65,6 +65,68 @@ WHERE product_category.product_id = ? return res; } + async getCategoryBasedFacets( + productId: number, + ): Promise<{ room: string[]; color: string[] }> { + ScopeAccess.validate(this.user).some([ + Scopes.CATEGORIES_READ, + Scopes.CATEGORIES_PUBLIC_READ, + ]); + + const ROOMS_PARENT_ID = 270; + const COLORS_PARENT_ID = 1665; + + const { rows } = await this.cachedRaw( + ` + WITH rooms AS ( + SELECT lft, rgt FROM categories WHERE id = ? + ), + colors AS ( + SELECT lft, rgt FROM categories WHERE id = ? + ) + SELECT + array_remove( + array_agg( + DISTINCT CASE + WHEN c.lft > rooms.lft AND c.rgt < rooms.rgt THEN c.name + END + ), + NULL + ) AS room, + array_remove( + array_agg( + DISTINCT CASE + WHEN c.lft > colors.lft AND c.rgt < colors.rgt THEN c.name + END + ), + NULL + ) AS color + FROM product_category pc + JOIN categories c ON c.id = pc.category_id + CROSS JOIN rooms + CROSS JOIN colors + WHERE pc.product_id = ? + AND ( + (c.lft > rooms.lft AND c.rgt < rooms.rgt) + OR + (c.lft > colors.lft AND c.rgt < colors.rgt) + ) + `, + [ROOMS_PARENT_ID, COLORS_PARENT_ID, productId], + ) + // @ts-ignore + .cache(MINUTE * 5); + + const facetRow = rows[0] ?? { room: [], color: [] }; + const roomCategories: string[] = facetRow.room ?? []; + const colorCategories: string[] = facetRow.color ?? []; + + return { + room: roomCategories, + color: colorCategories, + }; + } + async getCategoryById(id: number): Promise { // Dataloaders does not need scopes validation since they // use other functions. diff --git a/src/resolvers/products-resolver.ts b/src/resolvers/products-resolver.ts index 5ac2573..71accbc 100644 --- a/src/resolvers/products-resolver.ts +++ b/src/resolvers/products-resolver.ts @@ -68,11 +68,16 @@ const Product = { const keywords = await (( dataSources.keywordApi )).getProductKeywords(product.id); - const colors = []; + + const categoryBasedFacets = await (( + dataSources.categoryApi + )).getCategoryBasedFacets(product.id); + + const colorsByKeyword = []; const types = []; keywords.forEach((keyword) => { if (keyword.type === 'COLOR') { - colors.push(keyword.value); + colorsByKeyword.push(keyword.value); } else if (keyword.value === 'typeillustration') { types.push('illustration'); } else if (keyword.value === 'typephotography') { @@ -93,13 +98,28 @@ const Product = { ] : [getOrientationString(product.fields)]; + const colors = new Set([ + ...colorsByKeyword, + ...categoryBasedFacets.color.map((c) => { + if (c === 'multi color') { + // Normalize to match keyword format + return 'multicolor'; + } + return c; + }), + ]); + return [ - { attribute: 'color', values: colors }, + { + attribute: 'color', + values: [...colors], + }, { attribute: 'orientation', values: orientations.map((value) => value.toLowerCase()), }, { attribute: 'type', values: types }, + { attribute: 'room', values: categoryBasedFacets.room }, ]; }, };