MongoDB聚合管道中的平方根

to94eoyn  于 2023-05-28  发布在  Go
关注(0)|答案(3)|浏览(133)

有没有一种方法可以在MongoDB聚合管道中获取字段的平方根?我在想这样的事情:

db.collection.aggregate(
  { $group: _id: null, sum: { $sum: "$values" }}, 
  { $project: { answer: { $sqrt: "$sum" }}})

我知道$sqrt不存在,也不存在任何幂运算符,但是有没有一种方法可以在聚合管道中实现这一点?我知道这可以在map-reduce中使用用户定义的函数来完成,但在聚合管道中可能吗?

ffdz8vbo

ffdz8vbo1#

正如@AnandJayabalan所指出的,**$sqrt**运算符将随MongoDB版本3.2发布,其语法为:

{ $sqrt: <number> }

在您的示例中,这将是

db.collection.aggregate([
  { $group: { _id: null, total: { $sum: "$values" }}}, 
  { $project: { answer: { $sqrt: "$total" }}}])

在撰写本文时,作为一种变通方法,John Page关于calculating square roots within the aggregation framework的链接博客文章使用算术原语通过牛顿法计算平方根。
为了解释这个算法是如何工作的,假设你想找到一个正数N的平方根。牛顿的方法涉及对一个数字A进行有根据的猜测,当平方时,将接近等于N
例如,如果N = 121,您可能会猜测A = 10,因为A² = 100,这是一个接近的猜测,但您可以做得更好。
该方法中使用的方程是 * 牛顿平方根方程 *:

何处

  • N是一个正数,您要求其平方根
  • 是平方根符号
  • 表示“近似等于……”
  • A是你的猜测

如果需要,牛顿法允许您多次重复估计,以接近一个精确的数字。以John Page的例子N = 29为例,您可以猜测A = 5,然后将这些值输入到公式和算法步骤中

**a.**从猜测A = 5开始
**B.**将N除以猜测值(29/5 = 5.9)
**c.**将其添加到猜测(5.9 + 5 = 10.9)
**d.**然后将结果除以2 (10.9/2 = 5.45)
e.将其设置为新的猜测A = 5.45,并重复,从B开始。

5.45 5.38555 5.38516
经过2次迭代,答案是3.1623,接近平方根的精确值。
现在,使用聚合框架(来自John Page的博客文章)并将其应用到您的示例中,聚合管道将是:

var groupPipeline = { $group: _id: null, total: { $sum: "$values" } },
    firstGuess = { 
        $project : { 
            n : "$total", r : { $literal : 5 } /* similar to step a) in the algorithm, the $literal operator sets r to the value 5 */
        } 
    },
    refineStep = { 
        $project : { 
            n: 1, r : { 
                $divide : [  /* step d) of the algorithm */
                    { 
                        $add : [ /* step c) of the algorithm */
                            { $divide : [ "$n", "$r"] },  /* step b) of the algorithm */
                            "$r" 
                        ]
                    },
                    2 
                ]
            }
        }
    };

/* Run the aggregation pipeline */
> db.collection.aggregate(groupPipeline, firstGuess, refineStep, refineStep, refineStep)

> { "_id" : ObjectId("538062103439ddd3764ff791"), "n" : 29, "r" : 5.385164807134505 }
qnakjoqk

qnakjoqk2#

Mongo 3.2将有一个原生的sqrt method for the aggregation framework
如果参数解析为null值或引用的字段丢失,则$sqrt返回null。如果参数解析为NaN,则$sqrt返回NaN。
负数上的$sqrt错误。

nhaq1z21

nhaq1z213#

这篇文章很老了,但是我们需要编写自己的立方根实现来替换$pow(x,1/3)。原因是我们使用AWS DocumentDB不必托管和管理我们自己的MongoDB数据库。但是,AWS Document DB在其两个版本中都不支持运算符$sqrt$pow。我也不明白他们为什么不支持它。
我们实现了两个版本,使用Netwon的方法和Halley的方法(因为后者收敛得更快)。从技术上讲,它们也是基于使用聚合框架的@chridram的答案。您可以复制确切的命令以重现它:
启动一个docker容器并执行mongosh

docker run -d -p 27017:27017 --name test-mongo mongo:latest
docker exec -it test-mongo mongosh

Netwon方法:

use test
db.roots.insert({n:29})
firstguess = { $project : { n : 1, r : { $literal : 1}}}
refine = { $project : { n: 1, r : { $multiply: [{$divide: [1, 3]},{$add:[{$divide: ["$n", {$multiply: ["$r","$r"]}]},{$multiply:[2, "$r"]}]}]
}}}
db.roots.aggregate([firstguess,refine,refine,refine,refine,refine,refine,refine,refine])

Halley方法:

use test
db.roots.insert({n:29})
firstguess = { $project : { n : 1, r : { $literal : 1}}}
refine = { $project : { n: 1, r :
{
    $multiply: [
        "$r",
        {
            $divide: [
                {
                    $add: [
                        {$multiply: ["$r", "$r", "$r"]},
                        {$multiply: [2, "$n"]}
                    ]
                },
                {
                    $add: [
                        {$multiply: [2, "$r", "$r", "$r"]},
                        "$n"
                    ]
                }
            ]
        }
    ]
}}}
db.roots.aggregate([firstguess,refine,refine,refine,refine,refine,refine,refine,refine])

有关实际公式的更多详细信息,请参见https://en.wikipedia.org/wiki/Cube_root
进一步的改进可以是使用$addFields而不是$project。它更容易阅读。参见https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/

相关问题