Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
541 views
in Technique[技术] by (71.8m points)

javascript - Mongo / mongoose $ facet过滤器,如果客户应用了过滤器,则返回所有产品的品牌/标签(Mongo/mongoose $facet filters, return all product's brands/tags in response if customer applied filters)

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 :(这是带有tagsbrandsproducts架构:) 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.(用户访问商店,选择一个类别,所有匹配的产品应返回匹配的brandstagspriceRange和分页。) 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 ,然后请求返回匹配的productstagspriceRanges 以及所选类别的所有品牌,而不是匹配的产品) 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 ,然后请求应再次返回所有brandstags ,但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 ,请求应返回匹配的productsbrandstags 。) 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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could move the all the stages inside each facets sub-pipeline.(您可以移动每个构面子管道内的所有阶段。)

You could dynamically build the facets sub-pipeline based on the filters.(您可以基于过滤器动态构建构面子管道。) Something like(就像是) {$facets:{ tags:[ {$match:{}}, // Match for specific tags and brands {$lookup:{}} // look up for tags //rest of pipelines ], brands:[ {$match:{}}, // Match for specific tags and brands {$lookup:{}} // look up for brands //rest of pipelines ], products:[ // All for products {$lookup:{}}, // look up tags {$lookup:{}}, // look up brands //rest of pipelines ] }}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...