Можно ли найти наибольший размер документа в MongoDB?
db.collection.stats()
показывает средний размер, который на самом деле не является репрезентативным, поскольку размеры моего корпуса могут значительно различаться.
Можно ли найти наибольший размер документа в MongoDB?
db.collection.stats()
показывает средний размер, который на самом деле не является репрезентативным, поскольку размеры моего корпуса могут значительно различаться.
Вы можете использовать небольшой скрипт оболочки, чтобы получить это значение.
Примечание: при этом будет выполнено полное сканирование таблицы, которое будет медленным в больших коллекциях.
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, попробуйте это. Учитывая коллекцию под названием "запросы":
// 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
и $strLenBytes
используя $sum
для вычисления размера нескольких полей. В подавляющем большинстве случаев 20% полей занимают 80% размера (если не 10/90 или даже 1/99), а большие поля должны быть либо строками, либо массивами.
1 Технически, редко используемый тип binData
также может иметь переменный размер.
Если вы работаете с огромной коллекцией, загрузка ее сразу в память не будет работать, так как вам потребуется больше оперативной памяти, чем размер всей коллекции для работы.
Вместо этого вы можете обрабатывать всю коллекцию партиями, используя следующий пакет, который я создал: https://www.npmjs.com/package/mongodb-largest-documents
Все, что вам нужно сделать, это указать строку подключения и имя коллекции MongoDB. script выведет верхние X самых больших документов, когда он закончит обход всей коллекции партиями.
Ну.. это старый вопрос.. но - я поделился своим мнением об этом
Мой подход - использовать функцию 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
на имя нужной коллекции) и применить к нему дальнейшие агрегаты (максимальный размер, минимальный размер и т.д.)
-или-
Вы можете использовать один запрос (второй вариант) для получения единой статистики (min, max, avg и т.д.)
Вдохновлен пакетом Elad Nana, но может использоваться в консоли 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()
в соединении, чтобы иметь возможность перечислять коллекции, если вы находитесь на подчиненном узле)