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 从两个数组计算值、排序和限制 的相关文章

  • 如何在 MongoDB 2.6 副本集上启用 HTTP 控制台

    我正在运行一个 3 服务器 MongoDB 副本集 我最近从 2 4 升级到 2 6 在 2 4 中 我能够访问所有三台服务器上的 HTTP 控制台 无论它们是主服务器还是辅助服务器 现在 2 6 需要不同的配置设置来启用控制台 Disab
  • 如何在 Meteor 中读取依赖于另一个集合的集合

    我正在尝试从集合中加载最新的帖子 同时加载同一篇帖子的所有评论 该集合具有引用 而不是将整个文档存储在彼此内部 Post title body etc Comment postId body etc 我使用iron router作为路由包
  • FieldPath 字段名称不能包含“.”当尝试使用 AGGREGATE 时

    我的查询有什么问题吗 db table aggregate match gt expr gt and gt eq gt size gt events 4 events 0 updated gt lt gt 2019 05 05 我越来越 M
  • mongodb 查询 DBRef 类型

    如何在 mongodb shell 中将此查询转换为有效的 mongodb 查询 cars owner ref users cars owner这里是一个 DBRef 但是 ref是无效的 我收到此错误 err Positional ope
  • MongoDB 使用 $sort、$skip 和 $limit 返回错误的行

    这是我正在使用的功能 MyModel aggregate match query sort createdAt 1 skip skip limit 10 allowDiskUse true query是过滤行 skip是基于分页的动态值 即
  • PowerShell 与 MongoDB C# 驱动程序方法不兼容?

    由 C 泛型引起的最新 MongoDB 驱动程序的问题 Cannot find an overload for GetCollection and the argument count 1 我可能可以使用其他没有泛型的 GetCollect
  • MongoDB 获取聚合查询中的第一个和最后一个文档

    我如何获得第一个和最后一个文档time场地 我可以用 group并得到 first and last文档 但我不需要在这里分组 只需获取第一个和最后一个完整文档 也许我可以用slice 此查询不起作用 aggregate 353469045
  • mongodb - 检索数组子集

    看似简单的任务对我来说是一个挑战 我有以下 mongodb 结构 services TCP80 data status 1 delay 3 87 ts 1308056460 status 1 delay 2 83 ts 1308058080
  • Inno Setup安装先决条件[重复]

    这个问题在这里已经有答案了 我正在通过 Inno Setup 创建一个安装程序 我看到很多关于如何检测先决条件是否存在的代码示例 但没有任何关于当我找不到先决条件时如何实际安装先决条件的代码示例 我确信它非常简单 但是我该如何安装先决条件呢
  • 重命名 MongoDB 数组中嵌入文档中的字段不起作用

    Step One gt db myCollection find id ObjectId 2358523892345 field1 value 1 field2 subfield1 value 2 Subfield2 value 3 fie
  • 使用mongoid动态创建索引

    我有一项为我的文档创建新字段的工作 我想在这项工作结束时创建该字段的索引 我试过 Model index field gt 1 and also Mongoid Sessions default rating prediction ensu
  • 如何在 MongoDB v3.0.5 中创建用户

    我需要在 mongodb 中为我的数据库创建一个用户 但似乎我无法让它工作 我已经在我的 Windows 7 机器上安装了 mongoDb v3 0 5 根据本文 https docs mongodb org v3 0 tutorial a
  • 猫鼬的深层填充

    我有两个模式 一张用于用户 另一张用于帖子 在用户模式中 我有latestPost的一个属性 它是帖子模式中条目的ObjectId 当我加载用户对象时 我想将 lastestPost 作为对象获取 其中包含用户架构中作者的用户名 其中作者是
  • 在 MongoDB 中,如何根据嵌入对象中的属性对文档进行排序?

    在我的产品集合中 我可以找到已在 GB 地区发布的所有产品 gt db products find release region GB pretty id foo release region GB date ISODate 2012 03
  • 如何在 Mongoose 中执行查找查询?

    我在 mongodb 中有一组电子书数据 例如 id ObjectId 58b56fe19585b10cd42981d8 cover path D Ebooks uploads ebooks cover 1488285665748 img1
  • MongoDB 在仅返回 _id 时使用 COLLSCAN

    我想返回 MongoDB 集合中的所有 ID 我使用了以下代码 db coll find id 1 但MongoDB扫描整个集合而不是从默认读取信息index id 1 从日志中 find collection filter project
  • CoreMongooseArray 到普通数组

    我正在从一个架构中选出 2 个元素 并希望在另一个架构中进行更新 为此 我使用切片方法将数组中的前 2 个元素列入候选名单 但我越来越 CoreMongooseArray 元素1 元素2 而不是 元素1 元素2 如何删除 CoreMongo
  • MongoDB 和 Mongoose 访问一个数据库,同时针对另一个数据库进行身份验证(NodeJS、Mongoose)

    我有几个数据库 不想为每个数据库创建单独的用户帐户 MongoDB 支持使用另一个数据库中定义的帐户来验证对数据库的访问的概念 但语法示例很难获得 当我终于弄清楚时 我正准备提出一个问题 如果它对其他人有帮助 就放在这里 这是 mongod
  • 使用 AWS MSK 连接器连接到 AWS VPC 内的 MongoDB atlas

    我正在尝试使用MongoDB使用更改流Kafka 我选择 AWS MSK 是因为我的整个基础设施都位于 AWS 内 并且可以轻松与其他 AWS 服务集成 I created an AWS MSK cluster within the VPC
  • Mongodb聚合数组大小大于匹配项[重复]

    这个问题在这里已经有答案了 我有一个集合 其中投资是 mongodb 文档内的一个数组 现在使用聚合 我尝试过滤投资长度超过 5 倍的结果 然后使用匹配查询进行下一步处理 Collection id 000000 investments h

随机推荐