MongoDB 使用手册 - 视图(View)

MongoDB 视图(View)是一个可查询对象,其内容由 其他集合或视图上的聚合管道定义。MongoDB 不会将视图内容保存到磁盘。当客户端查询视图时,视图的内容是按需计算的。MongoDB 可以要求客户端 具有查询视图的权限。MongoDB 不支持针对视图的写操作。

例如,您可以:

  • 创建有关 exclude 任何私人或个人信息 (PII) 的员工数据集合的视图。应用程序可以在视图中查询不包含任何 PII 的员工数据。
  • 在收集的传感器数据集合上创建一个视图,以 add 计算字段和指标。应用程序可以使用简单的查找操作来查询数据。
  • 创建一个视图,其中 joins 两个集合分别包含库存和订单历史记录。应用程序可以在不管理或了解底层复杂管道的情况下查询连接的数据。

当客户端查询视图时,MongoDB 将客户端查询附加到底层管道,并将该组合管道的结果返回给客户端。MongoDB 可以将 聚合管道优化应用于组合管道。

以下页面讨论视图。有关按需物化视图的讨论,请参阅按需物化视图

创建视图

创建或定义视图:

  • 使用db.createCollection()方法或 create命令:
db.createCollection(
  "<viewName>",
  {
    "viewOn" : "<source>",
    "pipeline" : [<pipeline>],
    "collation" : { <collation> }
  }
)
  • 使用db.createView()方法:
db.createView(
  "<viewName>",
  "<source>",
  [<pipeline>],
  {
    "collation" : { <collation> }
  }
)

您必须在与源集合相同的数据库中创建视图。

  • 视图定义pipeline不能包含outmerge阶段。如果视图定义包括嵌套管道(例如视图定义包括 lookupfacet阶段),则此限制也适用于嵌套管道。

行为

视图表现出以下行为:

只读
视图是只读的;视图上的写操作会出错。
以下读取操作可以支持视图:

索引使用和排序操作

视图使用基础集合的索引。
由于索引位于基础集合上,因此您无法直接在视图上创建、删除或重新构建索引,也无法获取视图上的索引列表。
$natural 从 MongoDB 4.4 开始,您可以在视图上运行find命令时指定排序。MongoDB 的早期版本不支持$natural对视图进行排序。
视图的底层聚合管道受限于 100 兆字节的内存限制,用于阻塞排序和阻塞组操作。从 MongoDB 4.4 开始,您可以在视图上发出 find命令 allowDiskUse: true以允许 MongoDB 使用临时文件来阻止排序和分组操作。

在 MongoDB 4.4 之前,只有aggregate命令接受该 allowDiskUse 选项。

有关阻止排序操作内存限制的更多信息,请参阅排序操作

投影限制

find()视图上的操作不支持以下投影 运算符:

  • $
  • $elemMatch
  • $slice
  • $meta

不可变名称

您不能重命名视图。

视图创建

  • 视图是在读取操作期间按需计算的,MongoDB 对视图执行读取操作作为底层聚合管道的一部分。因此,视图不支持以下操作:

  • 如果用于创建视图的聚合管道抑制了该 _id字段,则视图中的文档没有该_id字段。

当您查询视图时:

  • 查询 filter, projection, sort, skip, limit 和其他操作 db.collection.find() 被转换为等效的聚合管道阶段
  • 转换后的聚合管道阶段将添加到 视图的聚合管道的末尾。这不会修改视图的底层管道,它是在您创建视图时设置的。
  • 聚合管道优化器重塑视图聚合管道阶段以提高性能。这不会更改查询结果。

分片视图
如果视图的底层集合是分片的,则视图被认为是分片的。因此,不能在$lookup$graphLookup 操作中为 from 字段指定分片视图。

视图和校对

您可以在创建时为视图指定默认排序规则。如果未指定排序规则,则视图的默认排序规则是“simple”二进制比较排序规则。也就是说,视图不继承集合的默认排序规则。
视图上的字符串比较使用视图的默认排序规则。尝试更改或覆盖视图的默认排序规则的操作将失败并出现错误。
如果从另一个视图创建视图,则不能指定与源视图的排序规则不同的排序规则。
如果执行涉及多个视图的聚合,例如 $lookup$graphLookup,则视图必须具有相同的比对规则。

公众视野定义

列出集合的操作,例如 db.getCollectionInfos()db.getCollectionNames() ,在其输出中包含视图。

视图定义是公开的;即视图上db.getCollectionInfos()explain操作将包括定义视图的管道。因此,请避免在视图定义中直接引用敏感字段和值。

删除视图

要删除视图,请在视图上使用 db.collection.drop() 方法。

修改视图

您可以通过删除并重新创建视图或使用 collMod 命令来修改视图。

支持的操作
以下操作提供对视图的支持,但本页中提到的限制除外:

示例

创建 students 要在以下示例中使用的集合:

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 }
] )

创建一个组合来自每个集合的元素的视图:

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
$lookup 使用集合 orders 中的 prodId字段 来“join” 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 }
]
posted @   Earen  阅读(3313)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示