Mongo db没有查询,因为有两个来自同一集合的文档子集

时间:2019-12-30 05:43:32

标签: mongodb mongodb-query aggregation-framework

我是mongodb的新手。假设以下内容。一个集合x,y和z中有3种文档。

docs = [{
  "item_id": 1
  "type": "x"
},
{
  "item_id": 2
  "type": "x"
},{
  "item_id": 3
  "type": "y",
  "relavent_item_ids": [1, 2]
},
{
  "item_id": 3
  "type": "y",
  "relavent_item_ids": [1, 2, 3]
},{
  "item_id": 4
  "type": "z",     
}]

我想得到以下内容。

  • 忽略类型为z的文档
  • 获取x类型的item_id不在relavent_item_ids类型的y文档中的所有文档。
  • 结果应包含item_id字段。

我尝试做match $in,但这返回了我所有的记录,我无法弄清楚如何处理y类型的文档子集。

3 个答案:

答案 0 :(得分:2)

您可以使用以下查询

const item_ids = (await db.collection.find({ "type": "y" })).map(({ relavent_item_ids }) => relavent_item_ids)

const result = db.collection.find({
  "item_id": { "$exists": true },
  "type": { "$ne": "z", "$eq": "x" },
  "relavent_item_ids": { "$nin": item_ids }
})

console.log({ result })

忽略类型为z的文件->使用不等于查询运算符的$ne来过滤z类型。

获取所有类型为item_id不在类型y的relavent_item_ids中的类型为x的所有文档->使用$expr来匹配相同的文档字段。

结果应具有item_id字段->使用$exists查询运算符。

答案 1 :(得分:1)

解决方案:

db.test.aggregate( [
  { 
      $facet: {
          firstQuery: [
              { 
                  $match: { type: { $eq: "x", $ne: "z" } } 
              },
              { 
                  $project: { 
                      item_id : 1, _id: 0 
                  } 
              }
          ],
          secondQuery: [
              { 
                  $match: { type: "y" } 
              },
              { 
                  $group: { 
                      _id: null, 
                      relavent: { $push: "$relavent_item_ids" } 
                  } 
              },
              { 
                  $project: { 
                      relavent: { 
                          $reduce: { 
                               input: "$relavent",
                               initialValue:  [ ],
                               in: { $setUnion: [ "$$value", "$$this" ] }
                          }
                      }
                  }
              }
          ]
      } 
  },
  { 
      $addFields: { secondQuery: { $arrayElemAt: [ "$secondQuery", 0 ] } }
  },
  { 
      $project: { 
          result: {
              $filter: { 
                   input: "$firstQuery" ,
                   as: "e",
                   cond: { $not: [ { $in: [ "$$e.item_id", "$secondQuery.relavent" ] } ] }
              }
          }
      } 
  },
] )

使用问题帖中的输入文档,然后再将以下文档添加到集合中

{
  "item_id": 11,
  "type": "x",     
}

:仅此文档的item_id(值11)将显示在输出中。

聚合使用$facet通过一次传递进行两个单独的查询。第一个查询将所有“ x”类型(并忽略“ z”类型)作为数组。第二个查询获取具有唯一值的relavent_item_ids数组(来自“ y”类型的文档)。最后的$project阶段使用以下条件过滤第一个查询结果数组:

  

获取所有不在type_id中的x类型的文档   y个文档的relavent_item_ids

答案 2 :(得分:0)

我不确定这是否是一种优雅的解决方案。

db.getCollection('test').aggregate([
    {   
        "$unwind": {
            "path": "$relavent_item_ids",
            "preserveNullAndEmptyArrays": true
        } 
    },
    {   
        "$group": {
            "_id":null, 
            "relavent_item_ids": {"$addToSet":"$relavent_item_ids"},
            "other_ids": {
                "$addToSet":{
                    "$cond":[
                        {"$eq":["$type", "x"]},
                        "$item_id",
                        null
                    ]
                }
            }
        } 
    },
    {
        "$project":{ 
                "includeIds": {"$setDifference":["$other_ids", "$relavent_item_ids"]}
        }
    },
    {
        "$unwind": "$includeIds"
    },
    {
        "$match": {"includeIds":{"$ne":null}}
    },
    {
        "$lookup":{
                "from": "test",
                "let": { "includeIds": "$includeIds"},
                "pipeline": [
                      { "$match":
                         { "$expr":
                            { "$and":
                               [
                                 { "$eq": [ "$item_id",  "$$includeIds" ] },
                                 { "$eq": [ "$type", "x" ] }
                               ]
                            }
                         }
                      }
                ],
                "as": "result"
        }
    },
    {
        "$unwind": "$result"
    },
])