$ lookup之后的聚合过滤器

如何在$ lookup之后添加过滤器,或者有其他方法可以执行此操作?

我的数据收集测试是:

{ "_id" : ObjectId("570557d4094a4514fc1291d6"), "id" : 100, "value" : "0", "contain" : [ ] }

{ "_id" : ObjectId("570557d4094a4514fc1291d7"), "id" : 110, "value" : "1", "contain" : [ 100 ] }

{ "_id" : ObjectId("570557d4094a4514fc1291d8"), "id" : 120, "value" : "1", "contain" : [ 100 ] }

{ "_id" : ObjectId("570557d4094a4514fc1291d9"), "id" : 121, "value" : "2", "contain" : [ 100, 120 ] }

我选择ID 100并汇总孩子:

db.test.aggregate([ {

$match : {

id: 100

}

}, {

$lookup : {

from : "test",

localField : "id",

foreignField : "contain",

as : "childs"

}

}]);

我回来了:

{  

"_id":ObjectId("570557d4094a4514fc1291d6"),

"id":100,

"value":"0",

"contain":[ ],

"childs":[ {

"_id":ObjectId("570557d4094a4514fc1291d7"),

"id":110,

"value":"1",

"contain":[ 100 ]

},

{

"_id":ObjectId("570557d4094a4514fc1291d8"),

"id":120,

"value":"1",

"contain":[ 100 ]

},

{

"_id":ObjectId("570557d4094a4514fc1291d9"),

"id":121,

"value":"2",

"contain":[ 100, 120 ]

}

]

}

但我只希望与“值:1”匹配的子项

最后,我希望得到以下结果:

{  

"_id":ObjectId("570557d4094a4514fc1291d6"),

"id":100,

"value":"0",

"contain":[ ],

"childs":[ {

"_id":ObjectId("570557d4094a4514fc1291d7"),

"id":110,

"value":"1",

"contain":[ 100 ]

},

{

"_id":ObjectId("570557d4094a4514fc1291d8"),

"id":120,

"value":"1",

"contain":[ 100 ]

}

]

}

回答:

这里的问题实际上是关于一些不同的东西,根本不需要$lookup。但是,对于仅从“$lookup之后过滤”标题到达此处的任何人,这些都是适合您的技术:

回答:

db.test.aggregate([

{ "$match": { "id": 100 } },

{ "$lookup": {

"from": "test",

"let": { "id": "$id" },

"pipeline": [

{ "$match": {

"value": "1",

"$expr": { "$in": [ "$$id", "$contain" ] }

}}

],

"as": "childs"

}}

])

回答:

db.test.aggregate([

{ "$match": { "id": 100 } },

{ "$lookup": {

"from": "test",

"localField": "id",

"foreignField": "contain",

"as": "childs"

}},

{ "$unwind": "$childs" },

{ "$match": { "childs.value": "1" } },

{ "$group": {

"_id": "$_id",

"id": { "$first": "$id" },

"value": { "$first": "$value" },

"contain": { "$first": "$contain" },

"childs": { "$push": "$childs" }

}}

])

如果您质疑为什么不$unwind使用$filter该数组,请阅读[Aggregate$lookup匹配管道中文档的总大小超出了所有文档的最大文档大小,以了解为什么这是通常必需的并且是最佳方法。

对于MongoDB 3.6及更高版本,通常是更具表现力的“子管道”,它是您想要在所有内容完全返回数组之前“过滤”外部集合的结果的工具。

回到答案,尽管实际上描述了为什么所提问题根本不需要“不参加”。


原版的

$lookup像这样使用并不是在这里执行所需操作的最“有效”方法。但是稍后会详细介绍。

作为一个基本概念,只需$filter在结果数组上使用:

db.test.aggregate([ 

{ "$match": { "id": 100 } },

{ "$lookup": {

"from": "test",

"localField": "id",

"foreignField": "contain",

"as": "childs"

}},

{ "$project": {

"id": 1,

"value": 1,

"contain": 1,

"childs": {

"$filter": {

"input": "$childs",

"as": "child",

"cond": { "$eq": [ "$$child.value", "1" ] }

}

}

}}

]);

$redact改为使用:

db.test.aggregate([ 

{ "$match": { "id": 100 } },

{ "$lookup": {

"from": "test",

"localField": "id",

"foreignField": "contain",

"as": "childs"

}},

{ "$redact": {

"$cond": {

"if": {

"$or": [

{ "$eq": [ "$value", "0" ] },

{ "$eq": [ "$value", "1" ] }

]

},

"then": "$$DESCEND",

"else": "$$PRUNE"

}

}}

]);

两者都得到相同的结果:

{  

"_id":ObjectId("570557d4094a4514fc1291d6"),

"id":100,

"value":"0",

"contain":[ ],

"childs":[ {

"_id":ObjectId("570557d4094a4514fc1291d7"),

"id":110,

"value":"1",

"contain":[ 100 ]

},

{

"_id":ObjectId("570557d4094a4514fc1291d8"),

"id":120,

"value":"1",

"contain":[ 100 ]

}

]

}

最重要的是,$lookup它本身不能“还”查询以仅选择某些数据。因此,所有“过滤”都需要在$lookup

但是,实际上对于这种类型的“自我连接”,您最好根本不使用它$lookup,并且完全避免额外读取和“哈希合并”的开销。只需获取相关项目,即可$group

db.test.aggregate([

{ "$match": {

"$or": [

{ "id": 100 },

{ "contain.0": 100, "value": "1" }

]

}},

{ "$group": {

"_id": {

"$cond": {

"if": { "$eq": [ "$value", "0" ] },

"then": "$id",

"else": { "$arrayElemAt": [ "$contain", 0 ] }

}

},

"value": { "$first": { "$literal": "0"} },

"childs": {

"$push": {

"$cond": {

"if": { "$ne": [ "$value", "0" ] },

"then": "$$ROOT",

"else": null

}

}

}

}},

{ "$project": {

"value": 1,

"childs": {

"$filter": {

"input": "$childs",

"as": "child",

"cond": { "$ne": [ "$$child", null ] }

}

}

}}

])

这只是有所不同,因为我故意删除了多余的字段。如果您确实要添加它们,请自己添加:

{

"_id" : 100,

"value" : "0",

"childs" : [

{

"_id" : ObjectId("570557d4094a4514fc1291d7"),

"id" : 110,

"value" : "1",

"contain" : [ 100 ]

},

{

"_id" : ObjectId("570557d4094a4514fc1291d8"),

"id" : 120,

"value" : "1",

"contain" : [ 100 ]

}

]

}

因此,这里唯一真正的问题是“过滤”null来自数组的任何结果,该数组是在当前文档正在parent处理到时创建的$push


您在这里似乎还缺少的是,您要查找的结果根本不需要聚合或“子查询”。您已经结束或可能在其他地方找到的结构是“设计的”,因此您可以在单个查询请求中获得一个“节点”及其所有“子代”。

这意味着只有“ query”才是真正需要的,而数据收集(由于没有真正“减少”任何内容而已发生的一切)只是迭代游标结果的功能:

var result = {};

db.test.find({

"$or": [

{ "id": 100 },

{ "contain.0": 100, "value": "1" }

]

}).sort({ "contain.0": 1 }).forEach(function(doc) {

if ( doc.id == 100 ) {

result = doc;

result.childs = []

} else {

result.childs.push(doc)

}

})

printjson(result);

这做的完全一样:

{

"_id" : ObjectId("570557d4094a4514fc1291d6"),

"id" : 100,

"value" : "0",

"contain" : [ ],

"childs" : [

{

"_id" : ObjectId("570557d4094a4514fc1291d7"),

"id" : 110,

"value" : "1",

"contain" : [

100

]

},

{

"_id" : ObjectId("570557d4094a4514fc1291d8"),

"id" : 120,

"value" : "1",

"contain" : [

100

]

}

]

}

并证明您在这里真正需要做的就是发出“单个”查询以选择父项和子项。返回的数据是相同的,并且您在服务器或客户端上所做的所有工作都在“按摩”为另一种收集的格式。

这是在这种情况下您可以“思考”如何在“关系”数据库中进行操作的情况之一,而没有意识到由于数据的存储方式已“改变”,因此您不再需要使用同样的方法。

这正是文档示例 “带有子引用的模型树结构”结构中 的要点,它使在一个查询中选择父项和子项变得容易。

以上是 $ lookup之后的聚合过滤器 的全部内容, 来源链接: utcz.com/qa/397300.html

回到顶部