主要集合是零售商,它包含一个商店数组。每家商店都包含一系列优惠(您可以在这家商店购买)。这提供了数组具有大小的数组。 (见下例)
现在,我尝试查找大小为 L
的所有优惠。
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"stores" : [
{
"_id" : ObjectId("56f277b5279871c20b8b4783"),
"offers" : [
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"size": [
"XS",
"S",
"M"
]
},
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"size": [
"S",
"L",
"XL"
]
}
]
}
}
db.getCollection('retailers').find({'stores.offers.size': 'L'})
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"stores" : [
{
"_id" : ObjectId("56f277b5279871c20b8b4783"),
"offers" : [
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"size": [
"S",
"L",
"XL"
]
}
]
}
}
size
XS、X 和 M 不匹配的报价。最佳答案
因此,您的查询实际上选择了“文档”,就像它应该的那样。但是您正在寻找的是“过滤数组”,以便返回的元素仅与查询条件匹配。
真正的答案当然是,除非您真的通过过滤掉这些细节来节省大量带宽,否则您甚至不应该尝试,或者至少不要尝试超过第一个位置匹配。
MongoDB 有一个 positional $
operator,它将从查询条件中返回匹配索引处的数组元素。但是,这仅返回“外部”最数组元素的“第一个”匹配索引。
db.getCollection('retailers').find(
{ 'stores.offers.size': 'L'},
{ 'stores.$': 1 }
)
"stores"
数组位置。因此,如果有多个“商店”条目,则只会返回包含匹配条件的元素中的“一个”。 但是 ,这对 "offers"
的内部数组没有任何作用,因此仍然会返回匹配的 "stores"
数组中的每个“报价”。db.getCollection('retailers').find(
{ 'stores.offers.size': 'L'},
{ 'stores.$.offers.$': 1 }
)
$filter
操作:db.getCollection('retailers').aggregate([
{ "$match": { "stores.offers.size": "L" } },
{ "$project": {
"stores": {
"$filter": {
"input": {
"$map": {
"input": "$stores",
"as": "store",
"in": {
"_id": "$$store._id",
"offers": {
"$filter": {
"input": "$$store.offers",
"as": "offer",
"cond": {
"$setIsSubset": [ ["L"], "$$offer.size" ]
}
}
}
}
}
},
"as": "store",
"cond": { "$ne": [ "$$store.offers", [] ]}
}
}
}}
])
$map
和 $setDifference
5:67920db.getCollection('retailers').aggregate([
{ "$match": { "stores.offers.size": "L" } },
{ "$project": {
"stores": {
"$setDifference": [
{ "$map": {
"input": {
"$map": {
"input": "$stores",
"as": "store",
"in": {
"_id": "$$store._id",
"offers": {
"$setDifference": [
{ "$map": {
"input": "$$store.offers",
"as": "offer",
"in": {
"$cond": {
"if": { "$setIsSubset": [ ["L"], "$$offer.size" ] },
"then": "$$offer",
"else": false
}
}
}},
[false]
]
}
}
}
},
"as": "store",
"in": {
"$cond": {
"if": { "$ne": [ "$$store.offers", [] ] },
"then": "$$store",
"else": false
}
}
}},
[false]
]
}
}}
])
db.getCollection('retailers').aggregate([
{ "$match": { "stores.offers.size": "L" } },
{ "$unwind": "$stores" },
{ "$unwind": "$stores.offers" },
{ "$match": { "stores.offers.size": "L" } },
{ "$group": {
"_id": {
"_id": "$_id",
"storeId": "$stores._id",
},
"offers": { "$push": "$stores.offers" }
}},
{ "$group": {
"_id": "$_id._id",
"stores": {
"$push": {
"_id": "$_id.storeId",
"offers": "$offers"
}
}
}}
])
$filter
是去这里的方式,因为它的设计考虑到了目的。由于数组有多个级别,因此您需要在每个级别应用它。因此,首先您要深入研究 "offers"
中的每个 "stores"
以检查和 $filter
该内容。"size"
数组是否包含我要查找的元素”。在这个逻辑上下文中,要做的简短事情是使用 $setIsSubset
操作将 ["L"]
的数组(“set”)与目标数组进行比较。如果条件是 true
(它包含 "L"),则保留 "offers"
的数组元素并在结果中返回。$filter
中,您将查看先前 $filter
的结果是否为 []
返回了一个空数组 "offers"
。如果它不为空,则返回该元素,否则将其删除。$filter
,您可以使用 $map
检查每个元素,然后使用 $setDifference
过滤掉作为 false
返回的任何元素。$map
将返回整个数组,但 $cond
操作只是决定是返回元素还是返回 false
值。在将 $setDifference
与 [false]
的单个元素“集合”进行比较时,将删除返回数组中的所有 false
元素。$unwind
,仅出于此目的,您应该 而不是 出于此目的“仅”使用聚合框架。$group
阶段,“第一个”重新构建内部阵列,然后重新构建外部阵列。在所有级别都有不同的 _id
值,因此只需要在分组的每个级别都包含这些值。$unwind
是 非常昂贵的 。虽然它仍然有目的,但它的主要用途不是对每个文档进行这种过滤。事实上,在现代版本中,只有当数组的元素需要成为“分组键”本身的一部分时才应该使用。$match
之外,它们还使用“单一”管道阶段来进行“过滤”。由此产生的效果比 .find()
的标准形式稍微多一点开销。db.getCollection('retailers').find(
{ 'stores.offers.size': 'L'},
{ 'stores.$': 1 }
).forEach(function(doc) {
// Technically this is only "one" store. So omit the projection
// if you wanted more than "one" match
doc.stores = doc.stores.filter(function(store) {
store.offers = store.offers.filter(function(offer) {
return offer.size.indexOf("L") != -1;
});
return store.offers.length != 0;
});
printjson(doc);
})
$match
和 $project
的现代版本中执行此操作,否则在服务器上处理的“成本”将大大超过通过首先剥离不匹配元素来减少网络开销的“ yield ”。{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"stores" : [
{
"_id" : ObjectId("56f277b5279871c20b8b4783"),
"offers" : [
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"size" : [
"S",
"L",
"XL"
]
}
]
}
]
}
https://stackoverflow.com/questions/36229123/
相关文章:
performance - MongoDB 'count()' 非常慢。我们如何改进/解决它?
mongodb - 服务器在 SASL 身份验证步骤 : Authentication failed
mongodb - meteor :如何备份我的 mongo 数据库
mongodb - 将 MongoDB 集合的子集保存到另一个集合
mysql - 将数据库从 mysql 转换为 mongoDb
mongodb - 如何使用 docker-compose 为 mongo 数据库播种?
node.js - 为什么建议不要在 Node.js 代码的任何地方关闭 MongoDB 连接?