MongoDB 从两个数组计算值、排序和限制

2024-03-28

我有一个存储浮点数组的 MongoDB 数据库。假设以下格式的文档集合:

{
    "id" : 0,
    "vals" : [ 0.8, 0.2, 0.5 ]
}

有一个查询数组,例如,带有值[ 0.1, 0.3, 0.4 ],我想计算集合中所有元素的距离(例如,差异之和;对于给定的文档和查询,它将通过以下方式计算)abs(0.8 - 0.1) + abs(0.2 - 0.3) + abs(0.5 - 0.4) = 0.9).

我尝试使用MongoDB的聚合函数来实现这一点,但我不知道如何迭代数组。 (我没有使用 MongoDB 的内置地理操作,因为数组可能相当长)

我还需要对结果进行排序并限制为前 100 个,因此不需要读取数据后进行计算。


当前处理是mapReduce

如果您需要在服务器上执行此操作并对排名靠前的结果进行排序并只保留排名前 100 的结果,那么您可以使用 mapReduce 来执行此操作,如下所示:

db.test.mapReduce(
    function() {
        var input = [0.1,0.3,0.4];
        var value = Array.sum(this.vals.map(function(el,idx) {
            return Math.abs( el - input[idx] )
        }));

        emit(null,{ "output": [{ "_id": this._id, "value": value }]});
    },
    function(key,values) {
        var output = [];

        values.forEach(function(value) {
            value.output.forEach(function(item) {
                output.push(item);
            });
        });

        output.sort(function(a,b) {
            return a.value < b.value;
        });

        return { "output": output.slice(0,100) };
    },
    { "out": { "inline": 1 } }
)

因此,映射器函数在同一键下执行计算和输出所有内容,因此所有结果都发送到减速器。最终输出将包含在单个输出文档的数组中,因此重要的是,所有结果都使用相同的键值发出,并且每个发出的输出本身就是一个数组,以便 MapReduce 可以正常工作。

排序和缩减是在缩减程序本身中完成的,当检查每个发出的文档时,元素将被放入单个临时数组中,进行排序,然后返回顶部结果。

这很重要,这就是发射器将其生成为数组的原因,即使一开始是单个元素。 MapReduce 的工作原理是按“块”处理结果,因此即使所有发出的文档具有相同的键,它们也不会立即全部处理。相反,reducer 将其结果放回发出的结果队列中进行缩减,直到该特定键只剩下一个文档。

为了列表的简洁性,我将此处的“切片”输出限制为 10,并包含统计数据以表明观点,因为可以看到在这 10000 个样本上调用的 100 个归约周期:

{
    "results" : [
        {
            "_id" : null,
            "value" : {
                "output" : [
                    {
                        "_id" : ObjectId("56558d93138303848b496cd4"),
                        "value" : 2.2
                    },
                    {
                        "_id" : ObjectId("56558d96138303848b49906e"),
                        "value" : 2.2
                    },
                    {
                        "_id" : ObjectId("56558d93138303848b496d9a"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d93138303848b496ef2"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497861"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497b58"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497ba5"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d94138303848b497c43"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d95138303848b49842b"),
                        "value" : 2.1
                    },
                    {
                        "_id" : ObjectId("56558d96138303848b498db4"),
                        "value" : 2.1
                    }
                ]
            }
        }
    ],
    "timeMillis" : 1758,
    "counts" : {
            "input" : 10000,
            "emit" : 10000,
            "reduce" : 100,
            "output" : 1
    },
    "ok" : 1
}

所以这是一个单一的文档输出,采用特定的mapReduce 格式,其中“值”包含一个元素,该元素是排序和有限结果的数组。

未来的处理是聚合的

截至撰写本文时,MongoDB 目前最新的稳定版本是 3.0,它缺乏使您的操作成为可能的功能。但即将发布的 3.2 版本引入了新的运算符,使这成为可能:

db.test.aggregate([
    { "$unwind": { "path": "$vals", "includeArrayIndex": "index" }},
    { "$group": {
        "_id": "$_id",
        "result": {
            "$sum": {
                "$abs": {
                    "$subtract": [ 
                        "$vals", 
                        { "$arrayElemAt": [ { "$literal": [0.1,0.3,0.4] }, "$index" ] } 
                    ]
                }
            }
        }
    }},
    { "$sort": { "result": -1 } },
    { "$limit": 100 }
])

为了简洁起见,还限制为相同的 10 个结果,您将得到如下输出:

{ "_id" : ObjectId("56558d96138303848b49906e"), "result" : 2.2 }
{ "_id" : ObjectId("56558d93138303848b496cd4"), "result" : 2.2 }
{ "_id" : ObjectId("56558d96138303848b498e31"), "result" : 2.1 }
{ "_id" : ObjectId("56558d94138303848b497c43"), "result" : 2.1 }
{ "_id" : ObjectId("56558d94138303848b497861"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b499037"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b498db4"), "result" : 2.1 }
{ "_id" : ObjectId("56558d93138303848b496ef2"), "result" : 2.1 }
{ "_id" : ObjectId("56558d93138303848b496d9a"), "result" : 2.1 }
{ "_id" : ObjectId("56558d96138303848b499182"), "result" : 2.1 }

这之所以成为可能,很大程度上是由于$unwind https://docs.mongodb.org/master/reference/operator/aggregation/unwind/被修改为在结果中投影包含数组索引的字段,并且还由于$arrayElemAt https://docs.mongodb.org/master/reference/operator/aggregation/arrayElemAt/这是一个新的运算符,可以从提供的索引中提取数组元素作为奇异值。

这允许通过输入数组中的索引位置“查找”值,以便将数学应用到每个元素。输入数组由现有的$literal https://docs.mongodb.org/manual/reference/operator/aggregation/literal/运算符所以$arrayElemAt不会抱怨并将其识别为数组(目前似乎是一个小错误,因为其他数组函数没有直接输入的问题)并通过使用生成的“index”字段获取适当的匹配索引值$unwind进行比较。

数学计算是通过$subtract https://docs.mongodb.org/manual/reference/operator/aggregation/subtract/当然还有另一个新的运营商$abs https://docs.mongodb.org/master/reference/operator/aggregation/abs/以满足您的功能。此外,由于首先需要展开数组,所以所有这些都是在一个内部完成的$group https://docs.mongodb.org/manual/reference/operator/aggregation/group/阶段累积每个文档的所有数组成员并通过$sum https://docs.mongodb.org/manual/reference/operator/aggregation/sum/累加器。

最后所有结果文档都经过处理$sort https://docs.mongodb.org/manual/reference/operator/aggregation/sort/然后是$limit https://docs.mongodb.org/manual/reference/operator/aggregation/limit/应用于仅返回顶部结果。

Summary

即使 MongoDB 聚合框架即将推出新功能,但哪种方法实际上对结果更有效仍然存在争议。这主要是因为仍然需要$unwind数组内容,它有效地为要处理的管道中的每个数组成员生成每个文档的副本,这通常会导致开销。

因此,虽然在新版本发布之前,mapReduce 是实现此目的的唯一方法,但它实际上可能优于聚合语句,具体取决于要处理的数据量,并且尽管聚合框架适用于本机编码运算符而不是翻译后的 JavaScript运营。

与所有事情一样,始终建议进行测试,以确定哪种情况更适合您的目的,以及哪种情况为您的预期处理提供最佳性能。


Sample

当然,问题中提供的示例文档的预期结果是0.9通过应用数学。但仅出于我的测试目的,这里有一个简短的列表,用于生成一些示例数据,我想至少验证 mapReduce 代码是否正常工作:

var bulk = db.test.initializeUnorderedBulkOp();

var x = 10000;

while ( x-- ) {
    var vals = [0,0,0];

    vals = vals.map(function(val) {
        return Math.round((Math.random()*10),1)/10;
    });

    bulk.insert({ "vals": vals });

    if ( x % 1000 == 0) {
        bulk.execute();
        bulk = db.test.initializeUnorderedBulkOp();
    }
}

这些数组是完全随机的单个小数点值,因此我作为示例输出给出的列出的结果没有太多分布。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MongoDB 从两个数组计算值、排序和限制 的相关文章

  • 猫鼬模式创建

    我刚刚开始使用猫鼬 我有一个使用 mongoose 的创建脚本 它使用示例数据创建模式和数据库 现在我编写实际的应用程序 我是否需要在每次应用程序运行时创建架构对象 或者它是否已经以某种方式可用 换句话说 我是否需要在每个使用 mongoo
  • 如何检查Mongodb企业版是否正在使用

    如何查看是否使用的是mongodb企业版 有没有我可以查询的标志或属性 mongod version只返回版本 我最近在 MongoDB JIRA 上问了这个问题 回复如下 至少可以通过三种方法来确定您是否正在运行 Enterprise 我
  • 使用 Homebrew 安装 mongodb 时遇到问题

    我正在尝试让 Mongo 运行 我安装了包管理器Homebrew https brew sh 然后我使用了 Mongo 站点上的命令 brew update brew install mongodb 这似乎安装正确 我输入了mongo在新的
  • MongoDB - 编辑器变量 - MongoDB shell - Windows 7

    EDITOR 变量功能真的可以在 Windows 7 上使用吗 我正在读一篇文章 说一旦我们设置了 EDITOR 变量在 mongorc js 中 我们只需在 shell 中输入 编辑变量名 and var name将被加载到编辑器中 在我
  • Mongo JSON 文档 -> JSON -> BSON

    我正在使用 Node js 构建一个使用 mongodb 的 Web 套接字服务器 我使用 node mongodb native 作为访问 mongodb 的库 当我对数据库中的对象调用 console log sys inspect i
  • 如何决定使用哪种NoSQL技术? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 MongoDB 基于文档 HBase 基于列 和 Neo4j 对象图 的优缺点是什么 我特别有兴趣了解
  • Mongodb upsert 嵌入文档

    我每天每米有一份文件 如果它不存在 如何在数据数组中添加另一个子文档并创建整个文档 key 20120418 123456789 data Meter 123456789 Dt ISODate 2011 12 29T16 00 00 0Z
  • 为什么使用 MongoDB 的 $push 向数组添加新对象时会添加带有 ObjectID 的 _id ?

    我正在使用 Node js 和 Mongoose 玩家和锦标赛变量是之前获取的 Mongoose 对象 我想将一个新的tournamentSession对象 不是Mongoose对象 添加到玩家对象的tournamentSessions字段
  • 是否可以通过编写单独的mapreduce程序并行执行Hive查询?

    我问了一些关于提高 Hive 查询性能的问题 一些答案与映射器和减速器的数量有关 我尝试使用多个映射器和减速器 但在执行中没有看到任何差异 不知道为什么 可能是我没有以正确的方式做 或者我错过了其他东西 我想知道是否可以并行执行 Hive
  • 为什么在我的例子中 For 循环比 Map、Reduce 和 List 理解更快

    我编写了一个简单的脚本来测试速度 这就是我发现的结果 实际上 for 循环在我的例子中是最快的 这真的让我感到惊讶 请查看下面 正在计算平方和 这是因为它在内存中保存列表还是有意为之 谁能解释一下这一点 from functools imp
  • 如何更改 MongoDB 用户权限?

    例如 如果我有这个用户 gt db system users find user testAdmin pwd some hash roles clusterAdmin otherDBRoles TestDB readWrite 我想给那个用
  • Hadoop - 直接从 Mapper 写入 HBase

    我有一个 hadoop 作业 其输出应写入 HBase 我并不真正需要减速器 我想要插入的行类型是在映射器中确定的 如何使用 TableOutputFormat 来实现此目的 从所有示例中 我看到的假设是 reducer 是创建 Put 的
  • Mongodb 限制聚合查询中的数组

    我正在尝试编写一个查询来返回每个类别中的前 X 个术语 例如前 5 个 前 10 个等 每个术语都有一个关联的类别 并且基于另一个术语的帮助堆栈溢出问题 https stackoverflow com questions 25666187
  • MongoDB 复合键:InvalidOperationException:{document}.Identity 不受支持

    我在水合由复合 ID 组成的类时遇到问题 该复合 ID 又具有基类 我收到一条错误消息InvalidOperationException document Identity is not supported 我试图写入数据库的类如下 pub
  • 如何使用 Stripe 在一次操作中创建客户和卡片?

    我正在尝试第一次初始化客户 我有一个表格 他们可以在那里注册和填写所有内容 然后他们提交 在客户端上 会发生以下情况 var cardValues AutoForm getFormValues credit card form insert
  • 为什么 Spark 比 Hadoop MapReduce 更快

    有人可以使用字数统计示例解释一下为什么 Spark 比 MapReduce 更快吗 bafna的答案提供了故事的记忆方面 但我想补充另外两个重要事实 DAG和生态系统 Spark 使用 惰性求值 来形成连续计算阶段的有向无环图 DAG 通过
  • 使用 Tweepy 获取推文时出错

    我有一个用于获取推文的 Python 脚本 在脚本中我使用该库 Tweepy 我使用有效的身份验证参数 运行此脚本后 一些推文存储在我的 MongoDB 中 有些则被 if 语句拒绝 但我仍然收到错误 requests packages u
  • Node.js 检测两个猫鼬查找何时完成

    我正在尝试使用自动完成功能初始化两个输入library https www devbridge com sourcery components jquery autocomplete 当我加载页面时 我将触发 Ajax 来初始化两个输入文本
  • 在 MongoDB 聚合的“addField”操作中使用“$count”

    我正在尝试找到聚合运算符的正确组合 以将标题为 totalCount 的字段添加到我的 mongoDB 视图中 这将为我提供聚合管道特定阶段的计数 并将其输出为每个文档的计数结果 count count 但最终我得到了一个具有此结果的文档
  • 如何处理 MongoDB 的断开连接错误

    我在 Node js 进程中看到了这个未捕获的异常 Uncaught exception Error read ETIMEDOUT at TCP onStreamRead internal stream base commons js 16

随机推荐