Как сгруппировать две коллекции в одном запросе с помощью MongoDB или Mongoose

#javascript #node.js #mongodb #mongoose

#javascript #node.js #mongodb #mongoose

Вопрос:

У меня есть такая страница. И данные с моего сервера MongoDB.

введите описание изображения здесь

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

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

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

Коллекция пользователей

 {
    "_id": {
        "$oid": "5f7e92b88dc8f64cb4e6c2c0"
    },
    "login": "mojombo",
    "id": 1,
    "avatar_url": "https://avatars0.githubusercontent.com/u/1?v=4",
    "url": "https://api.github.com/users/mojombo",
    "html_url": "https://github.com/mojombo",
    "type": "User",
    "site_admin": "false",
    "name": "Tom Preston-Werner",
    "company": null,
    "blog": "http://tom.preston-werner.com",
    "location": "San Francisco",
    "email": "tom@mojombo.com",
    "hireable": null,
    "bio": null,
    "created_at": {
        "$date": "2007-10-20T05:24:19.000Z"
    },
    "updated_at": {
        "$date": "2020-09-22T15:50:44.000Z"
    },
    "registerDate": {
        "$date": "2020-10-08T04:16:56.459Z"
    },
    "__v": 0
}
  

Коллекция счетчиков

 {
    "_id": {
        "$oid": "5f8e5bde9054ba2477dc2c57"
    },
    "repoName": "ale",
    "repoNumber": 171780764,
    "userName": "technicalpickles",
    "userNumber": 159,
    "viewDate": "2020-10-20",
    "count": 1
}
  

В коллекции счетчиков мне нужно только имя пользователя, поле count для вычисления данных счетчика.

Итак, я пытаюсь использовать цикл и агрегированный метод, но проблема в том, что пользовательских данных слишком много, поэтому мне приходится отправлять слишком много запросов на сервер. (Если у меня есть данные 10000 пользователей, я должен отправить 10000 запросов) Итак, я не должен использовать метод цикла for.

 router.get(`/sitemap/0`, async (req, res, next) => {
  let name;
  let dataArray = [];
  let pageNumber = (Number(req.params.page) * 10000); // Current Page Number
  let nextPage = (Number(req.params.page)   1) * 10000; // Next Page Number
  let pageResu< // Page Result
  try {
    let users = await User.find({}, 'login id').limit(1000)
    for (let i = 0; i < 1000; i  ) {
      name = users[i].login
      let counters = await Counter.aggregate([{
          $match: {
            id: users.id,
            userName: users[i].login
          }
        },
        {
          $group: {
            _id: `${users[i].login}`,
            count: {
              $sum: "$count"
            }
          }
        }
      ])
      dataArray.push(counters)
      console.log(counters)
    }
    console.log(dataArray)

  } catch (e) {
    // throw an error
    throw e;
  }
  res.render("sitemap", {
    dataArray
  })
});
  

Результат кодов

введите описание изображения здесь

Итак, я хочу отправить один запрос, и я думаю, что он должен использовать метод ‘aggregate’. Но без «цикла for».

Я хочу присоединиться Users collection , Counters collection но я слышал, что в MongoDB и Mongoose нет функции объединения.

Я просто хочу, чтобы результат был таким. Есть ли способ сделать это так?

 {
    "_id": {
        "$oid": "5f7e92b88dc8f64cb4e6c2c0"
    },
    "login": "mojombo", --------------Match login with counter collection 'userName' value
    "id": 1,  ------------------------Match id with counter collection 'id' value
    "count": [Sum of counters count data and it should be Number] ------- Only this field is added
    "avatar_url": "https://avatars0.githubusercontent.com/u/1?v=4",
    "url": "https://api.github.com/users/mojombo",
    "html_url": "https://github.com/mojombo",
    "type": "User",
    "site_admin": "false",
    "name": "Tom Preston-Werner",
    "company": null,
    "blog": "http://tom.preston-werner.com",
    "location": "San Francisco",
    "email": "tom@mojombo.com",
    "hireable": null,
    "bio": null,
    "created_at": {
        "$date": "2007-10-20T05:24:19.000Z"
    },
    "updated_at": {
        "$date": "2020-09-22T15:50:44.000Z"
    },
    "registerDate": {
        "$date": "2020-10-08T04:16:56.459Z"
    },
    "__v": 0
}
  

Ответ №1:

  • $lookup при сборе счетчиков с использованием этапа конвейера, позвольте передать 2 поля id, login для сопоставления с коллекцией счетчиков
  • $match условия обоих полей
  • $addFields для count суммирования поля count из массива counters, используя $reduce цикл итерации и $add суммирования значений поля count
  • $skip для разбивки на страницы
  • $limit передайте свой лимит документов
 let page = 1; // start pagination from first
let limit = 1000;
let skip = (page - 1) * limit;
let users = await User.aggregate([
  {
    $lookup: {
      from: "counters",
      let: {
        id: "$id",
        login: "$login"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: ["$userName", "$$login"],
              $eq: ["$userNumber", "$$id"]
            }
          }
        }
      ],
      as: "count"
    }
  },
  {
    $addFields: {
      count: {
        $reduce: {
          input: "$count",
          initialValue: 0,
          in: { $add: ["$$value", "$$this.count"] }
        }
      }
    }
  },
  { $skip: skip },
  { $limit: limit }
])
  

Игровая площадка

Комментарии:

1. Спасибо! Есть одно неправильное имя переменной, но я это исправлю! И это, наконец, работает! Я также редактирую новые коды Playground.