是否可以通过“加权平均值”进行排序
可能有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
然后我想按该值对所有文档进行排序。
订单将是
答案 0 :(得分:2)
对于MongoDB可以从这样的计算中对数据进行排序,答案真的是“是”和“否”。它当然可以做到,但可能不符合您的实际目的。
MongoDB必须进行任何计算的两个工具是a 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
运算符来匹配不同的元素及其计数,{{ 1}}以任何方式使这成为一种不具备性能的解决方案。
如果像$unwind
这样的运算符可以生成配对所需的索引值,并且引入任何方法来类似地对数组结果执行“内联求和”操作或其他数学运算,那么将来可能会变得切实可行处理$map
。但是在编写时,即使在开发过程中也不存在。