MongoDB 聚合 - 匹配数组中的值
MongoDB Aggregation - match if value in array
我有一个正在执行聚合的集合,我基本上已经将其归结为
{array:[1,2,3], value: 1},
{array:[1,2,3], value: 4}
我如何执行聚合匹配来检查值是否在数组中?我尝试使用 {$match: {"array: {$in: ["$value"]}}}
但它没有找到任何东西。
我希望输出(如果使用上面的例子)是:
{array:[1,2,3], value:1}
一种更有效的方法将涉及使用 $redact
运算符的单个管道,如下所示:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{
"$setIsSubset": [
["$value"],
"$array"
]
},
"$$KEEP",
"$$PRUNE"
]
}
}
])
对于不支持 $redact
(版本 < 2.6)的 MongoDB 的早期版本,请考虑使用 $unwind
运算符:
db.collection.aggregate([
{ "$unwind": "$array" },
{
"$project": {
"isInArray": {
"$cond": [
{ "$eq": [ "$array", "$value" ] },
1,
0
]
},
"value": 1,
"array": 1
}
},
{ "$sort": { "isInArray": -1 } },
{
"$group": {
"_id": {
"_id": "$_id",
"value": "$value"
},
"array": { "$push": "$array" },
"isInArray": { "$first": "$isInArray" }
}
},
{ "$match": { "isInArray": 1 } },
{ "$project": { "value": "$_id.value", "array": 1, "_id": 0 } }
])
根据@chridam 的回答略有不同:
db.test.aggregate([
{ "$unwind": "$array" },
{ "$group": {
_id: { "_id": "$_id", "value": "$value" },
array: { $push: "$array" },
mcount: { $sum: {$cond: [{$eq: ["$value","$array"]},1,0]}}
}
},
{ $match: {mcount: {$gt: 0}}},
{ "$project": { "value": "$_id.value", "array": 1, "_id": 0 }}
])
想法是 $unwind
和 $group
返回数组,在 mcount
中计算与该值匹配的项目数。之后,mcount > 0
上的简单 $match
将过滤掉不需要的文档。
如前所述,$where
是一个不错的选择,您不需要继续聚合管道中的逻辑。
但是如果你这样做,那么使用 $redact
, with $map
to transform the "value" into an array and use of $setIsSubSet
to compare. It is the fastest way to do this since you do not need to duplicate documents using $unwind
:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": { "$setIsSubset": [
{ "$map": {
"input": { "$literal": ["A"] },
"as": "a",
"in": "$value"
}},
"$array"
]},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
$redact
管道运算符允许将 $cond
and uses the special operations $$KEEP
to "keep" the document where the logical condition is true or $$PRUNE
中的逻辑条件处理到 "remove" 条件为假的文档。
这允许它像 $project
with a subsequent $match
一样工作,但在一个更高效的流水线阶段。
考虑到这些是本机编码的运算符而不是 JavaScript 那么它可能是 "the" 执行匹配的最快方式。因此,如果您使用的是 MongoDB 2.6 或更高版本,那么这就是您应该在文档中比较这些元素的方式。
试试 $eq 和 $setIntersection 的组合
{$group :{
_id: "$id",
yourName : { $sum:
{ $cond :[
{$and : [
{$eq:[{$setIntersection : ["$someArrayField", ["$value"]] },["$value"]]}
]
},1,0]
}
}
}
回答有点晚了,但这提供了另一种解决方案:
通过单独使用 addFields 和 match,这比编辑更灵活。您可以公开几个字段,然后根据结果一起使用其他匹配逻辑。
db.applications.aggregate([
{$addFields: {"containsValueInArray": {$cond:[{$setIsSubset: [["valueToMatch"], "$arrayToMatchIn"]},true,false]}}},
{$match: {"containsValueInArray":true}}
]);
3.6版本可以在正则查询中使用聚合表达式
db.collection_name.find({"$expr": {"$in": ["$value", "$array"]}})
使用聚合:
您可以在当前 3.6
版本中使用 $match + $expr
。
db.collection_name.aggregate({"$match": {"$expr": {"$in": ["$value", "$array"]}}})
您可以在 3.4
版本中尝试 $redact + $in
表达式。
db.collection_name.aggregate({
"$redact": {
"$cond": [
{
"$in": [
"$value",
"$array"
]
},
"$$KEEP",
"$$PRUNE"
]
}
})
我更喜欢不分组,自 v.3.2 以来有一个简单的方法
...aggregate([
{
$addFields: {
arrayFilter: {
$filter: {
input: '$array',
as: 'item',
cond: ['$$item', '$value']
}
}
}
},
{
$unwind: '$arrayFilter'
},
{
$project: {
arrayFilter: 0
}
}
]);
- 添加临时筛选字段
- $unwind 在结果数组上(删除带有空数组的管道结果)
- (可选)通过项目
从结果中删除过滤字段
您可以使用简单的 $project & $match
db.test.aggregate([{
$project: {
arrayValue: 1,
value: 1,
"has_same_value" : { $in: ["$value", "$arrayValue"] }
}
},
{
$match: {has_same_value: true}
},
{
$project: {has_same_value: 0}
}])
"$match": { "name": { "$in":["Rio","Raja"] }} }])
我有一个正在执行聚合的集合,我基本上已经将其归结为
{array:[1,2,3], value: 1},
{array:[1,2,3], value: 4}
我如何执行聚合匹配来检查值是否在数组中?我尝试使用 {$match: {"array: {$in: ["$value"]}}}
但它没有找到任何东西。
我希望输出(如果使用上面的例子)是:
{array:[1,2,3], value:1}
一种更有效的方法将涉及使用 $redact
运算符的单个管道,如下所示:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{
"$setIsSubset": [
["$value"],
"$array"
]
},
"$$KEEP",
"$$PRUNE"
]
}
}
])
对于不支持 $redact
(版本 < 2.6)的 MongoDB 的早期版本,请考虑使用 $unwind
运算符:
db.collection.aggregate([
{ "$unwind": "$array" },
{
"$project": {
"isInArray": {
"$cond": [
{ "$eq": [ "$array", "$value" ] },
1,
0
]
},
"value": 1,
"array": 1
}
},
{ "$sort": { "isInArray": -1 } },
{
"$group": {
"_id": {
"_id": "$_id",
"value": "$value"
},
"array": { "$push": "$array" },
"isInArray": { "$first": "$isInArray" }
}
},
{ "$match": { "isInArray": 1 } },
{ "$project": { "value": "$_id.value", "array": 1, "_id": 0 } }
])
根据@chridam 的回答略有不同:
db.test.aggregate([
{ "$unwind": "$array" },
{ "$group": {
_id: { "_id": "$_id", "value": "$value" },
array: { $push: "$array" },
mcount: { $sum: {$cond: [{$eq: ["$value","$array"]},1,0]}}
}
},
{ $match: {mcount: {$gt: 0}}},
{ "$project": { "value": "$_id.value", "array": 1, "_id": 0 }}
])
想法是 $unwind
和 $group
返回数组,在 mcount
中计算与该值匹配的项目数。之后,mcount > 0
上的简单 $match
将过滤掉不需要的文档。
如前所述,$where
是一个不错的选择,您不需要继续聚合管道中的逻辑。
但是如果你这样做,那么使用 $redact
, with $map
to transform the "value" into an array and use of $setIsSubSet
to compare. It is the fastest way to do this since you do not need to duplicate documents using $unwind
:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": { "$setIsSubset": [
{ "$map": {
"input": { "$literal": ["A"] },
"as": "a",
"in": "$value"
}},
"$array"
]},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
$redact
管道运算符允许将 $cond
and uses the special operations $$KEEP
to "keep" the document where the logical condition is true or $$PRUNE
中的逻辑条件处理到 "remove" 条件为假的文档。
这允许它像 $project
with a subsequent $match
一样工作,但在一个更高效的流水线阶段。
考虑到这些是本机编码的运算符而不是 JavaScript 那么它可能是 "the" 执行匹配的最快方式。因此,如果您使用的是 MongoDB 2.6 或更高版本,那么这就是您应该在文档中比较这些元素的方式。
试试 $eq 和 $setIntersection 的组合
{$group :{
_id: "$id",
yourName : { $sum:
{ $cond :[
{$and : [
{$eq:[{$setIntersection : ["$someArrayField", ["$value"]] },["$value"]]}
]
},1,0]
}
} }
回答有点晚了,但这提供了另一种解决方案:
通过单独使用 addFields 和 match,这比编辑更灵活。您可以公开几个字段,然后根据结果一起使用其他匹配逻辑。
db.applications.aggregate([
{$addFields: {"containsValueInArray": {$cond:[{$setIsSubset: [["valueToMatch"], "$arrayToMatchIn"]},true,false]}}},
{$match: {"containsValueInArray":true}}
]);
3.6版本可以在正则查询中使用聚合表达式
db.collection_name.find({"$expr": {"$in": ["$value", "$array"]}})
使用聚合:
您可以在当前 3.6
版本中使用 $match + $expr
。
db.collection_name.aggregate({"$match": {"$expr": {"$in": ["$value", "$array"]}}})
您可以在 3.4
版本中尝试 $redact + $in
表达式。
db.collection_name.aggregate({
"$redact": {
"$cond": [
{
"$in": [
"$value",
"$array"
]
},
"$$KEEP",
"$$PRUNE"
]
}
})
我更喜欢不分组,自 v.3.2 以来有一个简单的方法
...aggregate([
{
$addFields: {
arrayFilter: {
$filter: {
input: '$array',
as: 'item',
cond: ['$$item', '$value']
}
}
}
},
{
$unwind: '$arrayFilter'
},
{
$project: {
arrayFilter: 0
}
}
]);
- 添加临时筛选字段
- $unwind 在结果数组上(删除带有空数组的管道结果)
- (可选)通过项目 从结果中删除过滤字段
您可以使用简单的 $project & $match
db.test.aggregate([{
$project: {
arrayValue: 1,
value: 1,
"has_same_value" : { $in: ["$value", "$arrayValue"] }
}
},
{
$match: {has_same_value: true}
},
{
$project: {has_same_value: 0}
}])
"$match": { "name": { "$in":["Rio","Raja"] }} }])