[英]Using mongodb aggregate, how to turn field values into array literal
我们正在查询返回的结果应该是建议搜索词的列表的查询。
当前,我们有一个查询,用于检查多个字段的正则表达式匹配项:
$or:[
{'description.position':/s/i},
{'employer.name':/s/i},
{'hiringManager.profile.name':/s/i}
]
我们希望返回的结果是唯一(不重复)的匹配项数组。
返回的结果如下所示:
I20150311-18:17:14.151(-7)? "fields": {
I20150311-18:17:14.154(-7)? "hiringManager": {
I20150311-18:17:14.157(-7)? "profile": {
I20150311-18:17:14.160(-7)? "name": "Seth Sandler"
I20150311-18:17:14.163(-7)? }
I20150311-18:17:14.167(-7)? },
I20150311-18:17:14.173(-7)? "description": {
I20150311-18:17:14.177(-7)? "position": "Cook"
I20150311-18:17:14.181(-7)? },
I20150311-18:17:14.187(-7)? "employer": {
I20150311-18:17:14.191(-7)? "name": "Employer"
I20150311-18:17:14.195(-7)? },
I20150311-18:17:14.206(-7)? }
I20150311-18:17:14.209(-7)? }
I20150311-18:17:14.212(-7)? {
I20150311-18:17:14.223(-7)? "fields": {
I20150311-18:17:14.226(-7)? "hiringManager": {
I20150311-18:17:14.229(-7)? "profile": {
I20150311-18:17:14.232(-7)? "name": "Seth Sandler"
I20150311-18:17:14.234(-7)? }
I20150311-18:17:14.237(-7)? },
I20150311-18:17:14.240(-7)? "description": {
I20150311-18:17:14.243(-7)? "position": "Cook"
I20150311-18:17:14.246(-7)? },
I20150311-18:17:14.249(-7)? "employer": {
I20150311-18:17:14.252(-7)? "name": "Employer 4"
I20150311-18:17:14.254(-7)? },
I20150311-18:17:14.264(-7)? }
I20150311-18:17:14.267(-7)? }
I20150311-18:17:14.269(-7)? {
I20150311-18:17:14.281(-7)? "fields": {
I20150311-18:17:14.284(-7)? "hiringManager": {
I20150311-18:17:14.287(-7)? "profile": {
I20150311-18:17:14.290(-7)? "name": "Seth Sandler"
I20150311-18:17:14.293(-7)? }
I20150311-18:17:14.295(-7)? },
I20150311-18:17:14.298(-7)? "description": {
I20150311-18:17:14.301(-7)? "position": "Chef"
I20150311-18:17:14.304(-7)? },
I20150311-18:17:14.307(-7)? "employer": {
I20150311-18:17:14.310(-7)? "name": "Emplopyer 3"
I20150311-18:17:14.313(-7)? },
I20150311-18:17:14.321(-7)? }
I20150311-18:17:14.323(-7)? }
I20150311-18:17:14.325(-7)? {
I20150311-18:17:14.334(-7)? "fields": {
I20150311-18:17:14.336(-7)? "hiringManager": {
I20150311-18:17:14.338(-7)? "profile": {
I20150311-18:17:14.340(-7)? "name": "Seth Sandler"
I20150311-18:17:14.342(-7)? }
I20150311-18:17:14.344(-7)? },
I20150311-18:17:14.346(-7)? "description": {
I20150311-18:17:14.348(-7)? "position": "Chef"
I20150311-18:17:14.350(-7)? },
I20150311-18:17:14.353(-7)? "employer": {
I20150311-18:17:14.356(-7)? "name": "Employer"
I20150311-18:17:14.359(-7)? },
I20150311-18:17:14.366(-7)? }
I20150311-18:17:14.369(-7)? }
相反,我们希望结果是hiringManager.profile.name,loyer.name和description.position值的唯一数组。
我们当前的解决方案似乎并不理想(可能性能不佳),并且想知道是否有可能使用mongogodb聚合函数将字段值放入数组中。
当前解决方案(不理想):
aggregate([
{$match: {$or:[ {'description.position':/s/i}, {'employer.name':/s/i}, {'hiringManager.profile.name':/s/i} ]}},
{$group: {_id: 1, positions: {$push: '$description.position'}, employerNames: {$push: '$employer.name'}, hiringManagerNames: {$push:'$hiringManager.profile.name'}}},
{$project: {_id:1, texts: {$setUnion: ['$positions', {$setUnion: ['$employerNames', '$hiringManagerNames']}]}}}
])
})
这样的输出是正确的,但是我们希望有一个更好的聚合函数,可以限制结果。
I20150311-18:25:26.461(-7)? "result": [
I20150311-18:25:26.465(-7)? {
I20150311-18:25:26.468(-7)? "_id": 1,
I20150311-18:25:26.472(-7)? "texts": [
I20150311-18:25:26.478(-7)? "Employer 5",
I20150311-18:25:26.481(-7)? "Employer 4",
I20150311-18:25:26.485(-7)? "Employer 1",
I20150311-18:25:26.488(-7)? "Manager",
I20150311-18:25:26.504(-7)? "Cook",
I20150311-18:25:26.507(-7)? "Chef",
I20150311-18:25:26.530(-7)? ]
I20150311-18:25:26.534(-7)? }
I20150311-18:25:26.538(-7)? ]
通过使“文本”成为$group
管道的实际“分组键”,最好使用另一种技术以获得不同的结果。 在像2.6或更高版本这样的现代MongoDB版本中合理有效地做到这一点有一个技巧:
db.collection.aggregate([
{ "$match": {
"$or":[
{ "description.position":/s/i },
{ "employer.name":/s/i},
{ "hiringManager.profile.name":/s/i }
]
}},
{ "$project": {
"_id": {
"$setDifference": [
{ "$map": {
"input": { "$literal": ["A","B","C" ] },
"as": "type",
"in": { "$cond": [
{ "$eq": [ "$$type", "A" ] },
"$description.position",
{ "$cond": [
{ "$eq": [ "$$type", "B" ] },
"$employer.name",
"$hiringManager.profile.name"
]}
]}
}},
[null]
]
}
}},
{ "$unwind": "$_id" },
{ "$group": { "_id": "$_id" } }
])
因此,将$map
用作通过处理发送给它的$literal
数组["A","B","C"]
来触发“切换”的基础。 因此,对于这些元素中的每一个,都将选择适当的字段作为输出值。
万一这些值中的任何一个为null
或在同一文档中甚至可能是重复的,则$setDifference
运算符将对其进行整理。
每个文档中的结果数组都用$unwind
处理,以便随后将其元素作为分组键传递给$group
,从而为每个“文本”项生成不同的文档。
当然,这里需要权衡的是,管道中的文档将是集合中文档的倍数,每个字段最多有三个可能的值,因此管道中的文档比查询匹配的要多,直到进行明确分组为止。 因此,使用$unwind
涉及成本。
好处是结果中包含单独的文档,通过使用光标输出,文档可以增长到超过16MB的单个“文本”。 当然,首先要写很多文本。
现有聚合操作的另一注记是考虑您已经接受$setUnion
组合字段并获取不同的值,甚至可以通过使用$addToSet
来“减少”输入数组。 这样可以避免使用最终将要删除的重复项来增加阵列。
还应该考虑相同的$setDifference
操作,因为您的$or
条件不能保证“所有”字段都包含有效的字符串,甚至不存在。 如果并非所有字段都有效,那么您还将收到一个明显的null
结果以及其他文本项。
因此,权衡对于您而言更重要。 当前的操作可能会更快,资源占用更少(带有提到的修改),但是替代方法可以迎合更大且可能更可口的响应。 它还允许您“限制”,甚至可以执行诸如“计数”那些“文本”值的出现之类的操作。
@Neil的答案很接近,但似乎还需要其他匹配才能确保结果与原始正则表达式匹配。 我不确定这是否是一个好的解决方案,但这是一个新的工作汇总。 它似乎没有setDifferennce
可以工作,所以我不确定是否需要。
基本上,我会对展开结果进行另一个match
,以确保它们与原始正则表达式匹配。
aggregate([
{ '$match': {
'$or':[
{ 'description.position':/s/i },
{ 'employer.name':/s/i},
{ 'hiringManager.profile.name':/s/i }
]
}},
{ '$project': {
'_id':
{ '$map': {
'input': { '$literal': ['A','B','C' ] },
'as': 'type',
'in': { '$cond': [
{ '$eq': [ '$$type', 'A' ] },
'$description.position',
{ '$cond': [
{ '$eq': [ '$$type', 'B' ] },
'$employer.name',
'$hiringManager.profile.name'
]}
]}
},
}
}},
{ '$unwind': '$_id' },
{ '$match': { '_id':/s/i }},
{ '$group': { '_id': '$_id' } }
]);
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.