MongoDB查询获取具有外部链接文档计数的文档列表
我有一个mongodb数据库,其中包含的收集文档大致如下:MongoDB查询获取具有外部链接文档计数的文档列表,mongodb,nosql,mongodb-query,aggregation-framework,nosql-aggregation,Mongodb,Nosql,Mongodb Query,Aggregation Framework,Nosql Aggregation,我有一个mongodb数据库,其中包含的收集文档大致如下: //用户文档 { _id:$oid, 姓名:“姓名”, 说明:“说明”。 // ... } //图书文献 { _id:$oid, 用户ID:“…” 姓名:“姓名”, 描述:“描述” // ... } //页面文档 { _id:$oid, 书号:“…” 姓名:“姓名”, 描述:“描述” // ... } 一个用户有很多书,一本书有很多页。每个实体都是一个单独文档的原因是,用户可以拥有数千本书,一本书可以拥有数千页,因此,如果所有内容都在
//用户文档
{
_id:$oid,
姓名:“姓名”,
说明:“说明”。
// ...
}
//图书文献
{
_id:$oid,
用户ID:“…”
姓名:“姓名”,
描述:“描述”
// ...
}
//页面文档
{
_id:$oid,
书号:“…”
姓名:“姓名”,
描述:“描述”
// ...
}
一个用户有很多书,一本书有很多页。每个实体都是一个单独文档的原因是,用户可以拥有数千本书,一本书可以拥有数千页,因此,如果所有内容都在一个文档中,我们可以很容易地达到16MB的限制
使用每本书的pageCount
字段为指定的userId
检索图书列表的最佳方法是什么
这是我需要的json结果
{
书籍:[{
_id:$oid,
名称:“名称1”,
description:“description1”,
页数:8
}, {
_id:$oid,
名称:“名称2”,
description:“description2”,
页数:12
},
// ...
]
}
对于SQL数据库,连接计数非常简单,但对于mongodb,除了进行单独的查询以获取书籍列表,然后获取每本书的页数之外,我看不到任何简单的解决方案。您可以使用聚合框架中的
$lookup
阶段:
db.Users.aggregate([
{$match: {_id: userId}},
{$lookup: {
from: "Book",
localField: "userId",
foreignField: "_id",
as: "book"
}},
{$lookup: {
from: "Page",
localField: "bookId",
foreignField: "book._id",
as: "page"
}}
])
并添加stage
$group
以计算页数。但我认为这个查询会很慢。如果您想在以后或已经这样做的情况下对集合进行切分,则不能使用$lookup您可以使用聚合框架中的$lookup
阶段:
db.Users.aggregate([
{$match: {_id: userId}},
{$lookup: {
from: "Book",
localField: "userId",
foreignField: "_id",
as: "book"
}},
{$lookup: {
from: "Page",
localField: "bookId",
foreignField: "book._id",
as: "page"
}}
])
并添加stage
$group
以计算页数。但我认为这个查询会很慢。如果您想在MongoDB的聚合框架中使用$lookup,或者如果您已经使用了$lookup,那么您就不能使用$lookup,有一个名为的管道阶段,它允许您对同一数据库中的另一个集合进行左外部联接,以从“联接”中筛选文档收集以供处理
因此,使用这种武器,您可以运行聚合管道操作,将图书集合连接到页面集合
在管道步骤中,您可以通过从“连接”查询结果数组的大小来获得pageCount
假设您的MongoDB服务器版本至少为3.4,请考虑运行以下聚合操作以获得所需的结果:
db.books.aggregate([
{ "$match": { "userId": userId } },
{ "$lookup": {
"from": "pages",
"localField": "_id",
"foreignField": "bookId",
"as": "pageCount"
}},
{ "$addFields": {
"pageCount": { "$size": "$pageCount" }
}}
])
或者,您可以从
用户集合运行管道,如下所示:
db.user.aggregate([
{ "$match": { "_id": userId } },
{ "$lookup": {
"from": "books",
"localField": "_id",
"foreignField": "userId",
"as": "books"
}},
{ "$lookup": {
"from": "pages",
"localField": "books._id",
"foreignField": "bookId",
"as": "pages"
}},
{ "$addFields": {
"books": {
"$map": {
"input": "$books",
"as": "book",
"in": {
"name": "$$book.name",
"description": "$$book.description",
"pageCount": { "$size": "$$book.pages" }
}
}
}
}}
])
使用MongoDB的聚合框架,有一个称为的管道阶段,它允许您对同一数据库中的另一个集合进行左外部联接,以过滤“联接”集合中的文档进行处理
因此,使用这种武器,您可以运行聚合管道操作,将图书集合连接到页面集合
在管道步骤中,您可以通过从“连接”查询结果数组的大小来获得pageCount
假设您的MongoDB服务器版本至少为3.4,请考虑运行以下聚合操作以获得所需的结果:
db.books.aggregate([
{ "$match": { "userId": userId } },
{ "$lookup": {
"from": "pages",
"localField": "_id",
"foreignField": "bookId",
"as": "pageCount"
}},
{ "$addFields": {
"pageCount": { "$size": "$pageCount" }
}}
])
或者,您可以从用户集合运行管道,如下所示:
db.user.aggregate([
{ "$match": { "_id": userId } },
{ "$lookup": {
"from": "books",
"localField": "_id",
"foreignField": "userId",
"as": "books"
}},
{ "$lookup": {
"from": "pages",
"localField": "books._id",
"foreignField": "bookId",
"as": "pages"
}},
{ "$addFields": {
"books": {
"$map": {
"input": "$books",
"as": "book",
"in": {
"name": "$$book.name",
"description": "$$book.description",
"pageCount": { "$size": "$$book.pages" }
}
}
}
}}
])
它没有直接回答这个问题,而是给出了一些关于
进行单独查询以获取书籍列表,然后获取每本书的页数
部分。这并不总是一件坏事。MunGDB在简单查询中非常有效,所以我给您一些数字来考虑单个查找管道与多个查询的性能,并鼓励您测试数据集上的典型查询。如果你不同时需要所有的数据,分页会产生巨大的影响
设置
一个由100个用户X 1000本书X 1000页组成的小型数据库,每个页面位于一个小小的1 vCPU/2 GB内存/50 GB磁盘/LON1上-16.04 droplet上的Ubuntu MongoDB 3.4.10
页面
集合创建如下:
for USERID in {1..100}; do
echo "" > pages.json;
for BOOKID in {1..1000}; do
./node_modules/.bin/mgeneratejs "{\"bookId\": \"$USERID-$BOOKID\", \"name\": {\"\$sentence\":{\"words\":3}}, \"description\": \"\$paragraph\"}" -n 1000 >> pages.json
done
cat pages.json | mongoimport -d so -c pages
done
而书籍
一本几乎是一样的
基本统计数据:
db.books.stats(1024*1024)
"ns" : "so.books",
"size" : 50,
"count" : 100000,
"avgObjSize" : 533,
"storageSize" : 52,
"nindexes" : 2,
"totalIndexSize" : 1,
"indexSizes" : {
"_id_" : 0,
"userId_1" : 0
},
db.pages.stats(1024*1024)
"ns" : "so.pages",
"size" : 51673,
"count" : 100000000,
"avgObjSize" : 541,
"storageSize" : 28920,
"nindexes" : 2,
"totalIndexSize" : 1424,
"indexSizes" : {
"_id_" : 994,
"bookId_1" : 430
},
$lookup
来自@chridam答案的管道
db.books.aggregate([
{ "$match": { "userId": 18 } },
{ "$lookup": {
"from": "pages",
"localField": "_id",
"foreignField": "bookId",
"as": "pageCount"
}},
{ "$addFields": {
"pageCount": { "$size": "$pageCount" }
}}
])
快速响应:
"op" : "command",
"command" : {
"aggregate" : "books"
},
"keysExamined" : 1000,
"docsExamined" : 1000,
"nreturned" : 101,
"responseLength" : 57234,
"millis" : 1028
对于前100个文档,允许您在一秒钟内开始处理文档
整个事件的总时间:
db.books.aggregate([
{ "$match": { "userId": 18 } },
{ "$lookup": {
"from": "pages",
"localField": "_id",
"foreignField": "bookId",
"as": "pageCount"
}},
{ "$addFields": {
"pageCount": { "$size": "$pageCount" }
}}
]).toArray()
再增加8秒:
"op" : "getmore",
"query" : {
"getMore" : NumberLong("32322423895"),
"collection" : "books"
},
"keysExamined" : 0,
"docsExamined" : 0,
"nreturned" : 899,
"responseLength" : 500060,
"millis" : 8471
检索所有数据的总时间超过9秒
多重查询
检索书籍:
let bookIds = [];
db.books.find({userId:12}).forEach(b=>{bookIds.push(b._id);});
在10毫秒内填充阵列:
"op" : "query",
"query" : {
"find" : "books",
"filter" : {
"userId" : 34
}
},
"keysExamined" : 101,
"docsExamined" : 101,
"nreturned" : 101,
"responseLength" : 54710,
"millis" : 3
及
计算页数:
db.pages.aggregate([
{ $match: { bookId: { $in: bookIds } } },
{ $group: { _id: "$bookId", cnt: { $sum: 1 } } }
]).toArray()
总共需要1.5秒:
"op" : "command",
"command" : {
"aggregate" : "pages"
},
"keysExamined" : 1000001,
"docsExamined" : 0,
"nreturned" : 101,
"responseLength" : 3899,
"millis" : 1574
及
合并结果
不是查询,但应在应用程序级别执行。mongoshell javascript只需几毫秒,这使得检索所有数据的总时间不到2秒
它没有直接回答这个问题,而是给出了一些关于
进行单独查询以获取书籍列表,然后获取每本书的页数
部分。这并不总是一件坏事。MunGDB在简单查询中非常有效,所以我给您一些数字来考虑单个查找管道与多个查询的性能,并鼓励您测试数据集上的典型查询。如果你不同时需要所有的数据,分页会产生巨大的影响
设置
一个由100个用户X 1000本书X 1000页组成的小型数据库,每个页面位于一个小小的1 vCPU/2 GB内存/50 GB磁盘/LON1上-16.04 droplet上的Ubuntu MongoDB 3.4.10
页面
集合创建如下:
for USERID in {1..100}; do
echo "" > pages.json;
for BOOKID in {1..1000}; do
./node_modules/.bin/mgeneratejs "{\"bookId\": \"$USERID-$BOOKID\", \"name\": {\"\$sentence\":{\"words\":3}}, \"description\": \"\$paragraph\"}" -n 1000 >> pages.json
done
cat pages.json | mongoimport -d so -c pages
done
还有boo