如何在mongoDb中查找字段值的最长和最短长度?

13

该字段的数据类型为字符串。我想要在MongoDB中找到该字段最长和最短值的长度。

我的集合中一共有50万个文档。

4个回答

10

在现代版本的MongoDB中,有 $strLenBytes$strLenCP 聚合操作符,使您可以轻松执行以下操作:

Class.collection.aggregate([
  { "$group" => {
    "_id" => nil,
    "max" => { "$max" => { "$strLenCP" => "$a" } },
    "min" => { "$min" => { "$strLenCP" => "$a" } }
  }}
]) 

在您的文档中,"a" 是您想要获取最小和最大长度的字符串属性。


要输出最小和最大长度,最佳方法是使用mapReduce,并使用一些技巧仅保留值。

首先,您定义一个映射函数,它只会从集合中输出单个项目以减轻负载:

map = Q%{
    function () {

      if ( this.a.length < store[0] )
        store[0] = this.a.length;

      if ( this.a.length > store[1] )
        store[1] = this.a.length;

      if ( count == 0 )
        emit( null, 0 );

      count++;

    }
}

由于这个函数大部分是使用全局作用域的变量来保持最小和最大长度,因此您只需在发出的单个文档上用finalize函数替换它。没有缩减阶段,但是即使没有调用,也要为它定义一个“空白”函数:

reduce = Q%{ function() {} }

finalize = Q%{
    function(key,value) {
        return {
            min: store[0],
            max: store[1]
        };
    }
}

然后调用mapReduce操作:

Class.map_reduce(map,reduce).out(inline: 1).finalize(finalize).scope(store: [], count: 0)

所以所有的工作都是在服务器上完成,而不是通过对发送到客户端应用程序的结果进行迭代。对于像这样的小数据集:

{ "_id" : ObjectId("543e8ee7ddd272814f919472"), "a" : "this" }
{ "_id" : ObjectId("543e8eedddd272814f919473"), "a" : "something" }
{ "_id" : ObjectId("543e8ef6ddd272814f919474"), "a" : "other" }
您会得到这样的结果(Shell输出,但对于驱动程序基本相同):
{
    "results" : [
            {
                    "_id" : null,
                    "value" : {
                            "min" : 4,
                            "max" : 9
                    }
            }
    ],
    "timeMillis" : 1,
    "counts" : {
            "input" : 3,
            "emit" : 1,
            "reduce" : 0,
            "output" : 1
    },
    "ok" : 1
}

所以mapReduce允许在服务器上快速进行JavaScript处理,从而减少网络流量。目前MongoDB没有其他本地方式返回字符串长度,因此在服务器上需要进行JavaScript处理。


@muistooshort,为什么发出500000个文档更重要。OP只想要最小值和最大值。全局变量是JavaScript声明的,并被强制转换为此类。并且只能由mapReduce函数访问。清楚吗? - Neil Lunn
@muistooshort MongoDB没有其他操作符来返回字符串的长度,因此您需要使用JavaScript和mapReduce才能“返回”结果。如果有一些本地的东西会更好,但这仍然是一个对比一个文档值与另一个文档值的“全局”比较,而mapReduce是提供此功能的唯一方法。将500,000个文档传递给reducer来执行可以在mapper中完成的工作将是一种hack且不必要,更不用说更多的工作了。在引用的JavaScript中没有内部字符串,因此这并不重要。 - Neil Lunn
或许对于新的mongos,你应该使用“:”代替“=>”,使用“null”代替“nil”。此外,使用db.collections... - Blza Box
1
@AntonLosev,根据问题中 mongoid 的标签,使用 =>nil 的语言是“Ruby”。因此,在执行来自“Ruby”代码的基于JavaScript的mapreduce示例中也有 Q% 这一部分。这就是你之前错过的。 - Neil Lunn
明白了,很容易被忽略。 - Blza Box

8

获取字段的最长值

db.entities.aggregate([{ $match:{ condition   }  },{
  $addFields: {
    "length": { $strLenCP: "$feildName" }
  }},
{ "$sort": { "length": -1 } },
{$limit:1}
])

将 { "$sort": { "length": -1 } } 修改为 { "$sort": { "length": 1 } },以找到字段中最短的值。


7

您可以使用mongo shell脚本。请注意,它将执行全表扫描。

    function findMinMax() {
        var max = 0;
        var min = db.collection.findOne().fieldName.length;

        db.collection.find().forEach(function(doc) {
            var currentLength = doc.fieldName.length; 
            if (currentLength > max) {
               max = currentLength;
            }
            if (currentLength < min) {
               min = currentLength;
            }
        });

         print(max);
         print(min);
    }

   use <databaseName>
   findMinMax();

您可以将该函数保存在文件中,例如c:\minMax.js,并运行该文件:

c:\mongodb\bin> mongo dbName < c:\minMax.js

注意:您可能需要提供必要的主机名、用户名和密码来连接您的数据库。
c:\mongodb\bin> mongo --host hostName --port portNumber -u userName -p password dbName < c:\minMax.js

如何将此作为Mongo shell脚本运行? - sofs1
当我将函数保存为minMax.js时,我应该用db.<MyCollectionName>.find().forEach(function(doc)替换db.collection.find().forEach(function(doc)吗? 我也需要在这里提供我的数据库名称use <databaseName>吗? - sofs1
1
我扩展了这个答案并分享到了Github上,链接为https://github.com/boly38/mongo-scripts。 - boly38

1
虽然使用聚合框架绝对是首选的方法,但下面的答案对于可能包含空值的字段是无效的。
为了克服这个问题,我们需要使用$ifNull运算符(将fieldName替换为您希望查询的字段):
db.myCollection.aggregate([
    {
        $project: {
            fieldNameLength: {
                $strLenCP: { $ifNull: ["$fieldName", ""] }
            }
        }
    },
    {
        $group: {
            _id: null,
            maxLength: { $max: "$fieldNameLength" }
        }
    }
])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接