64

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

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

4

8 回答 8

105

你可以使用一个小的 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);
于 2013-06-06T08:43:55.500 回答
24

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

如果你还想要 _id,试试这个。给定一个名为 "requests" 的集合:

// 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" }
于 2014-01-28T21:43:54.517 回答
15

在 MongoDB 集合中查找最大的文档可能比使用聚合框架的其他答案快约 100 倍,并且对集合中的文档有一点了解。此外,您将在几秒钟内获得结果,而其他方法则需要几分钟(forEach或更糟的是,将所有文档都发送给客户端)。

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

  • 数组
  • 字符串

聚合框架可以计算每个的长度。请注意,您不会获得数组的字节大小,而是元素的长度。然而,更重要的是异常文档是哪些,而不是它们占用了多少字节。

这是对数组的处理方式。例如,假设我们在社交网络中有一组用户,并且我们怀疑数组friends.ids可能非常大(实际上,您可能应该保持一个单独的字段friendsCount与数组同步,但为了举例,我们'会假设它不可用):

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

关键是使用$size聚合管道操作符。它只适用于数组,那么文本字段呢?我们可以使用$strLenBytes操作符。假设我们怀疑该bio字段也可能非常大:

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

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


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

于 2019-05-08T20:29:47.987 回答
12

开始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 }

这个:

  • $groups 所有项目放在一起
  • $projects 的$max文件'$bsonSize
  • $$ROOT表示我们获得 bsonsize 的当前文档
于 2020-03-15T15:23:47.270 回答
3

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

我的方法 - 使用 MongomapReduce函数

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

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选项更改为您想要的集合名称)并对其应用进一步的聚合(最大大小、最小大小等)

-或者-

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

于 2019-08-08T10:47:03.847 回答
2

如果您正在处理一个庞大的集合,那么一次将其全部加载到内存中是行不通的,因为您需要比整个集合的大小更多的 RAM 才能工作。

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

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

预习

于 2016-05-07T14:41:04.053 回答
-1

Elad Nana 的 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用作读取首选项(如果您在从节点上,您可能还希望在连接上使用以便rs.slaveOk()能够列出集合)
于 2019-05-17T09:15:34.940 回答
-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 分钟。

于 2021-01-29T16:06:12.860 回答