在MongoDB中查找最大的文档大小

时间:2013-06-06 03:31:27

标签: mongodb

是否可以在MongoDB中找到最大的文档大小?

db.collection.stats()显示平均尺寸,这并不具有代表性,因为在我看来,尺码可能会有很大差异。

8 个答案:

答案 0 :(得分:79)

您可以使用小型shell脚本来获取此值。

注意:这将执行全表扫描,这对大型集合来说会很慢。

let max = 0, id = null;
db.test.find().forEach(doc => {
    const size = Object.bsonsize(doc); 
    if(size > max) {
        max = size;
        id = doc._id;
    } 
});
print(id, max);

答案 1 :(得分:14)

注意:这将尝试将整个结果集存储在内存中(来自.toArray)。小心大数据集。不要在生产中使用! Abishek的答案具有处理游标而不是内存数组的优势。

如果您还想要_id,请尝试此操作。给定一个名为“请求”的集合:

// Creates a sorted list, then takes the max
db.requests.find().toArray().map(function(request) { return {size:Object.bsonsize(request), _id:request._id}; }).sort(function(a, b) { return a.size-b.size; }).pop();

// { "size" : 3333, "_id" : "someUniqueIdHere" }

答案 2 :(得分:4)

Mongo 4.4开始,新的聚合运算符$bsonSize在编码为BSON时返回给定文档的字节大小。

因此,为了找到最大尺寸的文档的bson尺寸:

// { "_id" : ObjectId("5e6abb2893c609b43d95a985"), "a" : 1, "b" : "hello" }
// { "_id" : ObjectId("5e6abb2893c609b43d95a986"), "c" : 1000, "a" : "world" }
// { "_id" : ObjectId("5e6abb2893c609b43d95a987"), "d" : 2 }
db.collection.aggregate([
  { $group: {
    _id: null,
    max: { $max: { $bsonSize: "$$ROOT" } }
  }}
])
// { "_id" : null, "max" : 46 }

此:

  • $group将所有项目放在一起
  • $project是文档$max的{​​{1}}
  • $$ROOT代表我们得到其bsonsize的当前文档

答案 3 :(得分:1)

如果你正在处理一个庞大的集合,那么将它们一次性加载到内存中是行不通的,因为你需要的RAM大于整个集合的大小才能工作。

相反,您可以使用我创建的以下包批量处理整个集合: https://www.npmjs.com/package/mongodb-largest-documents

您所要做的就是提供MongoDB连接字符串和集合名称。该脚本在完成批量遍历整个集合时将输出前X个最大的文档。

Preview

答案 4 :(得分:1)

嗯..这是一个古老的问题..但是-我想分享我的看法

我的方法-使用Mongo mapReduce函数

首先-让我们获取每个文档的大小

db.myColection.mapReduce
(
   function() { emit(this._id, Object.bsonsize(this)) }, // map the result to be an id / size pair for each document
   function(key, val) { return val }, // val = document size value (single value for each document)
   { 
       query: {}, // query all documents
       out: { inline: 1 } // just return result (don't create a new collection for it)
   } 
)

这将返回所有文档大小,尽管值得一提的是将其保存为集合是一种更好的方法(结果是result字段中的结果数组)

第二个-通过处理此查询来获取文档的最大大小

db.metadata.mapReduce
(
    function() { emit(0, Object.bsonsize(this))}, // mapping a fake id (0) and use the document size as value
    function(key, vals) { return Math.max.apply(Math, vals) }, // use Math.max function to get max value from vals (each val = document size)
    { query: {}, out: { inline: 1 } } // same as first example
)

将为您提供单个结果的值等于最大文档大小

简而言之:

您可能想使用第一个示例并将其输出保存为一个集合(将out选项更改为所需的集合名称)并对其进行进一步的聚合(最大大小,最小大小等)。

-OR-

您可能希望使用单个查询(第二个选项)来获取单个统计信息(最小值,最大值,平均值等)

答案 5 :(得分:0)

使用aggregation framework和有关集合中文档的少量知识,查找MongoDB集合中最大的文档可能比其他答案快100倍。此外,使用其他方法(forEach,甚至更糟,将所有文档发送到客户端),您都可以在几秒钟内得到结果,而分钟则要得到结果。

您需要知道文档中的哪个字段可能是最大的字段-您几乎总是会知道。只有两个实用的 1 MongoDB types可以具有可变大小:

  • 数组
  • 字符串

聚合框架可以计算每个长度。请注意,您不会获得数组的大小(以字节为单位),但会得到元素的长度。但是,通常更重要的是哪些异常文档,而不是确切地占用了多少字节。

这是对数组进行的操作。举例来说,假设我们在一个社交网络中有一个用户集合,并且我们怀疑数组friends.ids可能非常大(实际上,您可能应该将friendsCount之类的单独字段与数组,但为示例起见,我们假设它不可用):

db.users.aggregate([
    { $match: {
        'friends.ids': { $exists: true }
    }},
    { $project: { 
        sizeLargestField: { $size: '$friends.ids' } 
    }},
    { $sort: {
        sizeLargestField: -1
    }},
])

关键是使用$size aggregation pipeline operator。但是它仅适用于数组,那么文本字段呢?我们可以使用$strLenBytes operator。假设我们怀疑bio字段也可能很大:

db.users.aggregate([
    { $match: {
        bio: { $exists: true }
    }},
    { $project: { 
        sizeLargestField: { $strLenBytes: '$bio' } 
    }},
    { $sort: {
        sizeLargestField: -1
    }},
])

您还可以使用$size组合$strLenBytes$sum来计算多个字段的大小。在绝大多数情况下,20% of the fields will take up 80% of the size(如果不是10/90甚至不是1/99),大字段必须是字符串或数组。


1 从技术上讲,很少使用的binData类型也可以具有可变大小。

答案 6 :(得分:0)

Elad Nana's package的启发,但可在MongoDB控制台中使用:

function biggest(collection, limit=100, sort_delta=100) {
  var documents = [];
  cursor = collection.find().readPref("nearest");
  while (cursor.hasNext()) {
    var doc = cursor.next();
    var size = Object.bsonsize(doc);
    if (documents.length < limit || size > documents[limit-1].size) {
      documents.push({ id: doc._id.toString(), size: size });
    }
    if (documents.length > (limit + sort_delta) || !cursor.hasNext()) {
      documents.sort(function (first, second) {
        return second.size - first.size;
      });
      documents = documents.slice(0, limit);
    }
  }
  return documents;
}; biggest(db.collection)
  • 使用光标
  • 提供limit个最大文档的列表,而不仅仅是最大的
  • limit将输出列表排序并剪切到sort_delta
  • nearest用作read preference(如果您在从属节点上,则可能还希望在连接上使用rs.slaveOk()以便能够列出集合)

答案 7 :(得分:-1)

正如 Xavier Guihot 已经提到的,Mongo 4.4 中引入了一个新的 $bsonSize 聚合运算符,它可以为您提供对象的大小(以字节为单位)。除此之外,我只想提供我自己的示例和一些统计数据。

用法示例:

// I had an `orders` collection in the following format
[
  {
    "uuid": "64178854-8c0f-4791-9e9f-8d6767849bda",
    "status": "new",
    ...
  },
  {
    "uuid": "5145d7f1-e54c-44d9-8c10-ca3ce6f472d6",
    "status": "complete",
    ...
  },
  ...
];

// and I've run the following query to get documents' size
db.getCollection("orders").aggregate(
  [
    {
      $match: { status: "complete" } // pre-filtered only completed orders
    },
    {
      $project: {
        uuid: 1,
        size: { $bsonSize: "$$ROOT" } // added object size
      }
    },
    {
      $sort: { size: -1 }
    },
  ],
  { allowDiskUse: true } // required as I had huge amount of data
);

结果,我收到了按大小降序排列的文档列表。

统计:

对于总共约 3M 条记录和约 70GB 大小的集合,上述查询耗时约 6.5 分钟。