使用聚合框架来比较数组元素重叠

时间:2017-07-25 20:45:26

标签: mongodb aggregation-framework

我的馆藏文件结构如下:

{
   carrier: "abc",
   flightNumber: 123,
   dates: [
      ISODate("2015-01-01T00:00:00Z"),
      ISODate("2015-01-02T00:00:00Z"),
      ISODate("2015-01-03T00:00:00Z")
    ]
}

我想搜索一下这个集合,看看是否有任何同一carrierflightNumber的文档在dates数组中都有日期。例如:

{
   carrier: "abc",
   flightNumber: 123,
   dates: [
      ISODate("2015-01-01T00:00:00Z"),
      ISODate("2015-01-02T00:00:00Z"),
      ISODate("2015-01-03T00:00:00Z")
    ]
},
{
   carrier: "abc",
   flightNumber: 123,
   dates: [
      ISODate("2015-01-03T00:00:00Z"),
      ISODate("2015-01-04T00:00:00Z"),
      ISODate("2015-01-05T00:00:00Z")
    ]
}

如果上述记录出现在集合中,我希望将它们归还,因为它们都有carrierabcflightNumber123并且他们也有ISODate("2015-01-03T00:00:00Z")数组中的日期dates。如果第二份文件中没有该日期,则不应返回。

通常我会通过分组和计算来完成此操作:

db.flights.aggregate([
  { 
    $group: { 
       _id: { carrier: "$carrier", flightNumber: "$flightNumber" }, 
       uniqueIds: { $addToSet: "$_id" },
       count: { $sum: 1 } 
    }
  }, 
  { 
    $match: { 
       count: { $gt: 1 } 
    }
  }
])

但我不确定如何修改它以查找数组重叠。有谁能建议如何实现这个目标?

1 个答案:

答案 0 :(得分:2)

$unwind数组如果要查看内容为"分组"在他们内:

db.flights.aggregate([
  { "$unwind": "$dates" },
  { "$group": {
    "_id": { "carrier": "$carrier", "flightnumber": "$flightnumber", "date": "$dates" },
     "count": { "$sum": 1 },
     "_ids": { "$addToSet": "$_id" }
  }},
  { "$match": { "count": { "$gt": 1 } } },
  { "$unwind": "$_ids" },
  { "$group": { "_id": "$_ids" } }
])

事实上,这确实告诉了哪些文件"重叠"居住,因为"相同的日期"以及您关注的其他相同的分组键值有一个" count"发生的次数超过一次。表明重叠。

$match之后的任何事情都只是为了#34;演示"因为如果您只想查看重叠,则无法为多个重叠报告相同的_id值。事实上,如果你想一起看到它们,最好留下"分组集"单独

现在,如果检索实际文档对您来说很重要,可以添加$lookup

db.flights.aggregate([
  { "$unwind": "$dates" },
  { "$group": {
    "_id": { "carrier": "$carrier", "flightnumber": "$flightnumber", "date": "$dates" },
     "count": { "$sum": 1 },
     "_ids": { "$addToSet": "$_id" }
  }},
  { "$match": { "count": { "$gt": 1 } } },
  { "$unwind": "$_ids" },
  { "$group": { "_id": "$_ids" } },
  }},
  { "$lookup": {
    "from": "flights",
    "localField": "_id",
    "foreignField": "_id",
    "as": "_ids"
  }},
  { "$unwind": "$_ids" },
  { "$replaceRoot": {
    "newRoot": "$_ids"
  }}
])

甚至可以使用$replaceRoot$project来返回整个文档。或者,如果不是大小问题,您甚至可以$addToSet$$ROOT完成。

但总体观点在前三个流程阶段中有所涉及,或者大多数只是在第一个"中。如果您想使用数组"跨文档",那么主要操作符仍然是$unwind

或者更多"报告"喜欢格式:

db.flights.aggregate([
  { "$addFields": { "copy": "$$ROOT" } }, 
  { "$unwind": "$dates" },
  { "$group": {
    "_id": {
      "carrier": "$carrier",
      "flightNumber": "$flightNumber",
      "dates": "$dates" 
    },
    "count": { "$sum": 1 },
    "_docs": { "$addToSet": "$copy" }  
  }},
  { "$match": { "count": { "$gt": 1 } } },
  { "$group": {
    "_id": {
      "carrier": "$_id.carrier",
      "flightNumber": "$_id.flightNumber",
    },
    "overlaps": {
      "$push": {
        "date": "$_id.dates",
        "_docs": "$_docs"  
      }  
    }  
  }}
])

会报告每个组中的重叠日期,并告诉您哪些文档包含重叠:

{
    "_id" : {
        "carrier" : "abc",
        "flightNumber" : 123.0
    },
    "overlaps" : [ 
        {
            "date" : ISODate("2015-01-03T00:00:00.000Z"),
            "_docs" : [ 
                {
                    "_id" : ObjectId("5977f9187dcd6a5f6a9b4b97"),
                    "carrier" : "abc",
                    "flightNumber" : 123.0,
                    "dates" : [ 
                        ISODate("2015-01-03T00:00:00.000Z"), 
                        ISODate("2015-01-04T00:00:00.000Z"), 
                        ISODate("2015-01-05T00:00:00.000Z")
                    ]
                }, 
                {
                    "_id" : ObjectId("5977f9187dcd6a5f6a9b4b96"),
                    "carrier" : "abc",
                    "flightNumber" : 123.0,
                    "dates" : [ 
                        ISODate("2015-01-01T00:00:00.000Z"), 
                        ISODate("2015-01-02T00:00:00.000Z"), 
                        ISODate("2015-01-03T00:00:00.000Z")
                    ]
                }
            ]
        }
    ]
}