javascript - 如何使用 MongoDB 或 Mongoose 在一个查询中对两个集合进行分组
问题描述
我有这样的页面。数据来自我的 MongoDB 服务器。
首先,我创建用户集合并放置这些用户数据。但是我添加了Counter 集合,因为我想显示他们的View Count。
每个用户有多个页面,View Count数据是 Counter 数据的总和。
用户集合看起来像这样,我只需要登录、id(唯一)数据。
用户集合
{
"_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
}
在 Counters 集合中,我只需要userName、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 pageResult; // 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
})
});
代码结果
所以我想发送单个查询,我认为应该使用“聚合”方法。但没有'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
}
解决方案
$lookup
使用管道阶段使用计数器收集,让传递 2 个字段id, login
以匹配计数器收集$match
两个字段条件$addFields
对count
计数器数组中的计数字段求和,$reduce
用于迭代循环并对$add
计数字段的值求和$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 }
])
推荐阅读
- python - 如何运行 g:profiler
- java - 使用 SimpleDateFormat Android 无法解析的日期
- android - Maps v3.0.0 Beta 的 Proguard 规则
- swift - 如何过滤我的表格视图的内容?
- android - 是否可以在凸起的按钮和 texfield 之间建立连接?
- python - 执行 Python 脚本时来自 Rsyslog 的执行格式错误
- ios - 迅捷;ViewController 从未取消初始化
- sql - 写入以输出所有更新的 ID
- swift - Swift 展开问题,视图控制器堆栈未更新
- python - 在 Tkinter 中上传文件后自动关闭窗口