首先,抱歉我的英语不好。
如果我们在MongoDB中有以下文档,
测试数据
{id:1, filter:{f1:'v1-1', f2:'v2-1', f3:['v3-1', 'v3-3']}}
{id:2, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-2', 'v3-3']}}
{id:3, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-1', 'v3-3']}}
准备收藏
db.test.drop()
db.test.insert({id:1, filter:{f1:'v1-1', f2:'v2-1', f3:['v3-1', 'v3-3']}})
db.test.insert({id:2, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-2', 'v3-3']}})
db.test.insert({id:3, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-1', 'v3-3']}})
您可以将过滤器字段视为在许多购物网站上使用的过滤器功能,例如,他们会告诉您网站上有多少台LED电视和多少台液晶电视。
我想使用MongoDB来计算每个过滤器选项的文档数量(包括数组字段中的每个项目),预期结果如下。
预期结果
[
{
_id : { key: 'f1', value: 'v1-1' }, count: 3
},
{
_id : { key: 'f2', value: 'v2-1' }, count: 1
},
{
_id : { key: 'f2', value: 'v2-2' }, count: 2
},
{
_id : { key: 'f3', value: 'v3-1' }, count: 2
},
{
_id : { key: 'f3', value: 'v3-2' }, count: 1
},
{
_id : { key: 'f3', value: 'v3-3' }, count: 3
}
]
使用map / reduce,
很容易得到结果映射/减少解决方案
map = function () {
for (k in this.filter) {
if (this.filter[k] instanceof Array) {
for (j in this.filter[k]) {
emit( { key: k, value: this.filter[k][j]}, 1 );
}
} else {
emit( { key: k, value: this.filter[k]}, 1 );
}
}
}
reduce = function (k, values) {
result = 0;
values.forEach( function(v) { result += v; } );
return result;
}
db.test.mapReduce(map, reduce, {out:{inline:1}})
但是由于map / reduce的性能问题,它不能用于实时查询。如果我添加一些查询条件,结果集可能会被更改,因此我无法将map / reduce结果保存到另一个集合中进行实时查询。
我可以使用聚合框架来计算一个过滤器的计数,
仅针对一个过滤器的汇总解决方案
db.test.aggregate(
{$project: {"filter.f2":1, "_id":0}},
{$group: {"_id": {"key": {$ifNull: [null, "f2"]}, "value":"$filter.f2"}, "count" : {$sum: 1}}}
)
[
{
"_id" : { "key" : "f2", "value" : "v2-2" }, "count" : 2
},
{
"_id" : { "key" : "f2", "value" : "v2-1" }, "count" : 1
}
]
但我不知道如何为所有过滤选项做到这一点。有什么想法吗?
答案 0 :(得分:1)
如果您将数据结构更改为此类,请注意所有值都是数组,即使是具有单个值的数组:
{
_id: 1,
filters: [{
key: 'f1',
values: ['v1-1']
},{
key: 'f2',
values: ['v2-1']
},{
key: 'f3',
values: ['v3-1', 'v3-3']
}]
}
{
_id: 2,
filters: [{
key: 'f1',
values: ['v1-1']
},{
key: 'f2',
values: ['v2-2']
},{
key: 'f3',
values: ['v3-2', 'v3-3']
}]
}
{
_id: 3,
filters: [{
key: 'f1',
values: ['v1-1']
},{
key: 'f2',
values: ['v2-2']
},{
key: 'f3',
values: ['v3-1', 'v3-3']
}]
}
你可以做一个像这样的聚合函数:
db.test.aggregate({
$unwind: "$filters"
},{
$project: {
_id: 1,
key: "$filters.key",
values: "$filters.values"
}
},{
$unwind: "$values"
},{
$group: {
_id: {
$concat: ["$key","|","$values"]
},
count: { $sum: 1 }
}
})
如果你愿意,你可以跳过项目步骤,我只是把它放在那里作为一个精确的。不管怎样,你都需要两次展开。