我是一个旧的SQL手,现在使用现有的Mongo数据库。
不是使用嵌入文档数组,而关键信息是数组元素中的一个字段,每个嵌入文档都是自己的字段,键作为字段名称。
假设一个名为bank_branches
的集合。而不是这个:
{
branch : "St. Louis",
branch_employees : [
{
name : "Mary",
id : "M12345"
hire_date : Date("2010-05-20")
},
{
name : "John",
id : "J29876",
hire_date : Date("2015-03-23")
}
]
},
{
branch : "Jefferson City",
branch_employees : [
{
name : "Lisa",
id : "L87653"
hire_date : Date("2016-01-07")
}
]
}
......我们有这样的文件:
{
branch : "St. Louis",
branch_employees : {
M12345 : {
name : "Mary",
hire_date : Date("2010-05-20")
},
J29876 : {
name : "John",
hire_date : Date("2015-03-23")
}
}
{
branch : "Jefferson City",
branch_employees : {
L87653 : {
name : "Lisa",
hire_date : Date("2016-01-07")
}
}
}
(这是一个显示问题的发明结构。)
在MongoDB Aggregate Pipeline或其他方面,有没有办法执行以下任一操作?
查询员工嵌入式文档的组件字段,以便我可以(例如)获得2016年雇用的员工的所有分支,而不事先知道所有嵌入式文档的所有字段名称< / EM>
&#34;开卷&#34;这些对象,以便我可以拥有一系列分支员工文档,再次提前知道所有嵌入文档的所有字段名称? (MongoDB $unwind
管道操作仅适用于数组。)
我怀疑第一个可以使用$where
和javascript
以及/或自定义javascript存储函数来解决。 (我之前从未使用过存储过的函数。)但我怀疑第二个只能通过编程方式解决。
我可以通过编写Python并进行迭代来满足我的用例。但我宁愿编写查询来查找记录而不是以编程方式过滤它们。 (唯一保证无错误的代码是您不必编写的代码。)
建议,有人吗?非常感谢,提前。
答案 0 :(得分:1)
我可以对此喋喋不休,但你至少已经意识到最好的答案了。将数据转换为数组。 “查询”文档的唯一方法是在服务器上操作文档(按文档处理),将这些“命名键”强制转换为数组。
最近发布的MongoDB中有更多“现代”方法,这意味着你不必“使用”$where
,但仍有一个主要的警告,即它仍然是错误的形式,你只需要“无法使用索引”来加快查询结果。
在基本解决问题时:
如果您希望根据您的条件在分支机构员工雇用日期“查找文档”,那么您可以使用$where
的表达式,如下所示:
db.bank_branches.find(
function() {
return Object.keys(this.branch_employees).some(e =>
e.hire_date => new Date("2016-01-01") && e.hire_date < new Date("2017-01-01")
)
}
)
我们使用JavaScript评估的主要原因是您需要以查询DSL无法表达的方式“遍历文档的密钥”,因此需要针对每个文档评估条件而不是使用索引。
使用MongoDB 3.4.4以后的更新版本,您可以使用$objectToArray
与Object.keys()
的目的相同,并将其用作$redact
聚合管道阶段内表达式的一部分:
db.bank_branches.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": { "$objectToArray": "$branch_employees" },
"in": {
"$and": [
{ "$gte": [ "$$this.hire_date", new Date("2016-01-01") ] },
{ "$lt": [ "$$this.hire_date", new Date("2017-01-01") ] }
]
}
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
$redact
基本上评估一个条件,并根据条件的布尔结果通过$$KEEP
或“删除”文件返回文件。这类似于$where
,除了它是本机运算符而不是使用解释的JavaScript,本质上是“整体”查询条件而不是$where
,它在技术上只是另一个查询参数,可以与其他查询参数一起使用条件。
从MongoDB 3.6我们得到$expr
,它允许缩短,甚至可以用作与$where
相同的“附加参数”语法,除了本机编码运算符:
$$PRUNE
但它们仍然基本上都很糟糕,因为你所能做的只是“扫描整个集合”以获得结果。所以更好的“数组”语法是:
db.bank_branches.find({
"$expr": {
"$anyElementTrue": {
"$map": {
"input": { "$objectToArray": "$branch_employees" },
"in": {
"$and": [
{ "$gte": [ "$$this.hire_date", new Date("2016-01-01") ] },
{ "$lt": [ "$$this.hire_date", new Date("2017-01-01") ] }
]
}
}
}
}
})
当然可以使用索引,因为db.bank_branches.find({
"branch_employees": {
"$elemMatch": {
"hire_date": { "$gte": new Date("2016-01-01"), "$lte": new Date("2017-01-01") }
}
}
})
的路径是一致的,并且不使用“命名密钥”作为所需属性的中间路径的一部分。这是你想要这个结构的主要原因
为了实际获取“数组形状”中的文档,我们应该根据查询的构造方式给出一些指示。
所以在第一个选项中,如果写一个“新集合”是一个选项,那么在现代版本中你应该能够简单地在服务器上进行整个转换:
"branch_employees.hire_date"
或者,如果您没有现代运算符,或者根本无法写入新集合,那么基本上循环集合并写回属性的新数据:
db.bank_branches.aggregate([
{ "$project": {
"branch": 1,
"branch_employees": {
"$map": {
"input": { "$objectToArray": "$branch_employees" },
"in": {
"$arrayToObject": {
"$concatArrays": [
[{ "k": "id", "v": "$$this.k" }],
{ "$objectToArray": "$$this.v" }
]
}
}
}
}
}},
{ "$out": "new_branches" }
])
两者基本上都使用相同的技术来获取对象的子键的值,并将其与底层对象合并为作为“数组”返回的元素。
NB 在
$where
表达式中运行的代码使用JavaScript并在服务器上运行,因此它仍然是“语言无关的解决方案”,其中要评估的“JavaScript”是实际上以其他语言的“字符串”提交。其他表达式基本上分解为选择语言中的BSON表示。就这种语法而言,Python和Ruby几乎与JavaScript完全相同,并且相同的BSON约定适用于所有地方。
此处的其他例程用于“转换”数据,作为“一次性”操作应该始终足以在shell的JavaScript执行环境中运行。
因此没有“需要”使用JavaScript(
$where
表达式除外),但这里的示例以通用格式给出,每个人都可以在MongoDB安装提供的shell中运行。