是否可以进行查询以按“加权平均值”排序
有 5 个可能的值,范围为 1-5。加权平均值为
(n5*5 + n4*4 + n3*3 + n2*2 + n1*1)/(n5+n4+n3+n2+n1)
其中 n5 是评级为 5 的对象的数量
我有以下示例。如果您找到更好的存储结构,我很高兴听到。
{
"_id" : "wPg4jzJsEFXNxR5Wf",
"caveId" : "56424a93819e7419112c883e",
"data" : [
{
"value" : 1
},
{
"value" : 3
},
{
"value" : 4
},
{
"value" : 2
}
]
}
{
"_id" : "oSrtv33MgnkJFvNan",
"caveId" : "56424a93819e7419112c949f",
"data" : [
{
"value" : 1
},
{
"value" : 4
},
{
"value" : 4
},
{
"value" : 2
}
]
}
{
"_id" : "gJRMMQPwDwjFrL7zz",
"caveId" : "56424a93819e7419112c8727",
"data" : [
{
"value" : 5
},
{
"value" : 1
},
{
"value" : 4
}
]
}
_ID 示例:oSrtv33MgnkJFvNan(第二个)
(2*4 + 1*2 + 1*1)/(2+1+1) = 2.75
然后我想按该值对所有文档进行排序。
顺序是
- gJRMMQPwDwjFrL7zz:值:3.33
- oSrtv33MgnkJFvNan:值 2.75
- wPg4jzJsEFXNxR5Wf:值 2.5
最佳答案
关于 MongoDB 是否可以像这样通过计算对数据进行排序,答案实际上是"is"和“否”。它当然可以做到这一点,但可能无法以实际的方式实现您的目的。
MongoDB 必须执行任何类型计算的两个工具是 ggregation framework和 mapReduce 。前者目前缺乏运营商来真正以实际的方式处理这个问题。第二个可以通过将要排序的组件放入分组键中(即使没有实际分组)来“诱骗”排序,作为 mapReduce 工作方式的工件。
所以你基本上可以用这样的东西来应用数学:
db.data.mapReduce(
function() {
var vals = this.data.map(function(el){ return el.value }),
uniq = {};
vals.forEach(function(el) {
if (!uniq.hasOwnProperty(el)) {
uniq[el] = 1;
} else {
uniq[el]++;
}
});
var weight = Array.sum(Object.keys(uniq).map(function(key) {
return uniq[key] * key
})) / Array.sum(Object.keys(uniq).map(function(key) {
return uniq[key];
}))
var id = this._id;
delete this._id;
emit({ "weight": weight, "orig": id },this);
},
function() {},
{ "out": { "inline": 1 } }
)
这会给你这个输出:
{
"results" : [
{
"_id" : {
"weight" : 2.5,
"orig" : "wPg4jzJsEFXNxR5Wf"
},
"value" : {
"caveId" : "56424a93819e7419112c883e",
"data" : [
{
"value" : 1
},
{
"value" : 3
},
{
"value" : 4
},
{
"value" : 2
}
]
}
},
{
"_id" : {
"weight" : 2.75,
"orig" : "oSrtv33MgnkJFvNan"
},
"value" : {
"caveId" : "56424a93819e7419112c949f",
"data" : [
{
"value" : 1
},
{
"value" : 4
},
{
"value" : 4
},
{
"value" : 2
}
]
}
},
{
"_id" : {
"weight" : 3.3333333333333335,
"orig" : "gJRMMQPwDwjFrL7zz"
},
"value" : {
"caveId" : "56424a93819e7419112c8727",
"data" : [
{
"value" : 5
},
{
"value" : 1
},
{
"value" : 4
}
]
}
}
]
}
因此所有结果都已排序,但当然存在限制,mapReduce 只能生成低于 16MB BSON 限制的“内联”输出,或者将结果写入另一个集合。
即使聚合框架中添加了可以提供帮助的新功能(来自当前的开发系列 3.1.x),这仍然需要使用 $unwind
来获得“总和”以任何方式包含元素(还没有“减少”功能之类的功能),这并不能使其成为稳定或实用的替代方案。
所以你可以用mapReduce来做到这一点,但为了我的钱,我会有另一个过程来计算它定期运行(或在更新时触发)并更新文档上的标准“权重”字段,然后可以直接使用用于排序。
在文档中添加值始终是最有效的选择。
出于好奇,您可以获取 MongoDB 的开发分支版本(3.1.x 系列)或此后的任何版本,并应用如下聚合管道:
db.data.aggregate([
{ "$project": {
"caveId": 1,
"data": 1,
"conv": {
"$setUnion": [
{ "$map": {
"input": "$data",
"as": "el",
"in": "$$el.value"
}},
[]
]
},
"orig": {
"$map": {
"input": "$data",
"as": "el",
"in": "$$el.value"
}
}
}},
{ "$project": {
"caveId": 1,
"data": 1,
"conv": 1,
"orig": 1,
"counts": { "$map": {
"input": "$conv",
"as": "el",
"in": {
"$size": {
"$filter": {
"input": "$orig",
"as": "o",
"cond": {
"$eq": [ "$$o", "$$el" ]
}
}
}
}
}}
}},
{ "$unwind": { "path": "$conv", "includeArrayIndex": true } },
{ "$group": {
"_id": "$_id",
"caveId": { "$first": "$caveId" },
"data": { "$first": "$data" },
"counts": { "$first": "$counts" },
"mult": {
"$sum": {
"$multiply": [
"$conv.value",
{ "$arrayElemAt": [ "$counts", "$conv.index" ] }
]
}
}
}},
{ "$unwind": "$counts" },
{ "$group": {
"_id": "$_id",
"caveId": { "$first": "$caveId" },
"data": { "$first": "$data" },
"count": { "$sum": "$counts" },
"mult": { "$first": "$mult" }
}},
{ "$project": {
"data": 1,
"weight": { "$divide": [ "$mult", "$count" ] }
}},
{ "$sort": { "weight": 1 } }
])
但即使使用像 $filter
和 $unwind
中的“includeArrayIndex”这样的帮助器,以及稍后使用该索引进行匹配的 $arrayElemAt
运算符不同的元素及其计数,以任何方式使用 $unwind
都会导致该解决方案性能不佳。
如果像 $map
这样的运算符可以生成配对所需的索引值,并引入任何方法来类似地进行“内联求和”运算或其他数学运算,那么它在未来可能会变得实用对数组结果进行处理,而不处理 $unwind
。但截至撰写本文时,即使在开发中,这种情况也不存在。
关于mongodb - 通过 mongodb 加权平均评分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33666418/