Найти наибольший размер документа в MongoDB - программирование
Подтвердить что ты не робот

Найти наибольший размер документа в MongoDB

Можно ли найти наибольший размер документа в MongoDB?

db.collection.stats() показывает средний размер, который на самом деле не является репрезентативным, поскольку размеры моего корпуса могут значительно различаться.

4b9b3361

Ответ 1

Вы можете использовать небольшой скрипт оболочки, чтобы получить это значение.

Примечание: при этом будет выполнено полное сканирование таблицы, которое будет медленным в больших коллекциях.

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);

Ответ 2

Примечание. Это попытается сохранить весь набор результатов в памяти (от .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" }

Ответ 3

Поиск самых больших документов в коллекции 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 также может иметь переменный размер.

Ответ 4

Если вы работаете с огромной коллекцией, загрузка ее сразу в память не будет работать, так как вам потребуется больше оперативной памяти, чем размер всей коллекции для работы.

Вместо этого вы можете обрабатывать всю коллекцию партиями, используя следующий пакет, который я создал: https://www.npmjs.com/package/mongodb-largest-documents

Все, что вам нужно сделать, это указать строку подключения и имя коллекции MongoDB. script выведет верхние X самых больших документов, когда он закончит обход всей коллекции партиями.

Предварительный просмотр

Ответ 5

Ну.. это старый вопрос.. но - я поделился своим мнением об этом

Мой подход - использовать функцию 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 и т.д.)

Ответ 6

Вдохновлен пакетом 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() в соединении, чтобы иметь возможность перечислять коллекции, если вы находитесь на подчиненном узле)