MongoDB文档嵌套文档过滤

时间:2017-02-08 10:30:27

标签: mongodb mongodb-query aggregation-framework

我和MongoDb打架

我有一组文档,其单个文档的结构如下

{
    "_id": { "$oid": "588a931d5c98fe0f3f84d93f" },
    "name": "Michele",
    "type": "F",
    "category": "G",
    "meta":{
        "code": "113835667",
        "updated": {"$date": "2017-02-07T00:00:00.000Z"},
        "since": {"$date": "2013-11-07T23:00:00.000Z"}
    },
    "data": [
        {
          "date": {"$date": "2013-11-07T23:00:00.000Z"},
          "close": 12.23
        }
        ... // removed 
        {
          "date": {"$date": "2017-02-07T00:00:00.000Z"},
          "close": 15.22
        }
    ]
}

我需要实现的是返回带有匹配_id的文档,但是从数据数组中过滤出date属性不在指定时间范围内的文档。

这是我从现在开始尝试的事情

 let id;    //[DocumentId]
 let from;  //[Date]
 let to;    //[Date]
 collection.aggregate([
            { $match: { _id: { $eq: id } } },
            { $unwind: '$data' },
            { $match: { 'data.date': { $gte: from, $lte: to } } },
            { $group: { _id: '$_id', 
                        data: { $push: { date:'$data.date', close: '$data.close' } } } }
        ], ...);

这种方法的问题是我返回的文档只包含_id和数据属性[数据过滤结果正常],而我需要返回完整的可用属性集。

建议非常感谢!

1 个答案:

答案 0 :(得分:1)

如果你可以升级到Mongo 3.4(最新的稳定版本),它可以很好地完成:

db.collection.aggregate([
        //Pre-filter to have data arrays with at least one matching date
	{$match: {_id: id, 'data.date': {$gte: from, $lte: to}}},
        //Filter the items array
	{
		$addFields: {
			'items': {
				$filter: {
					input: '$data', as: 'item', cond: {
						$and: [
							{$gte: ["$$item.date", from]},
							{$lte: ["$$item.date", to]}
						]
					}
				}
			}
		}
	}
]);

如果你必须保留mongo 3.2,我唯一能想到的就是使用这样的$ ROOT:

db.collection.aggregate([
	{$match: {_id: id, 'data.date': {$gte: from, $lte: to}}},
	{
		$project: {
			'filteredData': {
				$filter: {
					input: '$data', as: 'item', cond: {
						$and: [
							{$gte: ["$$item.date", from]},
							{$lte: ["$$item.date", to]}
						]
					}
				}
			},
			'originalDocument': '$$ROOT'
		}
	}
]);

生成的对象将具有originalDocument和filteredData作为属性,您需要在服务器代码中处理它们(实际上是一个循环)。