I have this endpoint, it's the initial endpoint when a customer is visiting the eshop:(我有这个端点,这是客户访问eshop时的初始端点:)
export const getAllProductsByCategory = async (req, res, next) => {
const pageSize = parseInt(req.query.pageSize);
const sort = parseInt(req.query.sort);
const skip = parseInt(req.query.skip);
const { order, filters } = req.query;
const { brands, tags, pricesRange } = JSON.parse(filters);
try {
const aggregate = Product.aggregate();
aggregate.lookup({
from: 'categories',
localField: 'categories',
foreignField: '_id',
as: 'categories'
});
aggregate.match({
productType: 'product',
available: true,
categories: {
$elemMatch: {
url: req.params
}
}
});
aggregate.lookup({
from: 'tags',
let: { tags: '$tags' },
pipeline: [
{
$match: {
$expr: { $in: ['$_id', '$$tags'] }
}
},
{
$project: {
_id: 1,
name: 1,
slug: 1
}
}
],
as: 'tags'
});
aggregate.lookup({
from: 'brands',
let: { brand: '$brand' },
pipeline: [
{
$match: {
$expr: { $eq: ['$_id', '$$brand'] }
}
},
{
$project: {
_id: 1,
name: 1,
slug: 1
}
}
],
as: 'brand'
});
if (brands.length > 0) {
const filterBrands = brands.map((_id) => utils.toObjectId(_id));
aggregate.match({
$and: [{ brand: { $elemMatch: { _id: { $in: filterBrands } } } }]
});
}
if (tags.length > 0) {
const filterTags = tags.map((_id) => utils.toObjectId(_id));
aggregate.match({ tags: { $elemMatch: { _id: { $in: filterTags } } } });
}
if (pricesRange.length > 0 && pricesRange !== 'all') {
const filterPriceRange = pricesRange.map((_id) => utils.toObjectId(_id));
aggregate.match({
_id: { $in: filterPriceRange }
});
}
aggregate.facet({
tags: [
{ $unwind: { path: '$tags' } },
{ $group: { _id: '$tags', tag: { $first: '$tags' }, total: { $sum: 1 } } },
{
$group: {
_id: '$tag._id',
name: { $addToSet: '$tag.name' },
total: { $addToSet: '$total' }
}
},
{
$project: {
name: { $arrayElemAt: ['$name', 0] },
total: { $arrayElemAt: ['$total', 0] },
_id: 1
}
},
{ $sort: { total: -1 } }
],
brands: [
{ $unwind: { path: '$brand' } },
{
$group: {
_id: '$brand._id',
name: { $first: '$brand.name' },
slug: { $first: '$brand.slug' },
total: {
$sum: 1
}
}
},
{ $sort: { name: 1 } }
],
pricesRange: [
{
$bucket: {
groupBy: {
$cond: {
if: { $ne: ['$onSale.value', true] },
then: '$price',
else: '$sale.salePrice'
}
},
boundaries: [0, 20.01, 50.01],
default: 'other',
output: {
count: { $sum: 1 },
products: { $push: '$_id' }
}
}
}
],
products: [
{ $skip: (skip - 1) * pageSize },
{ $limit: pageSize },
{
$project: {
_id: 1,
images: 1,
onSale: 1,
price: 1,
quantity: 1,
slug: 1,
sale: 1,
sku: 1,
status: 1,
title: 1,
brand: 1,
tags: 1,
description: 1
}
},
{ $sort: { [order]: sort } }
],
total: [
{
$group: {
_id: null,
count: { $sum: 1 }
}
},
{
$project: {
count: 1,
_id: 0
}
}
]
});
aggregate.addFields({
total: {
$arrayElemAt: ['$total', 0]
}
});
const [response] = await aggregate.exec();
if (!response.total) {
response.total = 0;
}
res.status(httpStatus.OK);
return res.json(response);
} catch (error) {
console.log(error);
return next(error);
}
};
If no filters are applied all products matches the category requested with no problem.(如果未应用任何过滤器,则所有产品都将与所请求的类别完全匹配。)
My issue is when a customer selects a brand or tag, then the facet returns the products, but returns only one brand/tag (as it should be since the products filtered have only this brand).(我的问题是,当客户选择一个品牌或标签时,该构面退回产品,但仅返回一个品牌/标签(这应该是因为过滤的产品仅具有该品牌)。)
What I must do in order to retain all brands/tags and let the user select more than one brand/tag?(为了保留所有品牌/标签并让用户选择多个品牌/标签,我必须做什么?) If customer selects a brand, then the tags should match the returned products tags and vice versa.(如果客户选择品牌,则标签应与返回的产品标签匹配,反之亦然。)
Is there a better way to implement tags
stage in $facet
since tags is an array and the desired output is: [{_id: 123, name: {label: 'test', value: 123]}]
(是否有更好的方法可以在$facet
实现tags
阶段,因为标签是一个数组,并且期望的输出是: [{_id: 123, name: {label: 'test', value: 123]}]
)
The request is like:(1,2,3,4 represents _id)(请求就像:(1,2,3,4代表_id))
http://locahost:3000/get-products/?filters={brands: [1, 2], tags: [3,4], pricesRange:[]}
Update(更新资料)
This is the products
schema with tags
and brands
:(这是带有tags
和brands
的products
架构:)
brand: {
ref: 'Brand',
type: Schema.Types.ObjectId
},
tags: [
{
ref: 'Tags',
type: Schema.Types.ObjectId
}
]
tags
schema:(tags
架构:)
{
metaDescription: {
type: String
},
metaTitle: {
type: String
},
name: {
label: {
type: String,
index: true
},
value: {
type: Schema.Types.ObjectId
},
},
slug: {
type: String,
index: true
},
status: {
label: {
type: String
},
value: {
default: true,
type: Boolean
}
}
}
brands
schema:(brands
架构:)
description: {
default: '',
type: String
},
name: {
required: true,
type: String,
unique: true
},
slug: {
type: String,
index: true
},
status: {
label: {
default: 'Active',
type: String
},
value: {
default: true,
type: Boolean
}
}
Scenario:(场景:)
User visits store, selects a category and all matching products should return with matched brands
, tags
, priceRange
& pagination.(用户访问商店,选择一个类别,所有匹配的产品应返回匹配的brands
, tags
, priceRange
和分页。)
Case 1:(情况1:)
User clicks a brand
from checkbox, then the request returns matching products
, tags
& priceRanges
and all brands of the selected category, not of matched products(用户单击复选框中的brand
,然后请求返回匹配的products
, tags
和priceRanges
以及所选类别的所有品牌,而不是匹配的产品)
Case 2:(情况2:)
User selects a brand
like Case 1, but then decides to check a tag
too, then the request should return all brands
and tags
again, but products
matched against them.(用户选择案例1之类的brand
,但随后也决定检查tag
,然后请求应再次返回所有brands
和tags
,但products
与之匹配。)
Case 3:(情况3:)
User do not select brand
but selects a tag
only, the request should return all matching products
that have that tag/tags and return the brands
that matched the products returned.(用户不选择brand
而是仅选择tag
,请求应返回所有具有该标签的匹配products
,并返回与返回brands
匹配的brands
。)
Case 4:(情况4:)
Same as case 3, but user selects a brand
after selecting a tag/tags
, the request should return matching products
, brands
& tags
.(与情况3相同,但是用户在选择tag/tags
后选择了brand
,请求应返回匹配的products
, brands
和tags
。)
In all cases pagination should return proper total, also priceRanges
should match the returned results.(在所有情况下,分页都应返回适当的总数,并且priceRanges
也应与返回的结果匹配。)
I hope it's clear now, I think I've not missed any other case.(我希望现在很清楚,我想我没有错过任何其他情况。) I could probably grey out/disable the tags/brands that do not match the response in the front end but I don't know if this is user friendly.(我可能会将灰色/禁用与前端响应不匹配的标签/品牌,但我不知道这是否对用户友好。)
ask by Stathis Ntonas translate from so
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…