Быстрый подсчет вложенных документов MongoDB - возможно, через индекс - PullRequest
0 голосов
/ 12 июля 2020

Я использую MongoDB 4.0 в кластере mongoDB Atlas (3 реплики - 1 осколок).

Предполагается, что у меня есть коллекция, содержащая несколько документов.

Каждый из этих документов содержит массив из поддокументов, представляющих города в определенном году с дополнительной информацией. Пример документа будет выглядеть так (я удалил ненужную информацию, чтобы упростить пример):

{_id:123,
 cities:[
  {name:"vienna",
   year:1985
  },
  {name:"berlin",
   year:2001
  }
  {name:"vienna",
   year:1985
  }
]}

У меня есть составной индекс по и год. Каков самый быстрый способ подсчета появления комбинаций имени и года?

Я уже пробовал следующую агрегацию:

[{$unwind: {
  path: '$cities'
}}, {$group: {
  _id: {
    name: 'cities.name',
    year: '$cities.year'
  },
  count: {
    $sum: 1
  }
}}, {$project: {
  count: 1,
  name: '$_id.name',
  year: '$_id.year',
  _id: 0
}}]

Другой подход, который я пробовал, - это сокращение карты в следующей форме - уменьшение карты выполняется немного лучше ~ на 30% меньше времени.

функция карты:

function m() {
    for (var i in this.cities) {
        emit({
                name: this.cities[i].name,
                year: this.cities[i].year
            },
            1);
    }
}

функция уменьшения (также пытались заменить сумму на длину, но на удивление сумма работает быстрее) :

function r(id, counts) {
    return Array.sum(counts);
}

вызов функции в mongoshell:

db.test.mapReduce(m,r,{out:"mr_test"})

Теперь я спрашивал себя - можно ли получить доступ к индексу? Насколько я знаю, это дерево B +, которое содержит указатели на соответствующие документы на диске, поэтому с технической точки зрения, я думаю, можно было бы перебирать все листья дерева индекса и просто подсчитывать указатели? Кто-нибудь, если это возможно?

Кто-нибудь знает другой способ решить этот подход с высокой производительностью? (Невозможно изменить дизайн из-за других зависимостей программного обеспечения, мы запускаем его на очень большом наборе данных). Может быть, у кого-нибудь есть опыт решения такой задачи с помощью шардов?

1 Ответ

1 голос
/ 17 июля 2020

Индекс не будет очень полезным в этой ситуации.

Индексы MongoDB были разработаны для идентификации документов, соответствующих заданным критериям.

Если вы создаете индекс на {cities.name:1, cities.year:1}

Этот документ:

{_id:123,
 cities:[
  {name:"vienna",
   year:1985
  },
  {name:"berlin",
   year:2001
  }
  {name:"vienna",
   year:1985
  }
]}

Будет иметь 2 записи в b-дереве, которые ссылаются на этот документ:

vienna|1985
berlin|2001

Даже если бы можно было подсчитать частоту возникновения указанному c ключу в индексе, это не обязательно соответствует.

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

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

...