MongoDB 根据过滤规则投影嵌套数组中对象的特定值
MongoDB Projecting specific values from objects in nested arrays according to filtering rules
我对 MongoDB 很陌生,第一次在大型应用程序上使用它。我们有一个复杂的嵌套结构,它代表一个对象,该对象具有与其关联的多个文档,以及与每个文档相关联的多个人。为了构建 GUI,我需要从文档层次结构中提取和连接一些信息并将其“提升”到顶层,从而创建一个简单的平面结构。如果每个嵌套的“子查询”有多个结果,我只对第一个感兴趣。
我曾尝试使用聚合构建器来实现这一点,但每次我都以一百多行无休止的 unwind、addfield、project 结束,它变得太长太复杂(而且可能也不是很快)可行。必须有一个更简单的解决方案。让我提供一个示例结构(为了简洁起见,我省略了大部分字段,只留下了基本的字段):
{
"_id": ObjectId(),
"number": "ABC-123456",
"status": "new",
"items": [
{
"_id": ObjectId(),
"name": "invoice",
"people": [
{
"first_name": "John",
"last_name": "Doe",
"active": false
},
{
"first_name": "Jane",
"last_name": "Smith",
"active": true
},
{
"first_name": "Fred",
"last_name": "Bloggs",
"active": true
}
]
},
{
"_id": ObjectId(),
"name": "unimportant_document",
"people": [
{
"first_name": "John",
"last_name": "Doe",
"active": true
}
]
},
{
"_id": ObjectId(),
"name": "order",
"people": [
{
"first_name": "Fred",
"last_name": "Bloggs",
"active": true
}
]
}
]
}
现在,我想得到类似这样的结果:
{
"_id": "XXX",
"number": "ABC-123456",
"status": "new",
"invoice_person_full_name": "Jane Smith",
"order_person_full_name": "Fred Bloggs"
}
基本上,我需要将“first_name”和“last_name”从具有“active”的第一人称连接起来:在特定名称的文档中的一组人中为真(“invoice “对于“invoice_person_full_name”和“订单”对于“order_person_full_name”)。
我不关心重复项,所以如果有多个名为“invoice”的文档,例如,我只想检索第一张发票和这张发票的第一位活动人员。
正如我所说,我已经尝试使用聚合生成器来执行此操作,使用带有过滤器的项目将项目数组过滤为仅“发票”名称,然后展开,再次投影过滤人员,再次投影以获得第一个,展开,添加字段,编写 JS 函数来连接名称(实际上有学位,中间名等需要用空格分隔并且可能为 null,因此 $concat 是不够的)并最终返回。这需要 40 多行代码并且仅适用于单个项目名称,因此我需要多次组合此代码以获得我需要的所有名称的结果。这可能意味着我没有以正确的方式解决问题。
我希望我把问题说清楚了,任何帮助将不胜感激!
$set
1.1。 invoice_persons
- 通过从 items
数组中获取第一个文档来创建字段,其中 name
是“invoice”。
1.2。 order_persons
- 通过从 items
数组中获取其 name
为“order”的第一个文档来创建字段。
$set
2.1。 invoice_person_full_name
- 首先过滤来自 invoice_persons.people
的文档,其 active
是 true
。接下来用 $map
执行全名的字符串连接。最后,获取第一个文档。
2.2。 order_person_full_name
- 首先过滤来自 order_persons.people
的文档,其 active
是 true
。接下来用 $map
执行全名的字符串连接。最后,获取第一个文档。
$unset
- 删除字段。
db.collection.aggregate([
{
$set: {
"invoice_persons": {
$first: {
$filter: {
input: "$items",
cond: {
$eq: [
"$$this.name",
"invoice"
]
}
}
}
},
"order_persons": {
$first: {
$filter: {
input: "$items",
cond: {
$eq: [
"$$this.name",
"order"
]
}
}
}
}
}
},
{
$set: {
"invoice_person_full_name": {
$first: {
$map: {
input: {
$filter: {
input: "$invoice_persons.people",
cond: {
$eq: [
"$$this.active",
true
]
}
}
},
in: {
"$concat": [
"$$this.first_name",
" ",
"$$this.last_name"
]
}
}
}
},
"order_person_full_name": {
$first: {
$map: {
input: {
$filter: {
input: "$order_persons.people",
cond: {
$eq: [
"$$this.active",
true
]
}
}
},
in: {
"$concat": [
"$$this.first_name",
" ",
"$$this.last_name"
]
}
}
}
}
}
},
{
"$unset": [
"invoice_persons",
"order_persons",
"items"
]
}
])
我对 MongoDB 很陌生,第一次在大型应用程序上使用它。我们有一个复杂的嵌套结构,它代表一个对象,该对象具有与其关联的多个文档,以及与每个文档相关联的多个人。为了构建 GUI,我需要从文档层次结构中提取和连接一些信息并将其“提升”到顶层,从而创建一个简单的平面结构。如果每个嵌套的“子查询”有多个结果,我只对第一个感兴趣。
我曾尝试使用聚合构建器来实现这一点,但每次我都以一百多行无休止的 unwind、addfield、project 结束,它变得太长太复杂(而且可能也不是很快)可行。必须有一个更简单的解决方案。让我提供一个示例结构(为了简洁起见,我省略了大部分字段,只留下了基本的字段):
{
"_id": ObjectId(),
"number": "ABC-123456",
"status": "new",
"items": [
{
"_id": ObjectId(),
"name": "invoice",
"people": [
{
"first_name": "John",
"last_name": "Doe",
"active": false
},
{
"first_name": "Jane",
"last_name": "Smith",
"active": true
},
{
"first_name": "Fred",
"last_name": "Bloggs",
"active": true
}
]
},
{
"_id": ObjectId(),
"name": "unimportant_document",
"people": [
{
"first_name": "John",
"last_name": "Doe",
"active": true
}
]
},
{
"_id": ObjectId(),
"name": "order",
"people": [
{
"first_name": "Fred",
"last_name": "Bloggs",
"active": true
}
]
}
]
}
现在,我想得到类似这样的结果:
{
"_id": "XXX",
"number": "ABC-123456",
"status": "new",
"invoice_person_full_name": "Jane Smith",
"order_person_full_name": "Fred Bloggs"
}
基本上,我需要将“first_name”和“last_name”从具有“active”的第一人称连接起来:在特定名称的文档中的一组人中为真(“invoice “对于“invoice_person_full_name”和“订单”对于“order_person_full_name”)。
我不关心重复项,所以如果有多个名为“invoice”的文档,例如,我只想检索第一张发票和这张发票的第一位活动人员。
正如我所说,我已经尝试使用聚合生成器来执行此操作,使用带有过滤器的项目将项目数组过滤为仅“发票”名称,然后展开,再次投影过滤人员,再次投影以获得第一个,展开,添加字段,编写 JS 函数来连接名称(实际上有学位,中间名等需要用空格分隔并且可能为 null,因此 $concat 是不够的)并最终返回。这需要 40 多行代码并且仅适用于单个项目名称,因此我需要多次组合此代码以获得我需要的所有名称的结果。这可能意味着我没有以正确的方式解决问题。
我希望我把问题说清楚了,任何帮助将不胜感激!
$set
1.1。
invoice_persons
- 通过从items
数组中获取第一个文档来创建字段,其中name
是“invoice”。1.2。
order_persons
- 通过从items
数组中获取其name
为“order”的第一个文档来创建字段。$set
2.1。
invoice_person_full_name
- 首先过滤来自invoice_persons.people
的文档,其active
是true
。接下来用$map
执行全名的字符串连接。最后,获取第一个文档。2.2。
order_person_full_name
- 首先过滤来自order_persons.people
的文档,其active
是true
。接下来用$map
执行全名的字符串连接。最后,获取第一个文档。$unset
- 删除字段。
db.collection.aggregate([
{
$set: {
"invoice_persons": {
$first: {
$filter: {
input: "$items",
cond: {
$eq: [
"$$this.name",
"invoice"
]
}
}
}
},
"order_persons": {
$first: {
$filter: {
input: "$items",
cond: {
$eq: [
"$$this.name",
"order"
]
}
}
}
}
}
},
{
$set: {
"invoice_person_full_name": {
$first: {
$map: {
input: {
$filter: {
input: "$invoice_persons.people",
cond: {
$eq: [
"$$this.active",
true
]
}
}
},
in: {
"$concat": [
"$$this.first_name",
" ",
"$$this.last_name"
]
}
}
}
},
"order_person_full_name": {
$first: {
$map: {
input: {
$filter: {
input: "$order_persons.people",
cond: {
$eq: [
"$$this.active",
true
]
}
}
},
in: {
"$concat": [
"$$this.first_name",
" ",
"$$this.last_name"
]
}
}
}
}
}
},
{
"$unset": [
"invoice_persons",
"order_persons",
"items"
]
}
])