【问题标题】:Merge multiple subdocuments as an array in aggregation projection MongoDB [duplicate]在聚合投影MongoDB中将多个子文档合并为一个数组[重复]
【发布时间】:2017-06-25 21:34:40
【问题描述】:

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

这是我的集合,名为visitors

{
  "_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他们,使用limitskip

{
  "_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"
          }
        }
     }])

但我并没有得到我想要的,而是只是按他们的类型对它们进行分组,我什至不能使用skiplimit(似乎只是跳过和限制访问者数量)。

{
   "_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 已经自动排序了

db.visitors.aggregate([
{
    $project: {
        activities: {
            $setUnion: ['$activities.visits', '$activities.purchases'], 
        }
    }
}, 
{
    $project:{
        activites: {
            $slice: ["$activities", 0, 2]
        }
    }
}
])

【问题讨论】:

  • $slice 可以作为我想要的跳过和限制,但它不能解决我的问题,而 $setUnion 正是我想要的

标签: mongodb aggregation-framework


【解决方案1】:

如果将activity_type 字段添加到每个数组元素,那么$setUnion 将很容易使用一个aggregation 阶段:

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

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

对于您问题的另一部分,sortskiplimit 是适用于每个文档而非子文档的聚合阶段,因此您需要 $match 您需要的文档, $unwind 是之前的 activities 数组的结果,然后您可以使用所有 sortskiplimit 运算符。

【讨论】:

  • 请注意,这将过滤掉结果中的重复项。
  • @Styvane visitspurchases 都有一个 _id 字段,不会有重复。我还要求他为他们俩添加activity_type字段
  • 你是对的。我没注意到。
  • @Tom 谢谢,我在每个数组中都添加了 activity_type,它就像一个魅力。 $setunion 正是我一直在寻找的。​​span>
猜你喜欢
  • 1970-01-01
  • 2015-04-03
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2018-05-06
  • 2021-12-18
相关资源
最近更新 更多