MongoDB - перестроить документ, сохранить последний элемент массива - PullRequest
0 голосов
/ 28 мая 2020

Пример документа https://pastebin.com/x2kVUnP3

Понимание данных

Количество каждого массива полей такое же, как и fetch_dates, поэтому, если мы хотим получить набор данных для 2019-06-07 05: 34: 29 он вернет все внутренние поля $arrayElemAt = 1 для последнего результата это будет -1 для соответствующего поля.

Data relation

Желаемый результат

Основная идея состоит в том, чтобы перестроить все документы и сохранить только последние fetch_date и last значение для каждого поля. В этом случае просто сохраните оранжевую линию изображения.

Вопрос

В основном я понятия не имел, с чего начать или как начать, и если это возможно делать. Каждый документ может иметь разные размеры массива (fetch_dates), но структура одинакова. Необходимо обработать 225 тыс. Документов, средний размер 2,5 КБ. Приветствуются любые подсказки.

Моя первоначальная идея

Я думал получить все поля и его последние элементы через сценарий PHP, что-то вроде перебора всех документов сначала, затем по всем полям и значению $ project $arrayElemAt => [ $field3.field3_1, -1 ] Я полагаю, что это плохая идея.

Я использую PHP - Laravel, но я могу преобразовать запрос, чтобы он работал есть.

1 Ответ

1 голос
/ 07 июня 2020

Вот решение исключительно из оболочки MongoDB.

Мое решение основано на конвейере агрегирования и использовании операции проекта . В своем вопросе вы сказали:

Каждый документ может иметь разные размеры массива (fetch_dates), но структура такая же.

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

function buildProjection(doc, prepend) {
  var projection = {};

  Object.keys(doc)
    .forEach(key => {
      var val = doc[key];
      var path = prepend==null? key : prepend + '.' + key;

      if (key == '_id') {
        projection[key] = 1;

      } else if ( Array.isArray(val) ) {
        projection[key] = { '$slice' : [ '$'+path, -1 ] };

      } else if ( typeof val === 'object' && val !== null ) {
        projection[key] = buildProjection(val, path);

      } else {
        projection[key] = 1;
      }
    });

    return projection;
}

/*
 * Pull a document out of the database and build the projection based off of it.
 * You may want to specify a particular document in the findone
 * that you know to be structured correctly
*/
var sample = db.myCollection.findOne({});
var projection = buildProjection(sample, null);

db.myCollection.aggregate([
  // apply the build projection
  { $project: projection },
  // insert results into another collection
  { $out: 'rebuiltWithLatest' }
]);

Я не был уверен, хотите ли вы сохранить результаты в другой коллекции. Я сделал это с помощью этого решения. Это заняло несколько секунд, но у меня не было проблем с запуском этого с 300 тыс. Документов, которые были очень похожи на ваш связанный пример.

Если вы только sh для просмотра документов, удалите операцию $ output из конвейер агрегации. Затем он вернет объект курсора, который вы можете перебирать, чтобы увидеть дополнительные результаты.

...