使用$ lookup在相关属性上聚合组

时间:2017-05-31 17:34:36

标签: mongodb mongoose mongodb-query aggregation-framework

我正在尝试执行Mongodb聚合,这将允许我对位于查找对象中的项进行分组,并根据位于子文档中的项进行计数,例如:

客户:

- customer: {type: Schema.Types.ObjectId, ref: 'Customer'},
- [OrderLineItem]

订单:

- name: {type: Schema.Types.String, minlength: 1, maxlength: 35, required: true},
- manufacturer: {type: Schema.Types.String, minlength: 5, maxlength: 35, required: true}, 
- cost: {type: Schema.Types.Number, required: true},
- current_inventory: {type: Schema.Types.Number, required: true}

产品:

- product: {type: Schema.Types.ObjectId, ref: 'Product'},
- quantity: {type: Schema.Types.Number, required: true},
- total_cost: {type: Schema.Types.Number, required: true}

的OrderLineItem:

this.order.aggregate([
{$lookup: {from: "Customer", localField: "customer", foreignField: "_id", as: "customerObj"}},
{$group : {_id : {zipCode: "$customerObj.zipcode", products: "not sure what goes here...."}, total: { $count: "not sure what goes here..." }}}
])

获取通过邮政编码购买的产品总数:

@using (Html.BeginForm("CallAllocationSubmit", "Home", FormMethod.Post, new { id = "FrmCallAllocationSubmit", ReturnUrl = ViewBag.ReturnUrl }))
{
    <table>
        <tr>
            <td>
                &nbsp;&nbsp;&nbsp;&nbsp;<button class="btn btn-success btn-icon " type="submit" style="width:100px;" name="Allocate" >Allocate</button>
            </td>
            <td>
                &nbsp;&nbsp;&nbsp;&nbsp;<button class="btn btn-primary  btn-icon " type="submit" style="width:100px;" name="Defer" >Defer </button>
            </td>
        </tr>
    </table>
}

对于上面的示例,我想获得一份按邮政编码购买的产品总数的报告。当我试图构建我的聚合时,我错过了它应该如何为这个场景编码。

1 个答案:

答案 0 :(得分:1)

$lookup运算符将返回&#34;数组&#34;在所有情况下匹配项目。由于客户的关系为1:1,因此您可以通过$arrayElemAt轻松解决该元素。

您的问题没有为包含OrderLineItem数组的架构属性命名,因此我假设"order_lines"位于下方,更改为您实际使用的内容。

Order.aggregate([
  // lookup the customer detail
  { "$lookup": {
    "from": "customers",
    "localField": "customer",
    "foreignField": "_id",
    "as": "customer"
  }},
  // Group by zip
  { "$group": {
    "_id": { "$arrayElemAt": [ "$customer.zipcode", 0 ] },
    "total_qty": { "$sum": "$order_lines.quantity" }
  }}
])

如果您需要"Product"详细信息本身的内容,例如"manufacturer",并且需要将其用作$group的一部分,那么就有必要再做$lookup $unwind再次,细节位于具有相同1:1关系的数组中。但您首先需要$sum "order_lines"

Order.aggregate([
  // lookup the customer detail
  { "$lookup": {
    "from": "customers",
    "localField": "customer",
    "foreignField": "_id",
    "as": "customer"
  }},

  // $unwind the order lines
  { "$unwind": "$order_lines" },

  // lookup the product detail
  { "$lookup": {
    "from": "products",
    "localField": "order_lines.product",
    "foreignField": "_id",
    "as": "order_lines.product"
  }},

  // Group by zip by manufacturer
  { "$group": {
    "_id": {
      "zip": { "$arrayElemAt": [ "$customer.zipcode", 0 ] },
      "manufacturer": { "$arrayElemAt": [ "$order_lines.product.manufactuer", 0 ] }
    },
    "total_qty": { "$sum": "$order_lines.quantity" }
  }}
])

您可能会注意到$unwind的使用情况不会发生变化,尽管仅在一种情况下"order_lines"数组上使用了$sum操作。这是因为从MongoDB 3.2开始,您可以直接表示包含内部属性值的数组,因此:

"items": [{ "a": 1, "b": 2 }, { "a": 2, "b": 3 }]

当引用为"$items.a"时:

[1,2]

此外,$group运算符已更改,因此它也会接受&#34;数组&#34;项目以及在Setting replyTo field in email

内累积

注意集合上使用的命名约定。默认情况下,Mongoose使用给定单一模型名称的约定,即"Customer"将是&#34;复数&#34;降为"products"作为实际的集合名称。 $lookup运算符在服务器上运行,而不是在了解模型的客户端代码中运行,因此服务器唯一知道的是集合。

您可能故意将您的集合命名为在猫鼬模型的声明中使用,如:

 mongoose.model("Customer", customerSchema, "customer")

这意味着该集合由其正确的名称引用。但通常大多数人都不这样做,因此区分这一点非常重要。