Docs 菜单

Docs 主页开发应用程序MongoDB Manual

创建和查询视图

在此页面上

  • db.createCollection() 语法
  • db.createView() 语法
  • 限制
  • 不支持的操作
  • 举例
  • 填充集合
  • 使用 db.createView() 创建视图
  • 使用 db.createCollection() 创建视图
  • 检索授予当前用户的角色相关的医疗信息
  • 检索授予当前用户的角色相关的预算文档
  • 多个数据库中具有相同名称的角色
  • 行为
  • 聚合优化
  • 资源锁定

要创建视图,请使用以下方法之一:

要在 MongoDB Atlas 用户界面中创建视图,您必须使用物化视图。如需了解详情,请参阅在 MongoDB Atlas 用户界面中创建物化视图

重要

视图名称包含在集合列表输出中

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

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

db.createCollection(
"<viewName>",
{
"viewOn" : "<source>",
"pipeline" : [<pipeline>],
"collation" : { <collation> }
}
)
db.createView(
"<viewName>",
"<source>",
[<pipeline>],
{
"collation" : { <collation> }
}
)
  • 您必须在与源集合相同的数据库中创建视图。

  • 视图定义 pipeline 不能包含 $out$merge 阶段。这一限制也适用于嵌入式管道,例如在 $lookup$facet 阶段中使用的管道。

  • 视图一旦创建便无法重命名。

某些操作不适用于视图:

有关详细信息,请参阅视图支持的操作。

第一个示例使用学生数据填充集合,并创建了一个用于查询该数据的视图。

创建一个用于此示例的 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(
"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 }
]

注意

投影限制

视图上的 find() 操作不支持以下查询和投影操作符

使用 db.createCollection() 方法可以创建具有特定选项的集合或视图。

以下示例将创建一个 graduateStudents 视图。该视图仅包含由 $match 阶段选择的文档。排序规则设置(可选)决定了排序顺序。

db.createCollection(
"graduateStudents",
{
viewOn: "students",
pipeline: [ { $match: { $expr: { $gt: [ "$year", 4 ] } } } ],
collation: { locale: "en", caseFirst: "upper" }
}
)

注意

排序规则行为

  • 您可以在创建视图时为其指定默认排序规则。如果未指定排序规则,则视图的默认排序规则是“简单”二进制比较排序规则。也就是说,视图不会继承集合的默认排序规则。

  • 视图上的字符串比较使用的是视图的默认排序规则。尝试更改或覆盖视图默认排序规则的操作会失败并报错。

  • 如果从另一个视图创建视图,则无法指定与源视图不同的排序规则。

  • 如果执行的聚合涉及多个视图,例如使用 $lookup$graphLookup ,则这些视图必须采用相同的排序规则

以下示例将查询该视图。为清晰起见,$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 }
]

从 MongoDB 7.0 开始,您可以使用新的 USER_ROLES 系统变量来返回用户角色

本节中的示例显示了用户对包含医疗信息的集合中的字段具有有限访问权限。该示例使用一个视图,该视图从 USER_ROLES 系统变量中读取当前用户角色,并根据相应角色隐藏字段。

该示例创建这些用户:

  • James 具有可以访问 creditCard 字段的 Billing 角色。

  • Michelle 具有可以访问 diagnosisCode 字段的 Provider 角色。

执行以下步骤来创建角色、用户、集合和视图:

1

运行:

db.createRole( { role: "Billing", privileges: [ { resource: { db: "test",
collection: "medicalView" }, actions: [ "find" ] } ], roles: [ ] } )
db.createRole( { role: "Provider", privileges: [ { resource: { db: "test",
collection: "medicalView" }, actions: [ "find" ] } ], roles: [ ] } )
2

创建名为 JamesMichelle 的用户,并赋予所需角色。将 test 数据库替换为您的数据库名称。

db.createUser( {
user: "James",
pwd: "js008",
roles: [
{ role: "Billing", db: "test" }
]
} )
db.createUser( {
user: "Michelle",
pwd: "me009",
roles: [
{ role: "Provider", db: "test" }
]
} )
3

运行:

db.medical.insertMany( [
{
_id: 0,
patientName: "Jack Jones",
diagnosisCode: "CAS 17",
creditCard: "1234-5678-9012-3456"
},
{
_id: 1,
patientName: "Mary Smith",
diagnosisCode: "ACH 01",
creditCard: "6541-7534-9637-3456"
}
] )
4

要使用系统变量,请将$$添加到变量名称的开头。将USER_ROLES系统变量指定为$$USER_ROLES

该视图将从 USER_ROLES 系统变量读取当前用户角色,并按角色隐藏字段。

运行:

db.createView(
"medicalView", "medical",
[ {
$set: {
"diagnosisCode": {
$cond: {
if: { $in: [
"Provider", "$$USER_ROLES.role"
] },
then: "$diagnosisCode",
else: "$$REMOVE"
}
}
},
}, {
$set: {
"creditCard": {
$cond: {
if: { $in: [
"Billing", "$$USER_ROLES.role"
] },
then: "$creditCard",
else: "$$REMOVE"
}
}
}
} ]
)

视图示例:

  • 包括具有 Provider 角色的用户的 diagnosisCode 字段。

  • 包括具有 Billing 角色的用户的 creditCard 字段。

  • 使用 $set 管道阶段和 $$REMOVE 以根据查询该视图的用户是否具有 $$USER_ROLES.role 中返回的匹配角色来隐藏字段。

执行以下步骤来检索 James 可以访问的信息:

1

运行:

db.auth( "James", "js008" )
2

运行:

db.medicalView.find()
3

James 具有 Billing 角色并查看以下文档,其中包括 creditCard 字段,但不包括 diagnosisCode 字段:

[
{
_id: 0, patientName: 'Jack Jones',
creditCard: '1234-5678-9012-3456'
},
{
_id: 1, patientName: 'Mary Smith',
creditCard: '6541-7534-9637-3456'
}
]

执行以下步骤来检索 Michelle 可以访问的信息:

1

运行:

db.auth( "Michelle", "me009" )
2

运行:

db.medicalView.find()
3

Michelle 具有 Provider 角色并查看以下文档,其中包括 diagnosisCode 字段,但不包括 creditCard 字段:

[
{ _id: 0, patientName: 'Jack Jones',
diagnosisCode: 'CAS 17' },
{ _id: 1, patientName: 'Mary Smith',
diagnosisCode: 'ACH 01' }
]

从 MongoDB 7.0 开始,您可以使用新的 USER_ROLES 系统变量来返回用户角色

本部分的场景显示了具有各种角色的用户,而这些用户对包含预算信息的集合中的文档具有有限访问权限。

该场景显示了 USER_ROLES 的一种可能用途。budget 集合包含带有名为 allowedRoles 字段的文档。正如以下场景所示,您可以编写查询将 allowedRoles 字段中找到的用户角色与 USER_ROLES 系统变量返回的角色进行比较。

注意

有关另一个USER_ROLES示例场景,请参阅检索授予当前用户的角色相关的医疗信息。该示例不会将用户角色存储在文档字段中,如以下示例所示。

对于本部分中的预算方案,请执行以下步骤来创建角色、用户和 budget 集合:

1

运行:

db.createRole( { role: "Marketing", roles: [], privileges: [] } )
db.createRole( { role: "Sales", roles: [], privileges: [] } )
db.createRole( { role: "Development", roles: [], privileges: [] } )
db.createRole( { role: "Operations", roles: [], privileges: [] } )
2

创建名为 JohnJane 的用户,并赋予所需角色。将 test 数据库替换为您的数据库名称。

db.createUser( {
user: "John",
pwd: "jn008",
roles: [
{ role: "Marketing", db: "test" },
{ role: "Development", db: "test" },
{ role: "Operations", db: "test" },
{ role: "read", db: "test" }
]
} )
db.createUser( {
user: "Jane",
pwd: "je009",
roles: [
{ role: "Sales", db: "test" },
{ role: "Operations", db: "test" },
{ role: "read", db: "test" }
]
} )
3

运行:

db.budget.insertMany( [
{
_id: 0,
allowedRoles: [ "Marketing" ],
comment: "For marketing team",
yearlyBudget: 15000
},
{
_id: 1,
allowedRoles: [ "Sales" ],
comment: "For sales team",
yearlyBudget: 17000,
salesEventsBudget: 1000
},
{
_id: 2,
allowedRoles: [ "Operations" ],
comment: "For operations team",
yearlyBudget: 19000,
cloudBudget: 12000
},
{
_id: 3,
allowedRoles: [ "Development" ],
comment: "For development team",
yearlyBudget: 27000
}
] )

执行以下步骤以创建视图并检索 John 可以访问的文档:

1

要使用系统变量,请将$$添加到变量名称的开头。将USER_ROLES系统变量指定为$$USER_ROLES

运行:

db.createView(
"budgetView", "budget",
[ {
$match: {
$expr: {
$not: {
$eq: [ { $setIntersection: [ "$allowedRoles", "$$USER_ROLES.role" ] }, [] ]
}
}
}
} ]
)

如果无法创建视图,请确保您以拥有视图创建权限的用户身份登录。

上一示例从budget集合中返回至少与运行该示例的用户所具有的角色之一匹配的文档。为此,该示例使用$setIntersection返回文档,其中budget文档allowedRoles字段与$$USER_ROLES的用户角色集之间的交集不为空。

2

运行:

db.auth( "John", "jn008" )
3

运行:

db.budgetView.find()
4

John 具有MarketingOperationsDevelopment 角色,且能看到以下文档:

[
{
_id: 0,
allowedRoles: [ 'Marketing' ],
comment: 'For marketing team',
yearlyBudget: 15000
},
{
_id: 2,
allowedRoles: [ 'Operations' ],
comment: 'For operations team',
yearlyBudget: 19000,
cloudBudget: 12000
},
{
_id: 3,
allowedRoles: [ 'Development' ],
comment: 'For development team',
yearlyBudget: 27000
}
]

执行以下步骤来获取 Jane 可以访问的文档:

1

运行:

db.auth( "Jane", "je009" )
2

运行:

db.budgetView.find()
3

Jane 具有 SalesOperations 角色,且能看到以下文档:

[
{
_id: 1,
allowedRoles: [ 'Sales' ],
comment: 'For sales team',
yearlyBudget: 17000,
salesEventsBudget: 1000
},
{
_id: 2,
allowedRoles: [ 'Operations' ],
comment: 'For operations team',
yearlyBudget: 19000,
cloudBudget: 12000
}
]

注意

在分片集群上,查询可以由另一个服务器节点代表用户在分片上运行。在这些查询中,USER_ROLES 仍填充用户的角色。

多个数据库可以具有同名的角色。如果您创建了一个视图,并在该视图中引用了一个特定角色,则应同时指定 db 数据库名称字段和 role 字段,或指定包含数据库名称和角色的 _id 字段。

以下示例将返回分配给 Jane 的角色,此用户拥有不同名称的角色。该示例将返回 _idroledb 数据库名称:

1

运行:

db.auth( "Jane", "je009" )
2

运行:

db.budget.findOne( {}, { myRoles: "$$USER_ROLES" } )
3

示例输出,其中显示了 myRoles 数组中的 _idroledb 数据库名称:

{
_id: 0,
myRoles: [
{ _id: 'test.Operations', role: 'Operations', db: 'test' },
{ _id: 'test.Sales', role: 'Sales', db: 'test' },
{ _id: 'test.read', role: 'read', db: 'test' }
]
}

以下各部分介绍了视图创建和查询的行为。

当您查询视图时:

  • 查询 filterprojectionsortskiplimitdb.collection.find() 的其他操作会转换为等效的聚合管道阶段。

  • MongoDB 将客户端查询附加到底层管道,并将组合管道的结果返回给客户端。MongoDB 可能会将聚合管道优化应用于组合管道。

  • 聚合管道优化器可重塑视图的聚合管道阶段以提高性能。优化操作不会更改查询结果。

db.createView() 在操作期间获得指定集合或视图的独占锁。对集合的所有后续操作都必须等到 db.createView() 释放该锁为止。db.createView() 通常会短暂占用该锁。

创建视图需获得数据库中 system.views 集合的额外独占锁。此锁会阻止创建或修改数据库中的视图,直到命令完成。

← 视图