我如何聚合过滤嵌套文档并从其他字段获取价值
How can i aggregate filter nested documents and get value from other field
我有一个 collection 这样的:
{
'_id' : ObjectId('6251f8556e75125f9260f333'),
'name': 'jojo',
'profile': 'jojo profile',
'date': ISODate("2022-04-09T21:18:40.473Z"),
'look': [
{ 'art': 'group-id', 'data': 'alma', 'dt': '1'},
{ 'art': 'called', 'data': 'central', 'dt': '1'},
{ 'art': 'access-time', 'data': 108000, 'dt': '1'}
]
'answer': [
{ 'art': 'rate-id', 'data': 'limit1', 'dt': '1'},
{ 'art': 'protocol', 'data': 'tcp', 'dt': '1'}
]
},
{
'_id' : ObjectId('6251f8306e75125f9260f332'),
'name': 'dodo',
'profile': 'dodo profile',
'date': ISODate("2022-04-09T15:20:58.562Z"),
'look': [
{ 'art': 'group-id', 'data': 'alma', 'dt': '1'},
{ 'art': 'called', 'data': 'central', 'dt': '1'},
]
'answer': [
{ 'art': 'rate-id', 'data': 'limit1', 'dt': '1'},
]
},
{
'_id' : ObjectId('6251a5113700ba4a0a59c48f'),
'name': 'kaka',
'profile': 'kaka profile',
'date': ISODate("2022-04-09T15:22:25.816Z"),
'look': [
{ 'art': 'access-time', 'data': 50400, 'dt': '1'}
]
'answer': [
{ 'art': 'protocol', 'data': 'tcp', 'dt': '1'}
]
}
我期待这样的输出:
{
'_id' : ObjectId('6251f8556e75125f9260f333'),
'name': 'jojo',
'profile': 'jojo profile',
'date': ISODate("2022-04-09T21:18:40.473Z"),
'goup': 'alma', // filter by 'group-id' and put value of data field
'called': 'central', // filter by 'called' and put value of data field
'accessTime': 108000, // filter by 'access-time' and put value of data field
'rate': 'limi1', // filter by 'rate-id' and put value of data field
'protocol': 'tcp', // filter by 'protocol' and put value of data field
},
{
'_id' : ObjectId('6251f8306e75125f9260f332'),
'name': 'dodo',
'profile': 'dodo profile',
'date': ISODate("2022-04-09T15:20:58.562Z"),
'goup': 'alma',
'called': 'central',
'accessTime': '', // set blank data if not exist
'rate': 'limi1',
'protocol': '', // set blank data if not exist
},
{
'_id' : ObjectId('6251a5113700ba4a0a59c48f'),
'name': 'kaka',
'profile': 'kaka profile',
'date': ISODate("2022-04-09T15:22:25.816Z"),
'goup': '', // set blank data if not exist
'called': '', // set blank data if not exist
'accessTime': 50400,
'rate': '', // set blank data if not exist
'protocol': 'tcp',
}
我在这里搜索过,但找不到与我面临的问题相匹配的答案,可能是因为关键字错误。
由于我是 mongodb 的新手,我对如何解决我想要的查询感到困惑。我怎样才能做到这一点?请帮助我...
为此,您应该使用 mongo 数据库的 aggregation framework
,因为需要复杂的操作才能获得所需形状的数据。
https://www.mongodb.com/docs/manual/aggregation/
每个聚合都是一系列阶段,每个阶段都做一些特定的事情。
我使用了下一个阶段:
- addFields:允许您将新字段添加到每个文档的响应中,因此如果文档中没有组,它将添加或替换它。
- 项目:允许您删除文档的某些字段。在投影阶段,如果将属性设置为 0,将从响应中删除该属性。
我还使用了一些运算符:
- 过滤器:这允许您过滤数组元素的数据
- arrayElemenAt: 接收一个数组和return指定的位置
管道:
[
{
"$addFields":{
"group":{
"$arrayElemAt":[
{
"$filter":{
"input":"$look",
"as":"item",
"cond":{
"$eq":[
"$$item.art",
"group-id"
]
}
}
},
0
]
},
"called":{
"$arrayElemAt":[
{
"$filter":{
"input":"$look",
"as":"item",
"cond":{
"$eq":[
"$$item.art",
"called"
]
}
}
},
0
]
},
"accessTime":{
"$arrayElemAt":[
{
"$filter":{
"input":"$look",
"as":"item",
"cond":{
"$eq":[
"$$item.art",
"access-time"
]
}
}
},
0
]
},
"rate":{
"$arrayElemAt":[
{
"$filter":{
"input":"$answer",
"as":"item",
"cond":{
"$eq":[
"$$item.art",
"rate-id"
]
}
}
},
0
]
},
"protocol":{
"$arrayElemAt":[
{
"$filter":{
"input":"$answer",
"as":"item",
"cond":{
"$eq":[
"$$item.art",
"protocol"
]
}
}
},
0
]
}
}
},
{
"$addFields":{
"group":"$group.data",
"called":"$called.data",
"accessTime":"$accessTime.data",
"rate":"$rate.data",
"protocol":"$protocol.data"
}
},
{
"$project":{
"look":0,
"answer":0
}
}
]
这对于当前的结构来说非常麻烦,因为对于每个字段你都必须将对象转换为数组,过滤它然后将其转换回来,这是它的样子:
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
{
_id: "$_id",
name: "$name",
profile: "$profile",
date: "$date",
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$look",
[]
]
},
cond: {
$eq: [
"$$this.art",
"group-id"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "goup",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$look",
[]
]
},
cond: {
$eq: [
"$$this.art",
"called"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "called",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$look",
[]
]
},
cond: {
$eq: [
"$$this.art",
"access-time"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "access-time",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$answer",
[]
]
},
cond: {
$eq: [
"$$this.art",
"rate-id"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "rate",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$answer",
[]
]
},
cond: {
$eq: [
"$$this.art",
"protocol"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "protocol",
v: "$$this.v"
}
}
}
}
]
}
}
}
])
如果您使用的是 Mongo 版本 5+,那么您可以使用 $getField 来稍微简化语法,下面是一个字段在此语法中的样子:
goup: {
$getField: {
field: 'data',
input: {
'$arrayElemAt': [
{
$filter: {
input: {
$ifNull: [
'$look',
[],
],
},
cond: {
$eq: [
'$$this.art',
'group-id',
],
},
},
},
0,
],
},
},
},
您需要一个聚合操作,该操作具有一个包含以下关键运算符和阶段的管道:
$map
:将 look
和 answer
数组转换为仅映射 k
和 v
字段的文档的运算符,对于获得具有以下运算符的哈希映射
$arrayToObject
:这使得上述成为可能,即将数组转换为单个文档
$mergeObjects
:将顶级字段即 _id
、date
、name
、profile
与上面转换后的文档组合在一起
$replaceWith
: 管道阶段用上面的指定文档替换根文档
总体而言,您的管道应遵循:
const first = {
$first: {
$split: ['$$this.art', '-']
}
};
const keyExpression = {
$cond: [
{ $eq: [first, 'access'] },
'accessTime',
first
]
};
const pipeline = [
{ $replaceWith: {
$mergeObjects: [
{
_id: '$_id',
date: '$date',
name: '$name',
profile: '$profile',
protocol: '',
group: '',
called: '',
rate: '',
accessTime: '',
},
{ $arrayToObject: {
$map: {
input: '$look',
in: { k: keyExpression, v: '$$this.data' }
}
} },
{ $arrayToObject: {
$map: {
input: '$answer',
in: { k: keyExpression, v: '$$this.data' }
}
} }
]
} }
]
我有一个 collection 这样的:
{
'_id' : ObjectId('6251f8556e75125f9260f333'),
'name': 'jojo',
'profile': 'jojo profile',
'date': ISODate("2022-04-09T21:18:40.473Z"),
'look': [
{ 'art': 'group-id', 'data': 'alma', 'dt': '1'},
{ 'art': 'called', 'data': 'central', 'dt': '1'},
{ 'art': 'access-time', 'data': 108000, 'dt': '1'}
]
'answer': [
{ 'art': 'rate-id', 'data': 'limit1', 'dt': '1'},
{ 'art': 'protocol', 'data': 'tcp', 'dt': '1'}
]
},
{
'_id' : ObjectId('6251f8306e75125f9260f332'),
'name': 'dodo',
'profile': 'dodo profile',
'date': ISODate("2022-04-09T15:20:58.562Z"),
'look': [
{ 'art': 'group-id', 'data': 'alma', 'dt': '1'},
{ 'art': 'called', 'data': 'central', 'dt': '1'},
]
'answer': [
{ 'art': 'rate-id', 'data': 'limit1', 'dt': '1'},
]
},
{
'_id' : ObjectId('6251a5113700ba4a0a59c48f'),
'name': 'kaka',
'profile': 'kaka profile',
'date': ISODate("2022-04-09T15:22:25.816Z"),
'look': [
{ 'art': 'access-time', 'data': 50400, 'dt': '1'}
]
'answer': [
{ 'art': 'protocol', 'data': 'tcp', 'dt': '1'}
]
}
我期待这样的输出:
{
'_id' : ObjectId('6251f8556e75125f9260f333'),
'name': 'jojo',
'profile': 'jojo profile',
'date': ISODate("2022-04-09T21:18:40.473Z"),
'goup': 'alma', // filter by 'group-id' and put value of data field
'called': 'central', // filter by 'called' and put value of data field
'accessTime': 108000, // filter by 'access-time' and put value of data field
'rate': 'limi1', // filter by 'rate-id' and put value of data field
'protocol': 'tcp', // filter by 'protocol' and put value of data field
},
{
'_id' : ObjectId('6251f8306e75125f9260f332'),
'name': 'dodo',
'profile': 'dodo profile',
'date': ISODate("2022-04-09T15:20:58.562Z"),
'goup': 'alma',
'called': 'central',
'accessTime': '', // set blank data if not exist
'rate': 'limi1',
'protocol': '', // set blank data if not exist
},
{
'_id' : ObjectId('6251a5113700ba4a0a59c48f'),
'name': 'kaka',
'profile': 'kaka profile',
'date': ISODate("2022-04-09T15:22:25.816Z"),
'goup': '', // set blank data if not exist
'called': '', // set blank data if not exist
'accessTime': 50400,
'rate': '', // set blank data if not exist
'protocol': 'tcp',
}
我在这里搜索过,但找不到与我面临的问题相匹配的答案,可能是因为关键字错误。 由于我是 mongodb 的新手,我对如何解决我想要的查询感到困惑。我怎样才能做到这一点?请帮助我...
为此,您应该使用 mongo 数据库的 aggregation framework
,因为需要复杂的操作才能获得所需形状的数据。
https://www.mongodb.com/docs/manual/aggregation/
每个聚合都是一系列阶段,每个阶段都做一些特定的事情。
我使用了下一个阶段:
- addFields:允许您将新字段添加到每个文档的响应中,因此如果文档中没有组,它将添加或替换它。
- 项目:允许您删除文档的某些字段。在投影阶段,如果将属性设置为 0,将从响应中删除该属性。
我还使用了一些运算符:
- 过滤器:这允许您过滤数组元素的数据
- arrayElemenAt: 接收一个数组和return指定的位置
管道:
[ { "$addFields":{ "group":{ "$arrayElemAt":[ { "$filter":{ "input":"$look", "as":"item", "cond":{ "$eq":[ "$$item.art", "group-id" ] } } }, 0 ] }, "called":{ "$arrayElemAt":[ { "$filter":{ "input":"$look", "as":"item", "cond":{ "$eq":[ "$$item.art", "called" ] } } }, 0 ] }, "accessTime":{ "$arrayElemAt":[ { "$filter":{ "input":"$look", "as":"item", "cond":{ "$eq":[ "$$item.art", "access-time" ] } } }, 0 ] }, "rate":{ "$arrayElemAt":[ { "$filter":{ "input":"$answer", "as":"item", "cond":{ "$eq":[ "$$item.art", "rate-id" ] } } }, 0 ] }, "protocol":{ "$arrayElemAt":[ { "$filter":{ "input":"$answer", "as":"item", "cond":{ "$eq":[ "$$item.art", "protocol" ] } } }, 0 ] } } }, { "$addFields":{ "group":"$group.data", "called":"$called.data", "accessTime":"$accessTime.data", "rate":"$rate.data", "protocol":"$protocol.data" } }, { "$project":{ "look":0, "answer":0 } } ]
这对于当前的结构来说非常麻烦,因为对于每个字段你都必须将对象转换为数组,过滤它然后将其转换回来,这是它的样子:
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
{
_id: "$_id",
name: "$name",
profile: "$profile",
date: "$date",
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$look",
[]
]
},
cond: {
$eq: [
"$$this.art",
"group-id"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "goup",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$look",
[]
]
},
cond: {
$eq: [
"$$this.art",
"called"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "called",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$look",
[]
]
},
cond: {
$eq: [
"$$this.art",
"access-time"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "access-time",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$answer",
[]
]
},
cond: {
$eq: [
"$$this.art",
"rate-id"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "rate",
v: "$$this.v"
}
}
}
},
{
"$arrayToObject": {
$map: {
input: {
$filter: {
input: {
$objectToArray: {
$ifNull: [
{
"$arrayElemAt": [
{
$filter: {
input: {
$ifNull: [
"$answer",
[]
]
},
cond: {
$eq: [
"$$this.art",
"protocol"
]
}
}
},
0
]
},
{
art: ""
}
]
}
},
cond: {
$eq: [
"$$this.k",
"data"
]
}
}
},
in: {
k: "protocol",
v: "$$this.v"
}
}
}
}
]
}
}
}
])
如果您使用的是 Mongo 版本 5+,那么您可以使用 $getField 来稍微简化语法,下面是一个字段在此语法中的样子:
goup: {
$getField: {
field: 'data',
input: {
'$arrayElemAt': [
{
$filter: {
input: {
$ifNull: [
'$look',
[],
],
},
cond: {
$eq: [
'$$this.art',
'group-id',
],
},
},
},
0,
],
},
},
},
您需要一个聚合操作,该操作具有一个包含以下关键运算符和阶段的管道:
$map
:将look
和answer
数组转换为仅映射k
和v
字段的文档的运算符,对于获得具有以下运算符的哈希映射$arrayToObject
:这使得上述成为可能,即将数组转换为单个文档$mergeObjects
:将顶级字段即_id
、date
、name
、profile
与上面转换后的文档组合在一起$replaceWith
: 管道阶段用上面的指定文档替换根文档
总体而言,您的管道应遵循:
const first = {
$first: {
$split: ['$$this.art', '-']
}
};
const keyExpression = {
$cond: [
{ $eq: [first, 'access'] },
'accessTime',
first
]
};
const pipeline = [
{ $replaceWith: {
$mergeObjects: [
{
_id: '$_id',
date: '$date',
name: '$name',
profile: '$profile',
protocol: '',
group: '',
called: '',
rate: '',
accessTime: '',
},
{ $arrayToObject: {
$map: {
input: '$look',
in: { k: keyExpression, v: '$$this.data' }
}
} },
{ $arrayToObject: {
$map: {
input: '$answer',
in: { k: keyExpression, v: '$$this.data' }
}
} }
]
} }
]