Я ищу наиболее эффективный способ индексации / агрегирования большой коллекции телешоу, чтобы получить средние оценки для каждого шоу, которое транслировалось в определенную дату.
В идеале пользователь должен предоставить дату в формате ISO (ГГГГ-ММ-ДД), и агрегация должна возвращать список передач, которые транслировались в этот день, вместе с полем, содержащим их 30-дневные средние.
В настоящее время у меня есть 2 коллекции, Dates
и Shows
. Вот упрощенный пример:
// Dates (provides parent_id to link show records by date)
{ _id: 1, dataDate: 2018-09-01T00:00:00-04:00 }
{ _id: 2, dataDate: 2018-09-02T00:00:00-04:00 }
// Shows
{
parent_id: 1,
name: "SHOW A",
start: 2018-09-01T19:00:00-04:00,
end: 2018-09-01T20:00:00-04:00,
rating: 100
},{
parent_id: 1,
name: "SHOW B",
start: 2018-09-01T20:00:00-04:00,
end: 2018-09-01T21:00:00-04:00,
rating: 150
},{
parent_id: 1,
name: "SHOW C",
start: 2018-09-01T21:00:00-04:00,
end: 2018-09-01T22:00:00-04:00,
rating: 200
}, {
parent_id: 2,
name: "SHOW A",
start: 2018-09-02T19:00:00-04:00,
end: 2018-09-02T20:00:00-04:00,
rating: 100
},{
parent_id: 2,
name: "SHOW B",
start: 2018-09-02T20:00:00-04:00,
end: 2018-09-02T21:00:00-04:00,
rating: 150
},{
parent_id: 2,
name: "SHOW C",
start: 2018-09-02T21:00:00-04:00,
end: 2018-09-02T22:00:00-04:00,
rating: 200
}
Вот мой текущий подход -
- Поиск даты, запрошенной пользователем в
Dates
коллекции
- Использовать идентификатор из записи даты для сопоставления шоу по
parent_id
- Для каждого шоу просмотрите последние 30 эпизодов (на основе имени, времени начала / окончания)
- Сгруппируйте результаты $ lookup, используя $ avg
- Объединить среднее поле с исходной записью шоу
Прямо сейчас это занимает значительное количество времени для завершения. У меня есть 2 индекса на коллекцию, { parent_id: 1 }
и { start: -1, name: 1 }
. Если я удаляю последние 3 этапа 2-го оператора сопоставления (который проверяет название шоу и час начала / окончания), он возвращается почти мгновенно. Однако мне нужно проверить эти переменные, чтобы исключить повторные запуски (показы с тем же именем, которые транслировались в разное время дня) от включения в окончательный результат. Есть ли лучший способ проиндексировать это? Или здесь есть какое-то конкретное утверждение, которое замедляет его?
let dataDate = DateTime.fromISO('2018-09-01').setZone('America/New_York');
let avgDate = DateTime.fromISO('2018-09-01').setZone('America/New_York').minus({ days: 30 });
let parent = Dates.findOne({ dataDate: dataDate.toJSDate() });
db.shows.aggregate([{
$match: {
parent_id: parent._id
}
}, {
$lookup: {
from: 'shows',
let: {
name: '$name',
start: { $hour: { date: '$start', timezone: 'America/New_York' } },
end: { $hour: { date: '$end', timezone: 'America/New_York' } } },
pipeline: [{
$match: {
$expr: {
$and: [
{ $lt: [ '$start', dataDate.toJSDate() ] },
{ $gte: [ '$start', avgDate.toJSDate() ] },
{ $eq: [ '$name', '$$name' ] },
{ $eq: [ { $hour: { date: '$start', timezone: 'America/New_York' } }, '$$start' ] },
{ $eq: [ { $hour: { date: '$end', timezone: 'America/New_York' } }, '$$end' ] },
]
}
}
}, {
$group: {
_id: null,
averageRating: { $avg: `$$rating` }
}
}],
as: 'average'
}
}, {
$replaceRoot: { newRoot: { $mergeObjects: [{ $arrayElemAt: ['$average', 0] }, "$$ROOT"] } }
}, {
$project: {
channel_id: 1,
start: 1,
end: 1,
todayRating: `$$rating`,
averageRating: 1,
name: 1,
}
}])