MongoDB: динамические подсчеты

#node.js #mongodb #mongoose #aggregation-framework

#node.js #mongodb #мангуст #агрегация-фреймворк

Вопрос:

У меня есть две коллекции. Коллекция «пользователи» и коллекция «события». В коллекции событий есть первичный ключ, который указывает, какому пользователю принадлежит событие.

Я хотел бы подсчитать, сколько событий у пользователя соответствует определенному условию.

В настоящее время я выполняю это следующим образом:

 
db.users.find({ usersMatchingACondition }).forEach(user => {

  const eventCount = db.events.find({
    title: 'An event title that I want to find',
    userId: user._id
  }).count();

  print(`This user has ${eventCount} events`);

});

  

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

С 10 000 пользователями — это, очевидно, создает 10 000 запросов, и я думаю, что это можно было бы сделать намного эффективнее!

Я предполагаю, что это легко с помощью какого-то агрегированного запроса, но я не знаком с синтаксисом и изо всех сил пытаюсь разобраться в этом.

Любая помощь была бы высоко оценена!

Ответ №1:

Вам нужен $ lookup, чтобы получить данные из events сопоставления user_id . Затем вы можете использовать $filter для применения вашего условия на уровне события, а для получения подсчета вы можете использовать оператор $size

 db.users.aggregate([
    {
        $match: { //users matching condition }
    },
    {
        $lookup:
        {
            from: 'events',
            localField: '_id', //your "primary key"
            foreignField: 'user_id',
            as: 'user_events'
        }
    },
    {
        $addFields: {
            user_events: {
                $filter: {
                    input: "$user_events",
                    cond: {
                        $eq: [
                            '$$this.title', 'An event title that I want to find'
                        ]
                    }
                }
            }
        }
    },
    {
        $project: {
            _id: 1,
            // other fields you want to retrieve: 1,
            totalEvents: { $size: "$user_events" }
        }
    }
])
  

Ответ №2:

Без aggregate не так много оптимизации, но поскольку вы специально сказали, что

Во-первых, вместо

 const eventCount = db.events.find({
    title: 'An event title that I want to find',
    userId: user._id
  }).count();
  

Делать

 const eventCount = db.events.count({
    title: 'An event title that I want to find',
    userId: user._id
  });
  

Это значительно ускорит ваши запросы, потому что запрос find фактически сначала извлекает документы, а затем выполняет подсчет.

Для возврата массива вы можете просто инициализировать массив в начале и поместить в него объекты {userid: id, count: eventCount}.