How to project specific fields in array on filtered lookup

丶灬走出姿态 提交于 2019-12-24 08:06:16

问题


I am combining two collection using $lookup and I am able to apply a filter on the 'joined' collection and use a projection on the starting collection , but I did not manage to combine both a filter and a projection on the joined collection trying several approach using $redact and $project. I have looked intensively on stackoverflow, but I could not find this combination. Here come an example: collection meta:

{ "Exp": "A","test": "OK","date": "3"}
{ "Exp": "B","test": "OK","date": "5"}
{ "Exp": "C","test": "Failed","date": "9"}

collection merge (to be joined'):

{ "Exp": "A","M1": "2","M2": "test","T": "1"}
{ "Exp": "A","M1": "2","M2": "val", "T": "2"}
{ "Exp": "A","M1": "2", "M2": "val","T": "3"}
{ "Exp": "B","M1": "1", "M2": "test","M4": "1","T": "1"}
{ "Exp": "B","M1": "1","M2": "val","M4": "1","T": "2"}
{ "Exp": "B","M1": "1","M2": "val","M4": "1","T": "3"}
{ "Exp": "C","M1": "2","M2": "test","M3": "2","T": "1"}
{ "Exp": "C","M1": "2","M2": "val","M3": "2","T": "2"}
{ "Exp": "C","M1": "2","M2": "val","M3": "2","T": "3"}

And the query is: Join 'meta' and 'merge' using 'Exp', and select only those where meta.test="OK" and merge.M2="val", but show only meta.Exp, meta.test and merge.M1, merge.M2, and merge.T.

This is how far I got:

db.meta.aggregate([
{ $match: { test: "OK" }},
{ $lookup:
  { from: "merge",
    localField: "Exp",
    foreignField: "Exp",
    as: "kin"
  }
 },
 { $project:
   { "Exp": true,
    "test": true,
    kin :
     { $filter:
      { input: "$kin",
        as: "kin",
        cond: { $eq: [ "$$kin.M2", "val" ]} 
      }
     }
    }
  }
])

but trying to include an additional projection on merge.M1, merge.M2, and merge.T together with the filter keeps failing. The result should be:

{  "Exp" : "B",
  "test" : "OK",
   "kin" : [ 
      {  "M1" : "1",
         "M2" : "val",
         "T" : "2"}, 
      {  "M1" : "1",
         "M2" : "val",
         "T" : "3"}]
 }
 { "Exp" : "A",
   "test" : "OK",
   "kin" : [ 
      { "M1" : "2",
        "M2" : "val",
        "T" : "2"}, 
      { "M1" : "2",
        "M2" : "val",
        "T" : "3"}
    ]
 } 

Thanks for hints! Jordi


回答1:


You use $map to specify which fields of an array to return:

db.meta.aggregate([
  { "$match": { test: "OK" }},
  { "$lookup":{ 
    "from": "merge",
    "localField": "Exp",
    "foreignField": "Exp",
    "as": "kin"
  }},
  { "$project": {
    "Exp": 1,
    "test": 1,  
    "kin": {
      "$map": {
        "input": {
          "$filter": {
            "input": "$kin",
            "as": "k",
            "cond": { "$eq": [ "$$k.M2", "val" ] }
          }  
        },
        "as": "k",
        "in":  {
          "M1": "$$k.M1",
          "M2": "$$k.M2",
          "T": "$$k.T"  
        }  
      }  
    }  
  }}
])

Which returns for you:

/* 1 */
{
    "_id" : ObjectId("5979a8857dcd6a5f6a9b4b9a"),
    "Exp" : "A",
    "test" : "OK",
    "kin" : [ 
        {
            "M1" : "2",
            "M2" : "val",
            "T" : "2"
        }, 
        {
            "M1" : "2",
            "M2" : "val",
            "T" : "3"
        }
    ]
}

/* 2 */
{
    "_id" : ObjectId("5979a8857dcd6a5f6a9b4b9b"),
    "Exp" : "B",
    "test" : "OK",
    "kin" : [ 
        {
            "M1" : "1",
            "M2" : "val",
            "T" : "2"
        }, 
        {
            "M1" : "1",
            "M2" : "val",
            "T" : "3"
        }
    ]
}



回答2:


An alternative way, proposed by a colleaque is using a second $projection. I think this is more efficient than $map, because it does not go over each element of the array. The trick is to repeat the projections from the first $projection.

db.meta.aggregate([
  { $match: { test: "OK" }},
  { $lookup: { from: "merge", localField: "Exp", foreignField: "Exp", as: 
     "kin" }},
  { $project:
    { "Exp": true, "test": true, "date": true,
      kin : { $filter: { input: "$kin", as: "kin", cond: { $eq: [ 
      "$$kin.M2", 
      "val" ]}}
     }
   }
  },
  { $project: { 
    "Exp": true, "test": true, "date": true,
    "kin.M1": true, "kin.M2": true, "kin.T": true }
   }
])


来源:https://stackoverflow.com/questions/45326664/how-to-project-specific-fields-in-array-on-filtered-lookup

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!