基本上你需要
$group
所有的东西都由共同的关键过滤后的共同条件
dayOfWeek
然后查看分组是否确实是“对”的,并且实际上包含了您想要“相交”的两个结果:
db.collection.aggregate([
{ "$match": { "dayOfWeek": "MONDAY" } },
{ "$group": {
"_id": "$channelId",
"docs": { "$push": "$$ROOT" },
"count": { "$sum": 1 }
}},
{ "$match": {
"count": { "$gt": 1 },
"docs": {
"$all": [
{ "$elemMatch": { "productId": "1" } },
{ "$elemMatch": { "productId": "2" } }
]
}
}}
])
一旦“分组”了
$all
具有
$elemMatch
它内部的条件确保在数组中的“分组”文档中满足“两个”条件。还要注意,如果你的意思是“只有两个”,那么你可以找
"count": 2
与简单的
"count": { "$gt": 1 }
在这里的例子中,这意味着“分组”至少与“某物”配对,如果不完全是“二”。
它基本上返回一个结果,每个分组对应的文档如下:
{
"_id" : "ID-A",
"docs" : [
{
"_id" : ObjectId("5b22f455fe0315289f716483"),
"channelId" : "ID-A",
"dayOfWeek" : "MONDAY",
"productId" : "1"
},
{
"_id" : ObjectId("5b22f455fe0315289f716484"),
"channelId" : "ID-A",
"dayOfWeek" : "MONDAY",
"productId" : "2"
}
],
"count" : 2
}
如果你最终只需要“文档”,那么你可以在
$unwind
具有
$replaceRoot
如果您有超过3.4的支持MongoDB版本:
db.collection.aggregate([
{ "$match": { "dayOfWeek": "MONDAY" } },
{ "$group": {
"_id": "$channelId",
"docs": { "$push": "$$ROOT" },
"count": { "$sum": 1 }
}},
{ "$match": {
"count": { "$gt": 1 },
"docs": {
"$all": [
{ "$elemMatch": { "productId": "1" } },
{ "$elemMatch": { "productId": "2" } }
]
}
}},
{ "$unwind": "$docs" },
{ "$replaceRoot": { "newRoot": "$docs" } }
])
或与
$project
并明确命名所有不需要的字段:
db.collection.aggregate([
{ "$match": { "dayOfWeek": "MONDAY" } },
{ "$group": {
"_id": "$channelId",
"docs": {
"$push": {
"_id": "$_id",
"channelId": "$channelId",
"dayOfWeek": "$dayOfWeek",
"productId": "$productId
}
},
"count": { "$sum": 1 }
}},
{ "$match": {
"count": { "$gt": 1 },
"docs": {
"$all": [
{ "$elemMatch": { "productId": "1" } },
{ "$elemMatch": { "productId": "2" } }
]
}
}},
{ "$unwind": "$docs" },
{ "$project": {
"_id": "$docs._id",
"channelId": "$docs.channelId",
"dayOfWeek": "$docs.dayOfWeek",
"productId": "$docs.productId"
}}
])
实际上,在最后一种形式中,该语句基本上与MongoDB的每个版本兼容,因为聚合框架是用版本2.2发布的。
或者你也可以用
$setIsSubset
只要您有MongoDB 3.6或更高版本,
$expr
以下内容:
db.collection.aggregate([
{ "$match": { "dayOfWeek": "MONDAY" } },
{ "$group": {
"_id": "$channelId",
"docs": { "$push": "$$ROOT" },
"count": { "$sum": 1 }
}},
{ "$match": {
"count": { "$gt": 0 },
"$expr": {
"$setIsSubset": [ [ "1", "2" ], "$docs.productId" ]
}
}}
])
你甚至可以改变它
$redact
或者使用
$项目
接着是另一个
$match
但这并不是重点,就像你可能认为“设置操作符”这些实际上并不最适合你在这里寻找的特定结果。
请注意,尽管任何类型的“交集”或“子集”基本上都依赖于能够相互比较文档。这实质上意味着将“分组”的东西放入一个数组中进行比较。如果实际结果大小导致这样一个“分组”到
exceed the BSON limit
它们实际上不能使用这样的和方法,而且实际上除了通过光标将匹配的文档加载到初始查询过滤器并进行检查之外,没有其他选择。
因此,对于“完整性”,在这种情况下,您可以考虑
$lookup
可用于使用“自引用联接”,而不是使用
$push
要累积匹配的文档:
db.collection.aggregate([
{ "$match": { "dayOfWeek": "MONDAY" } },
{ "$group": {
"_id": "$channelId",
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } }, // keep only "multiple" groups
{ "$lookup": {
"from": "collection",
"localField": "_id",
"foreignField": "channelId",
"as": "docs"
}},
{ "$unwind": "$docs" },
// ** See note below about the $match **
//{ "$match": { "docs.productId": { "$in": [ "1", "2" ] } } },
])
这里的优势是
"docs"
从来没有按照
$lookup
+
$unwind
Coalescence
它本质上“卷起”了
unwinding
行动要在
$查找
本身。这样,您就可以通过
推送美元
操作,但已“分离”成自己的文档,且不会破坏
16MB BSON limit
.
但是,在这种形式下,“集合”实际上无法进行比较,因为您需要“数组”来查看“分组”项是否在“集合”中。所以实际上是为了避免极限破坏而避免的“分组”行为。尽管如此,这通常比简单地将匹配文档的光标迭代到
"MONDAY"
单独使用,因为您已经有“分组”结果的指示
"channelId"
.
唯一可以做的其他比较是使用附加的
$匹配
使用
$in
.这将再次“卷起”到实际
$查找
操作只有效地返回那些也符合该条件的文档。然而,结果本质上是“否定的”,因为它实际上是可以用
美元
对于初始查询,当然意味着
“仅限”
那些文件包含
"1"
或
"2"
而不是其他任何关于这些值的“子集的一部分”。