在聚合投影MongoDB中将多个子文档合并为一个数组

时间:2016-09-18 05:01:57

标签: mongodb aggregation-framework

我在这里要做的是查询并合并多个子文档作为数组。我认为聚合框架是可行的方法,但我似乎无法完全正确。

以下是我的名为访客的集合:

{
  "_id": ObjectId("57dc5c2b7463d336ec3fff8c"),
  "username": "Bob",
  "fullname": "Bobby",
  "activities": 
      { "visits" : [
         {
            "_id": ObjectId("57dc674e4208b12fd4a52a3d"),
            "date": ISODate("2016-09-18T08:00:00.000Z"),
            "location" : "Jakarta",
         },
         {
            "_id": ObjectId("57dd3795c13c5e2b7484ea4b"),
            "date": ISODate("2016-09-17T08:00:00.000Z"),
            "location" : "Denpasar",
         }
       ],
        "purchases" : [
        {
          "_id": ObjectId("57dc4769c0f09317282b3f92"),
          "date": ISODate("2016-09-17T07:30:00.000Z"),
          "product_name" : "Shirt",
          "price": 125000
        },{
          "_id": ObjectId("57dbfdc6be9dcf1e7c4a1751"),
          "date": ISODate("2016-09-18T08:30:00.000Z"),
          "product_name" : "Shoes",
          "price": 150000
        }
      ]},
  }
}

这是文档 我想要并尝试使用聚合来实现这一点,条件是我可以按日期sort使用{ {1}}和limit

skip

我一直试图通过这种聚合

来做到这一点
{
  "_id": ObjectId("57dc5c2b7463d336ec3fff8c"),
  "activities": [
     {
       "activity_type": "purchases",
       "_id": ObjectId("57dbfdc6be9dcf1e7c4a1751"),
        "date": ISODate("2016-09-18T08:30:00.000Z"),
       "product_name" : "Shoes",
       "price": 150000
     },{
       "activity_type": "visits",
       "visits_id": ObjectId("57dc674e4208b12fd4a52a3d"),
       "date": ISODate("2016-09-18T08:00:00.000Z"),
       "location" : "Jakarta",
     },{
       "activity_type": "visits",
       "visits_id": ObjectId("57dd3795c13c5e2b7484ea4b"),
       "date": ISODate("2016-09-17T08:00:00.000Z"),
       "location" : "Denpasar",
     },{
       "activity_type": "purchases",
       "date": ISODate("2016-09-17T07:30:00.000Z"),
       "product_name" : "Shirt",
       "price": 125000
     }]
}

但是我没有得到我想要的东西,而是根据他们的类型对它们进行分组,我甚至无法使用db.visitors.aggregate([{ $match: { _id: ObjectId("57dc5c2b7463d336ec3fff8c") } }, { $group: { _id: "$_id", visits: { "$addToSet": "$activities.visits" }, purchases: { "$addToSet": "$activities.purchases" } } }]) skip(似乎只是跳过并限制访问者)数)。

limit

我已尝试使用{ "_id": ObjectId("57dc5c2b7463d336ec3fff8c"), "visits": [ [ { "_id": ObjectId("57dc674e4208b12fd4a52a3d"), "date": ISODate("2016-09-18T08:00:00.000Z"), "location" : "Jakarta", }, { "_id": ObjectId("57dd3795c13c5e2b7484ea4b"), "date": ISODate("2016-09-17T08:00:00.000Z"), "location" : "Denpasar", } ] ], "news": [ [ { "_id": ObjectId("57dc4769c0f09317282b3f92"), "date": ISODate("2016-09-17T07:30:00.000Z"), "product_name" : "Shirt", "price": 125000 },{ "_id": ObjectId("57dbfdc6be9dcf1e7c4a1751"), "date": ISODate("2016-09-18T08:30:00.000Z"), "product_name" : "Shoes", "price": 150000 } ] ] }

进行另一次投影
unwind

但它似乎在每个索引合并不同类型的文档

db.visitors.aggregate([
   { $match: { _id: ObjectId("57dc5c2b7463d336ec3fff8c") } },
   { $unwind: '$activities.visits' },
   { $unwind: '$activities.purchases' },
   { $project: {
        _id: 0,
        //visits
        "visits_id": "$activities.visits._id",
        "visits_date": "$activities.visits.date",
        "visits_location" : "$activities.visits.location"
        //purchases
        "purchases_id": "$activities.purchases._id",
        "purchases_date": "$activities.purchases.date",
        "purchases_product_name": "$activities.purchases.product_name",
        "purchases_price": "$activities.purchases.price",
     }
   }
])
  .skip(0)
  .limit(2)

是否可以使用我当前的文档执行此操作?或者我应该更改我的文档结构?

更新已解决

我最后在数组的每个子文档中添加{ "_id": ObjectId("57dc5c2b7463d336ec3fff8c"), "activities": [ { "purchases_id": ObjectId("57dbfdc6be9dcf1e7c4a1751"), "purchases_date": ISODate("2016-09-18T08:30:00.000Z"), "purchases_product_name" : "Shoes", "purchases_price": 150000 "visits_id": ObjectId("57dc674e4208b12fd4a52a3d"), "visits_date": ISODate("2016-09-18T08:00:00.000Z"), "visits_location" : "Jakarta", },{ "purchases_id": ObjectId("57dc4769c0f09317282b3f92"), "purchases_date": ISODate("2016-09-17T07:30:00.000Z"), "purchases_product_name" : "Shirt", "purchases_price": 125000 "visits_id": ObjectId("57dd3795c13c5e2b7484ea4b"), "visits_date": ISODate("2016-09-17T08:00:00.000Z"), "visits_location" : "Denpasar", }] } ,并使用activity_type解决此问题,将多个数组合并为数组,并使用$setUnion限制并跳过数组。我不知道如何,但似乎$slice已经自动对其进行排序

$setUnion

1 个答案:

答案 0 :(得分:1)

如果您为每个数组元素添加activity_type字段,则$setUnion一个aggregation阶段将很容易使用{<1}}:

db.visitors.aggregate([
    {
        $project: {
            activities: {
                $setUnion: ['$activities.visits', '$activities.purchases']
            }
        }
    }
])

在这种情况下,您将把数组“连接”在一起,形成一个带有类型的活动数组。

对于问题的其他部分,sortskiplimit是聚合阶段,可以处理每个文档而不是子文档,因此您需要{ {1}}您需要的文档,$match之前的结果$unwind数组,然后您就可以使用activitiessortskip运算符。