Mongo 数据库中的多面过滤器,用于具有多个选项的产品
Faceted filters in Mongo DB with for products with multiple options
我们正在尝试创建对 MangoDB 的调用以接收所有可能的产品过滤器。
我会尝试创建我们产品的示例
第一个产品是阿迪达斯鞋,它有两种选择 select - 颜色和尺码。但是对于不同的颜色,你有不同的尺寸。
{
id: 1
name: "Adidas Shoes",
filters: [
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "white"
},
{
code: "size",
value: 41
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "white"
},
{
code: "size",
value: 42
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "white"
},
{
code: "size",
value: 43
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "blue"
},
{
code: "size",
value: 41
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "blue"
},
{
code: "size",
value: 44
}
]
]
}
第二个产品是耐克鞋。
{
id: 2
name: "Nike Shoes",
filters: [
[
{
code: "brand",
value: "Nike",
},
{
code: "colour",
value: "white",
},
{
code: "size",
value: 41,
}
],
[
{
code: "brand",
value: "Nike",
},
{
code: "colour",
value: "white",
},
{
code: "size",
value: 42,
}
],
[
{
code: "brand",
value: "Nike",
},
{
code: "colour",
value: "green",
},
{
code: "size",
value: 41,
}
]
]
}
还有 Reebook 鞋
{
id: 3
name: "Reebook Shoes",
filters: [
[
{
code: "brand",
value: "Reebook",
},
{
code: "colour",
value: "black",
},
{
code: "size",
value: 41,
}
]
]
}
如您所见,选项大小取决于颜色,而颜色又取决于大小。
我们如何创建 MongoDb.aggregate 以拥有所有可能的过滤器?
品牌: 阿迪达斯 (1), 耐克 (1), Reebook (1)
尺码: 41 (3), 42 (2), 43 (1), 44 (1)
颜色: 白色 (2)、蓝色 (1)、绿色 (1)、黑色 (1)
调用应该独立于我们有多少个过滤器和哪些过滤器(有一个选项的产品,有更多选项和不同过滤器的产品)。你能解释一下在这种情况下如何使用 $group, $unwind 吗?以及我们以后如何使用 $facet 改进它?
非常感谢!!!
编辑 2017 年 2 月 10 日
响应示例
facets: [
{
code: "brand",
values: [
{
name: "Adidas",
count: 1
},
{
name: "Nike",
count: 1
},
{
name: "Reebook",
count: 1
}
]
},
{
code: "size",
values: [
{
name: 41,
count: 3
},
{
name: 42,
count: 2
},
{
name: 43,
count: 1
},
{
name: 44,
count: 1
}
]
},
{
code: "colour",
values: [
{
name: "White",
count: 2
},
{
name: "Blue",
count: 1
},
{
name: "Green",
count: 1
},
{
name: "Black",
count: 1
}
]
}
]
或
facets: {
"brand": {
"Adidas": 1,
"Nike":1,
"Reebook":1
},
"size": {
"41": 3,
"42":2,
"43":1,
"44":1
},
"colour": {
"White": 2,
"Blue":1,
"Green":1,
"Black":1
}
}
这是我们的第一阶段。下一步将是如何在我 selected 尺寸:41 和颜色:白色时搜索可能的过滤器。
谢谢
这是一个可能适合您的汇总。
db.getCollection('test').aggregate([
{$unwind: '$filters'},
{$unwind: '$filters'},
{
$group: {
_id: {code: '$filters.code', value: '$filters.value'},
products: {$addToSet: '$_id'}
}
},
{
$project: {
'filter.value': '$_id.value',
'filter.count': {$size: '$products'}
}
},
{
$group: {
_id: '$_id.code',
filters: {$push: '$filter'}
}
}
]);
您需要的数据来自 slightly different format,因为没有简单的方法将分组值数组转换为对象属性。
如果一些过滤器已经 selected 你需要在第一个 $unwind 之后的另一个 $match 阶段。
它还支持多个 selects。假设我想要 white/black 由 Reebook/Adidas 制造的鞋子。
db.getCollection('test').aggregate([
{$unwind: '$filters'},
{
$match: {
$and: [
//Add objects here fo everything that is selected already
{'filters': {$elemMatch: {code: 'colour', value: {$in: ['black', 'white']}}}},
{'filters': {$elemMatch: {code: 'brand', value: {$in: ['Adidas', 'Reebook']}}}}
]
}
},
{$unwind: '$filters'},
{
$group: {
_id: {code: '$filters.code', value: '$filters.value'},
products: {$addToSet: '$_id'}
}
},
{
$project: {
'filter.value': '$_id.value',
'filter.count': {$size: '$products'}
}
},
{
$group: {
_id: '$_id.code',
filters: {$push: '$filter'}
}
}
]);
最后一件事是这样的依赖行为:
Select Nike => 尺码和颜色按品牌筛选,但您仍然可以 select 所有品牌。
Select Nike + 42 尺码 => 您可以 select 只有尺码为 42 的品牌、颜色和有 42 尺码鞋子的品牌。
等等。
您可以为此利用 $facet。事实上,当这个想法是下一个。
如果我们正在计算品牌 - 我们应该根据 select 在大小和颜色下拉列表中编辑的内容来过滤记录。
如果我们计算尺寸 - 应用颜色和品牌。颜色的逻辑相同。
这是在 mongo shell 中工作的代码:
//This hash is going to by dynamic
//Add and remove properties, change $in arrays
//Depending on what user does
var conditionsForUserSelect = {
'colour': {'filters': {$elemMatch: {code: 'colour', value: {$in: ['green']}}}},
'brand': {'filters': {$elemMatch: {code: 'brand', value: {$in: ['Nike']}}}},
'size': {'filters': {$elemMatch: {code: 'size', value: {$in: [41]}}}}
};
var getFacetStage = function (code) {
//Empty object, accept all filters if nothing is selected
var matchStageCondition = {};
var selectedFilters = Object.keys(conditionsForUserSelect);
if (selectedFilters && selectedFilters.length) {
//Take all queries EXCEPT for the passed
//E.g. if we are counting brand filters then we should apply color and size.
//Because for example if no size/colour selected we should
//allow all brands even if Reebok is selected
var conditionsToApply = selectedFilters
.filter(function (key) {
return key !== code
})
.map(function (key) {
return conditionsForUserSelect[key]
});
if (conditionsToApply && conditionsToApply.length) {
matchStageCondition = {
$and: conditionsToApply
};
}
}
return [
{$unwind: '$filters'},
{
$match: matchStageCondition
},
{$unwind: '$filters'},
{
$group: {
_id: {code: '$filters.code', value: '$filters.value'},
products: {$addToSet: '$_id'}
}
},
{
$project: {
'filter.value': '$_id.value',
'filter.count': {$size: '$products'}
}
},
{
$group: {
_id: '$_id.code',
filters: {$push: '$filter'}
}
},
{
$match: {_id: code}
}
];
};
db.getCollection('test').aggregate([
{
$facet: {
colour: getFacetStage('colour'),
size: getFacetStage('size'),
brand: getFacetStage('brand')
}
}
]);
我们正在尝试创建对 MangoDB 的调用以接收所有可能的产品过滤器。
我会尝试创建我们产品的示例
第一个产品是阿迪达斯鞋,它有两种选择 select - 颜色和尺码。但是对于不同的颜色,你有不同的尺寸。
{
id: 1
name: "Adidas Shoes",
filters: [
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "white"
},
{
code: "size",
value: 41
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "white"
},
{
code: "size",
value: 42
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "white"
},
{
code: "size",
value: 43
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "blue"
},
{
code: "size",
value: 41
}
],
[
{
code: "brand",
value: "Adidas"
},
{
code: "colour",
value: "blue"
},
{
code: "size",
value: 44
}
]
]
}
第二个产品是耐克鞋。
{
id: 2
name: "Nike Shoes",
filters: [
[
{
code: "brand",
value: "Nike",
},
{
code: "colour",
value: "white",
},
{
code: "size",
value: 41,
}
],
[
{
code: "brand",
value: "Nike",
},
{
code: "colour",
value: "white",
},
{
code: "size",
value: 42,
}
],
[
{
code: "brand",
value: "Nike",
},
{
code: "colour",
value: "green",
},
{
code: "size",
value: 41,
}
]
]
}
还有 Reebook 鞋
{
id: 3
name: "Reebook Shoes",
filters: [
[
{
code: "brand",
value: "Reebook",
},
{
code: "colour",
value: "black",
},
{
code: "size",
value: 41,
}
]
]
}
如您所见,选项大小取决于颜色,而颜色又取决于大小。
我们如何创建 MongoDb.aggregate 以拥有所有可能的过滤器?
品牌: 阿迪达斯 (1), 耐克 (1), Reebook (1)
尺码: 41 (3), 42 (2), 43 (1), 44 (1)
颜色: 白色 (2)、蓝色 (1)、绿色 (1)、黑色 (1)
调用应该独立于我们有多少个过滤器和哪些过滤器(有一个选项的产品,有更多选项和不同过滤器的产品)。你能解释一下在这种情况下如何使用 $group, $unwind 吗?以及我们以后如何使用 $facet 改进它?
非常感谢!!!
编辑 2017 年 2 月 10 日
响应示例
facets: [
{
code: "brand",
values: [
{
name: "Adidas",
count: 1
},
{
name: "Nike",
count: 1
},
{
name: "Reebook",
count: 1
}
]
},
{
code: "size",
values: [
{
name: 41,
count: 3
},
{
name: 42,
count: 2
},
{
name: 43,
count: 1
},
{
name: 44,
count: 1
}
]
},
{
code: "colour",
values: [
{
name: "White",
count: 2
},
{
name: "Blue",
count: 1
},
{
name: "Green",
count: 1
},
{
name: "Black",
count: 1
}
]
}
]
或
facets: {
"brand": {
"Adidas": 1,
"Nike":1,
"Reebook":1
},
"size": {
"41": 3,
"42":2,
"43":1,
"44":1
},
"colour": {
"White": 2,
"Blue":1,
"Green":1,
"Black":1
}
}
这是我们的第一阶段。下一步将是如何在我 selected 尺寸:41 和颜色:白色时搜索可能的过滤器。
谢谢
这是一个可能适合您的汇总。
db.getCollection('test').aggregate([
{$unwind: '$filters'},
{$unwind: '$filters'},
{
$group: {
_id: {code: '$filters.code', value: '$filters.value'},
products: {$addToSet: '$_id'}
}
},
{
$project: {
'filter.value': '$_id.value',
'filter.count': {$size: '$products'}
}
},
{
$group: {
_id: '$_id.code',
filters: {$push: '$filter'}
}
}
]);
您需要的数据来自 slightly different format,因为没有简单的方法将分组值数组转换为对象属性。
如果一些过滤器已经 selected 你需要在第一个 $unwind 之后的另一个 $match 阶段。 它还支持多个 selects。假设我想要 white/black 由 Reebook/Adidas 制造的鞋子。
db.getCollection('test').aggregate([
{$unwind: '$filters'},
{
$match: {
$and: [
//Add objects here fo everything that is selected already
{'filters': {$elemMatch: {code: 'colour', value: {$in: ['black', 'white']}}}},
{'filters': {$elemMatch: {code: 'brand', value: {$in: ['Adidas', 'Reebook']}}}}
]
}
},
{$unwind: '$filters'},
{
$group: {
_id: {code: '$filters.code', value: '$filters.value'},
products: {$addToSet: '$_id'}
}
},
{
$project: {
'filter.value': '$_id.value',
'filter.count': {$size: '$products'}
}
},
{
$group: {
_id: '$_id.code',
filters: {$push: '$filter'}
}
}
]);
最后一件事是这样的依赖行为: Select Nike => 尺码和颜色按品牌筛选,但您仍然可以 select 所有品牌。 Select Nike + 42 尺码 => 您可以 select 只有尺码为 42 的品牌、颜色和有 42 尺码鞋子的品牌。 等等。
您可以为此利用 $facet。事实上,当这个想法是下一个。 如果我们正在计算品牌 - 我们应该根据 select 在大小和颜色下拉列表中编辑的内容来过滤记录。 如果我们计算尺寸 - 应用颜色和品牌。颜色的逻辑相同。
这是在 mongo shell 中工作的代码:
//This hash is going to by dynamic
//Add and remove properties, change $in arrays
//Depending on what user does
var conditionsForUserSelect = {
'colour': {'filters': {$elemMatch: {code: 'colour', value: {$in: ['green']}}}},
'brand': {'filters': {$elemMatch: {code: 'brand', value: {$in: ['Nike']}}}},
'size': {'filters': {$elemMatch: {code: 'size', value: {$in: [41]}}}}
};
var getFacetStage = function (code) {
//Empty object, accept all filters if nothing is selected
var matchStageCondition = {};
var selectedFilters = Object.keys(conditionsForUserSelect);
if (selectedFilters && selectedFilters.length) {
//Take all queries EXCEPT for the passed
//E.g. if we are counting brand filters then we should apply color and size.
//Because for example if no size/colour selected we should
//allow all brands even if Reebok is selected
var conditionsToApply = selectedFilters
.filter(function (key) {
return key !== code
})
.map(function (key) {
return conditionsForUserSelect[key]
});
if (conditionsToApply && conditionsToApply.length) {
matchStageCondition = {
$and: conditionsToApply
};
}
}
return [
{$unwind: '$filters'},
{
$match: matchStageCondition
},
{$unwind: '$filters'},
{
$group: {
_id: {code: '$filters.code', value: '$filters.value'},
products: {$addToSet: '$_id'}
}
},
{
$project: {
'filter.value': '$_id.value',
'filter.count': {$size: '$products'}
}
},
{
$group: {
_id: '$_id.code',
filters: {$push: '$filter'}
}
},
{
$match: {_id: code}
}
];
};
db.getCollection('test').aggregate([
{
$facet: {
colour: getFacetStage('colour'),
size: getFacetStage('size'),
brand: getFacetStage('brand')
}
}
]);