我有一个聚合管道查询(我已经删除了不必要的内容),它在使用$expr时可以使用,但在不使用$expr时就不能使用。但是,为了获得更好的性能,我希望避免使用$expr,因此将使用索引。从逻辑上讲,规则和资源之间存在多对多的关系。我想总结一下每个规则的资源成本。这里的问题是在不使用表达式的情况下匹配分组数组内的资源。
使用$Expr:
db.collection.aggregate([
{'$group': {'_id': {'rule_id': '$rule_id'}, 'rule_id': {'$first': '$rule_id'}, 'resources_ids': {'$push': '$resource_id'}}},
{'$lookup':
{'from': 'other_collection',
'let': {'resources_ids': '$resources_ids'},
'pipeline': [
{'$match':
{'$expr":
{'$and': [
{'$in':['$resource_id', '$$resources_ids']}
]}
}
},
{'$group': {'_id': {}, 'total_cost': {'$sum': '$cost'}}}], 'as': 'results'}}])
不使用$expr:
db.collection.aggregate([
{'$group': {'_id': {'rule_id': '$rule_id'}, 'rule_id': {'$first': '$rule_id'}, 'resources_ids': {'$push': '$resource_id'}}},
{'$lookup':
{'from': 'cost_data',
'let': {'resources_ids': '$resources_ids'},
'pipeline': [
{'$match':
{'$and': [
{'resource_id': {'$in': '$$resources_ids'}},
]}
},
{'$group': {'_id': {}, 'total_cost': {'$sum': '$cost'}}}], 'as': 'results'}}])
1条答案
按热度按时间qltillow1#
我认为@rickhg12hs的评论给出了正确的答案。这里应该没有任何需要
$expr
的必要。当localField
是阵列时,localField
/foreignField
语法将正常工作,而无需使用$unwind
(或使用$expr
),如此处所述。因此,$lookup
的匹配组件实际上可以看起来如下所示:您可以比较this syntax above和more verbose
pipeline
/$expr
version的输出,以发现它们是相同的。我脑海中浮现出一些其他的想法。首先,您可以将
localField
/foreignField
语法与pipeline
语法结合使用,以便第二个$group
仍然可以嵌套在$lookup
中。这将使$lookup
阶段的最终版本结构如下:该组件的操场演示是here。
第二件事是,使用索引执行
$lookup
很重要,可能会提高性能,但它可能不会使此操作变得“快速”。如上所述,该聚合将执行完整集合扫描,以处理源集合中的所有文档。(即使另一个集合中的索引用于$lookup
,您仍将在此源集合的explain
输出中看到COLLSCAN
)。最后,另一个集合上的索引可能应该是
{ resource_id: 1, cost: 1 }
。这应该允许数据库在执行$lookup
时覆盖查询,并避免完全获取这些文档。编辑以解决此评论:
在匹配中不需要$expr。我已经做过了。然而,在这里,因为我通过管道构建的数组不允许我在没有$expr的情况下在匹配中使用它。
这是不正确的。具体地说,数组的来源在这里并不相关。对于
$lookup
阶段,该数组是源文档中的一个字段,还是在较早的管道阶段中生成的,这一点都不重要。事实上,它甚至不知道该数组来自哪里,只知道它是传递给它的生成文档中的一个字段。相反,您所描述的是
$match
本身的行为。来自the documentation:$match
获取指定查询条件的文档。查询语法与读操作查询语法相同;即$match
不接受原始聚合表达式。相反,可以使用$expr
查询表达式在$match
中包含聚合表达式。换句话说,如果没有
$expr
,您目前不能从文档中引用任何字段(无论它们来自哪里,或者它们具有什么类型和值)。但这一事实应该与您的用例基本无关。您可以使用
localField
/foreignField
语法进行此数组匹配。如果需要匹配其他筛选器,则还可以在相同的$lookup
中使用let
/pipeline
语法。Here is an arbitrary demonstration of that(请注意,由于otherVal
不匹配,_id: 4
文档不匹配)。还值得注意的是,
$expr
本身通常并不排除索引的使用。不幸的是,目前的一个例外似乎是$in
(reference)。不过,如果您将$lookup
匹配的那部分放入localField
/foreignField
参数中,这对您来说应该不重要。