视图
MongoDB视图是一个可查询对象,其内容由其他集合或视图上的 聚合管道定义。 MongoDB不会将视图内容持久保存到磁盘。当客户端查询视图时,会按需计算视图的内容。 MongoDB可以要求客户端拥有查询视图的权限。 MongoDB不支持对视图执行写入操作。
例如,您可以:
在员工数据集合上创建视图,以
exclude
任何私有或个人信息 (PII)。 应用程序可以在该视图中查询不包含任何 PII 的员工数据。在收集的传感器数据集合上创建
add
计算字段和指标的视图。 应用程序可以使用简单的查找操作来查询数据。创建一个视图,用于
joins
分别包含库存和订单历史记录的两个集合。 应用程序可以查询联接的数据,而无需管理或了解底层的复杂管道。
您可以在用户界面中为 MongoDB Atlas 托管的部署创建物化视图。
当客户端查询视图时, MongoDB会将客户端查询附加到根本的管道,并将组合管道的结果返回给客户端。 MongoDB可能会将聚合管道优化应用组合管道。
注意
以下页面将讨论视图。 有关按需物化视图的讨论,请参阅按需物化视图。
创建视图
要创建或定义视图,请执行以下操作:
使用
db.createCollection()
方法或create
命令:db.createCollection( "<viewName>", { "viewOn" : "<source>", "pipeline" : [<pipeline>], "collation" : { <collation> } } ) 使用
db.createView()
方法:db.createView( "<viewName>", "<source>", [<pipeline>], { "collation" : { <collation> } } )
注意
— 必须在与源collection相同的数据库中创建视图。
警告
请勿尝试创建名称为system.profile
的时间序列集合或视图,否则 MongoDB Server 会崩溃。
行为
视图表现出以下行为:
只读
视图为只读;对视图进行写入操作会出错。
以下读取操作可以支持视图:
索引使用和排序操作
视图使用底层集合的索引。
由于索引位于底层collection上,因此您无法直接在视图上创建、删除或重新构建索引,也无法获取视图上的索引列表。
从 MongoDB 4.4 开始,对视图运行 命令时,可以指定
$natural
find
排序。以前版本的 MongoDB 不支持对视图进行$natural
排序。对于阻塞排序和阻塞分组操作,视图的底层聚合管道受到 100 MB 内存限制。 从 MongoDB 4.4 开始,您可以对视图发出带有
allowDiskUse: true
的find
命令,以允许 MongoDB 使用临时文件进行阻塞排序和分组操作。在 MongoDB 4.4 之前,只有
aggregate
命令接受allowDiskUse
选项。
投影限制
不可变名称
您无法重命名视图。
视图创建
视图是在读取操作期间按需计算的,MongoDB 将对视图执行读取操作作为底层聚合管道的一部分。 因此,视图不支持以下操作:
如果用于创建视图的聚合管道抑制
_id
字段,则视图中的文档没有_id
字段。
查询视图时,:
查询
filter
、projection
、sort
、skip
、limit
和db.collection.find()
的其他操作会转换为等效的聚合管道阶段。转换后的聚合管道阶段将添加到视图的聚合管道的末尾。 这不会修改视图的底层管道,该管道是在创建视图时设置的。
聚合管道优化器会重塑视图聚合管道阶段以提高性能。 这不会更改查询结果。
分片视图
如果视图的底层集合已分片,则视图也被视为已分片。 因此,您无法在$lookup
和$graphLookup
操作中为from
字段指定分片视图。
视图和排序规则
您可以在创建视图时为其指定默认排序规则。如果未指定排序规则,则视图的默认排序规则是“简单”二进制比较排序规则。也就是说,视图不会继承集合的默认排序规则。
视图上的字符串比较使用的是视图的默认排序规则。尝试更改或覆盖视图默认排序规则的操作会失败并报错。
如果从另一个视图创建视图,则无法指定与源视图不同的排序规则。
如果执行的聚合涉及多个视图,例如使用
$lookup
或$graphLookup
,则这些视图必须采用相同的排序规则。
快照隔离
视图不维护集合更改的时间戳,也不支持时间点或快照读取隔离。
公共视图定义
列出collection的操作(例如db.getCollectionInfos()
和db.getCollectionNames()
)在其输出中包含视图。
重要
视图定义是公开的;即视图上的 db.getCollectionInfos()
和 explain
操作将包括定义视图的管道。因此,应避免在视图定义中直接引用敏感字段和值。
删除视图
要删除视图,请对视图执行 db.collection.drop()
方法。
修改视图
您可以通过删除并重新创建视图或使用collMod
命令来修改视图。
支持的操作
以下操作为视图提供支持,但本页提到的限制除外:
命令 | 方法 |
---|---|
示例
创建要在以下示例中使用的students
collection:
db.students.insertMany( [ { sID: 22001, name: "Alex", year: 1, score: 4.0 }, { sID: 21001, name: "bernie", year: 2, score: 3.7 }, { sID: 20010, name: "Chris", year: 3, score: 2.5 }, { sID: 22021, name: "Drew", year: 1, score: 3.2 }, { sID: 17301, name: "harley", year: 6, score: 3.1 }, { sID: 21022, name: "Farmer", year: 1, score: 2.2 }, { sID: 20020, name: "george", year: 3, score: 2.8 }, { sID: 18020, name: "Harley", year: 5, score: 2.8 }, ] )
使用 db.createView() 创建视图
使用 db.createView()
创建仅限一年级学生的视图:
db.createView( "firstYears", "students", [ { $match: { year: 1 } } ] )
在示例中:
firstYears
是新视图的名称。students
是视图所依据的集合。$match
是一个聚合表达式,它会与students
集合中的一年级学生进行匹配。
此示例查询视图:
db.firstYears.find({}, { _id: 0 } )
以下输出仅包含具有一年级学生数据的文档。{ _id: 0 }
投影会抑制输出中的 _id
字段。
[ { sID: 22001, name: 'Alex', year: 1, score: 4 }, { sID: 22021, name: 'Drew', year: 1, score: 3.2 }, { sID: 21022, name: 'Farmer', year: 1, score: 2.2 } ]
使用 db.createCollection() 创建视图
使用 db.createCollection()
方法可以创建具有特定选项的集合或视图。
以下示例将创建一个 graduateStudents
视图。该视图仅包含由 $match
阶段选择的文档。排序规则设置(可选)决定了排序顺序。
db.createCollection( "graduateStudents", { viewOn: "students", pipeline: [ { $match: { $expr: { $gt: [ "$year", 4 ] } } } ], collation: { locale: "en", caseFirst: "upper" } } )
以下示例将查询该视图。为清晰起见,$unset
阶段会从输出中删除 _id
字段。
db.graduateStudents.aggregate( [ { $sort: { name: 1 } }, { $unset: [ "_id" ] } ] )
对输出进行排序时,$sort
阶段使用排序规则对小写字母前的大写字母进行排序。
[ { sID: 18020, name: 'Harley', year: 5, score: 2.8 }, { sID: 17301, name: 'harley', year: 6, score: 3.1 } ]
使用视图连接两个集合
使用$lookup
创建两个集合的视图,然后对该视图运行查询通常很方便。 应用程序可以查询视图,而无需构建或维护复杂的管道。
创建两个样本集合 inventory
和 orders
:
db.inventory.insertMany( [ { prodId: 100, price: 20, quantity: 125 }, { prodId: 101, price: 10, quantity: 234 }, { prodId: 102, price: 15, quantity: 432 }, { prodId: 103, price: 17, quantity: 320 } ] ) db.orders.insertMany( [ { orderID: 201, custid: 301, prodId: 100, numPurchased: 20 }, { orderID: 202, custid: 302, prodId: 101, numPurchased: 10 }, { orderID: 203, custid: 303, prodId: 102, numPurchased: 5 }, { orderID: 204, custid: 303, prodId: 103, numPurchased: 15 }, { orderID: 205, custid: 303, prodId: 103, numPurchased: 20 }, { orderID: 206, custid: 302, prodId: 102, numPurchased: 1 }, { orderID: 207, custid: 302, prodId: 101, numPurchased: 5 }, { orderID: 208, custid: 301, prodId: 100, numPurchased: 10 }, { orderID: 209, custid: 303, prodId: 103, numPurchased: 30 } ] )
创建一个视图,将每个collection中的元素组合起来:
db.createView( "sales", "orders", [ { $lookup: { from: "inventory", localField: "prodId", foreignField: "prodId", as: "inventoryDocs" } }, { $project: { _id: 0, prodId: 1, orderId: 1, numPurchased: 1, price: "$inventoryDocs.price" } }, { $unwind: "$price" } ] )
在示例中:
db.createView()
创建sales
视图。sales
视图基于orders
collection。$lookup
阶段使用orders
集合中的prodId
字段来“连接”inventory
集合中具有匹配prodId
字段的文档。匹配的文档将作为数组添加到
inventoryDocs
字段中。$project
阶段选择可用字段的子集。$unwind
阶段会将price
字段从数组转换为标量值。
sales
视图中的文档包括:
{ prodId: 100, numPurchased: 20, price: 20 }, { prodId: 101, numPurchased: 10, price: 10 }, { prodId: 102, numPurchased: 5, price: 15 }, { prodId: 103, numPurchased: 15, price: 17 }, { prodId: 103, numPurchased: 20, price: 17 }, { prodId: 102, numPurchased: 1, price: 15 }, { prodId: 101, numPurchased: 5, price: 10 }, { prodId: 100, numPurchased: 10, price: 20 }, { prodId: 103, numPurchased: 30, price: 17 }
要查找每种产品的总销售量,请查询视图:
db.sales.aggregate( [ { $group: { _id: "$prodId", amountSold: { $sum: { $multiply: [ "$price", "$numPurchased" ] } } } } ] )
输出见下:
[ { _id: 100, amountSold: 600 }, { _id: 103, amountSold: 1105 }, { _id: 101, amountSold: 150 }, { _id: 102, amountSold: 90 } ]