是否可以在 MongoDB 中找到最大的文档大小?
db.collection.stats()
显示平均尺寸,这并不具有代表性,因为在我的情况下,尺寸可能会有很大差异。
你可以使用一个小的 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);
注意:这将尝试将整个结果集存储在内存中(来自.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" }
在 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
类型也可以具有可变大小。
开始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
s 所有项目放在一起$project
s 的$max
文件'$bsonSize
$$ROOT
表示我们获得 bsonsize 的当前文档嗯..这是一个老问题..但是 - 我想分享我的一分钱
我的方法 - 使用 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
选项更改为您想要的集合名称)并对其应用进一步的聚合(最大大小、最小大小等)
-或者-
您可能希望使用单个查询(第二个选项)来获取单个统计信息(最小值、最大值、平均值等)
如果您正在处理一个庞大的集合,那么一次将其全部加载到内存中是行不通的,因为您需要比整个集合的大小更多的 RAM 才能工作。
相反,您可以使用我创建的以下包批量处理整个集合: https ://www.npmjs.com/package/mongodb-largest-documents
您所要做的就是提供 MongoDB 连接字符串和集合名称。该脚本将在完成批量遍历整个集合时输出前 X 个最大的文档。
受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()
能够列出集合)正如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 分钟。