Mongodb $lookup on ObjectId';在一个数组中

Mongodb $lookup on ObjectId';在一个数组中,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,对一个ObjectId数组而不仅仅是单个ObjectId的字段执行$lookup的语法是什么 订单文件示例: { _id: ObjectId("..."), products: [ ObjectId("..<Car ObjectId>.."), ObjectId("..<Bike ObjectId>..") ] } 期望结果 { _id: ObjectId("..."), products: [ ObjectId("..<

对一个ObjectId数组而不仅仅是单个ObjectId的字段执行$lookup的语法是什么

订单文件示例:

{
  _id: ObjectId("..."),
  products: [
    ObjectId("..<Car ObjectId>.."),
    ObjectId("..<Bike ObjectId>..")
  ]
}
期望结果

{
  _id: ObjectId("..."),
  products: [
    ObjectId("..<Car ObjectId>.."),
    ObjectId("..<Bike ObjectId>..")
  ],
  productObjects: [
    {<Car Object>},
    {<Bike Object>}
  ],
}
{
_id:ObjectId(“…”),
产品:[
ObjectId(“…”),
ObjectId(“…”)
],
productObjects:[
{},
{}
],
}
2017年更新 <代码>不再需要$unwind

旧答案 聚合管道阶段不会直接与阵列一起工作。设计的主要目的是对可能的相关数据进行“左连接”,作为“一对多”类型的连接(或者实际上是“查找”)。但该值是单数的,而不是数组

因此,在执行
$lookup
操作之前,必须先“解除”内容的正常化,才能使其正常工作。这意味着使用:

db.orders.aggregate([
//展开源文件
{“$unwind”:“$products”},
//进行查找匹配
{“$lookup”:{
“来自”:“产品”,
“localField”:“产品”,
“外域”:“\u id”,
“作为”:“productObjects”
}},
//展开结果数组(可能是一个或无)
{“$unwind”:“$productObjects”},
//分组回阵列
{“$组”:{
“\u id”:“$\u id”,
“产品”:{“$push”:“$products”},
“productObjects”:{“$push”:“$productObjects”}
}}
])
$lookup
匹配每个数组成员后,结果就是一个数组本身,因此您可以再次
$unwind
并使用新数组获得最终结果

请注意,任何未找到的“左连接”匹配项都将为给定产品上的“productObjects”创建一个空数组,从而在调用第二个
$unwind
时否定“product”元素的文档

虽然直接应用到数组会很好,但它只是通过将单个值与可能的多个值进行匹配来实现的

由于
$lookup
基本上是非常新的,因此它目前的工作方式与那些熟悉那里提供的
.populate()
方法的“穷人版”的人所熟悉的一样。不同之处在于,
$lookup
提供了“连接”的“服务器端”处理,而不是在客户机上,
$lookup
中的一些“成熟度”目前缺乏
.populate()
提供的功能(例如直接在数组上插入查找)

这实际上是一个需要改进的问题,所以如果运气好的话,它将在下一个版本或之后不久发布

作为一个设计原则,您当前的结构既不好也不坏,只是在创建任何“连接”时会受到开销的影响。因此,MongoDB在inception中的基本原则是适用的,如果您“可以”在一个集合中“预先加入”数据,那么最好这样做

作为一般原则,
$lookup
可以说的另一件事是,此处“连接”的目的是以与此处所示不同的方式工作。因此,与其将其他文档的“相关ID”保存在“父级”文档中,最好的一般原则是“相关文档”包含对“父级”的引用


因此,
$lookup
可以说是“最有效”的“关系设计”,与mongoose
.populate()
执行客户端连接的方式相反。通过在每个“多”中标识“一”,您只需输入相关项,而无需先
$unwind
阵列。

从MongoDB v3.4(2016年发布)开始,该。不再需要
$unwind


这是在中跟踪的。

使用
$lookup
和后续的
$group
进行聚合非常麻烦,因此,如果(如果这是一种媒介)您正在使用node&Mongoose或支持库并在架构中提供一些提示,则可以使用来获取这些文档:

var mongoose=require(“mongoose”),
Schema=mongoose.Schema;
var productSchema=Schema({…});
var orderSchema=Schema({
_身份证号码:,
产品:[{type:Schema.Types.ObjectId,ref:“产品”}]
});
var Product=mongoose.model(“产品”,productSchema);
var Order=mongoose.model(“Order”,orderSchema);
...
命令
.查找(…)
.填充(“产品”)
...

使用$unwind您将获得第一个对象,而不是对象数组

查询:

db.getCollection('vehicles').aggregate([
  {
    $match: {
      status: "AVAILABLE",
      vehicleTypeId: {
        $in: Array.from(newSet(d.vehicleTypeIds))
      }
    }
  },
  {
    $lookup: {
      from: "servicelocations",
      localField: "locationId",
      foreignField: "serviceLocationId",
      as: "locations"
    }
  },
  {
    $unwind: "$locations"
  }
]);
结果:

{
    "_id" : ObjectId("59c3983a647101ec58ddcf90"),
    "vehicleId" : "45680",
    "regionId" : 1.0,
    "vehicleTypeId" : "10TONBOX",
    "locationId" : "100",
    "description" : "Isuzu/2003-10 Ton/Box",
    "deviceId" : "",
    "earliestStart" : 36000.0,
    "latestArrival" : 54000.0,
    "status" : "AVAILABLE",
    "accountId" : 1.0,
    "locations" : {
        "_id" : ObjectId("59c3afeab7799c90ebb3291f"),
        "serviceLocationId" : "100",
        "regionId" : 1.0,
        "zoneId" : "DXBZONE1",
        "description" : "Masafi Park Al Quoz",
        "locationPriority" : 1.0,
        "accountTypeId" : 0.0,
        "locationType" : "DEPOT",
        "location" : {
            "makani" : "",
            "lat" : 25.123091,
            "lng" : 55.21082
        },
        "deliveryDays" : "MTWRFSU",
        "timeWindow" : {
            "timeWindowTypeId" : "1"
        },
        "address1" : "",
        "address2" : "",
        "phone" : "",
        "city" : "",
        "county" : "",
        "state" : "",
        "country" : "",
        "zipcode" : "",
        "imageUrl" : "",
        "contact" : {
            "name" : "",
            "email" : ""
        },
        "status" : "",
        "createdBy" : "",
        "updatedBy" : "",
        "updateDate" : "",
        "accountId" : 1.0,
        "serviceTimeTypeId" : "1"
    }
}


{
    "_id" : ObjectId("59c3983a647101ec58ddcf91"),
    "vehicleId" : "81765",
    "regionId" : 1.0,
    "vehicleTypeId" : "10TONBOX",
    "locationId" : "100",
    "description" : "Hino/2004-10 Ton/Box",
    "deviceId" : "",
    "earliestStart" : 36000.0,
    "latestArrival" : 54000.0,
    "status" : "AVAILABLE",
    "accountId" : 1.0,
    "locations" : {
        "_id" : ObjectId("59c3afeab7799c90ebb3291f"),
        "serviceLocationId" : "100",
        "regionId" : 1.0,
        "zoneId" : "DXBZONE1",
        "description" : "Masafi Park Al Quoz",
        "locationPriority" : 1.0,
        "accountTypeId" : 0.0,
        "locationType" : "DEPOT",
        "location" : {
            "makani" : "",
            "lat" : 25.123091,
            "lng" : 55.21082
        },
        "deliveryDays" : "MTWRFSU",
        "timeWindow" : {
            "timeWindowTypeId" : "1"
        },
        "address1" : "",
        "address2" : "",
        "phone" : "",
        "city" : "",
        "county" : "",
        "state" : "",
        "country" : "",
        "zipcode" : "",
        "imageUrl" : "",
        "contact" : {
            "name" : "",
            "email" : ""
        },
        "status" : "",
        "createdBy" : "",
        "updatedBy" : "",
        "updateDate" : "",
        "accountId" : 1.0,
        "serviceTimeTypeId" : "1"
    }
}

您还可以使用
管道
阶段对子文档数组执行检查

下面是使用
python
的示例(对不起,我是蛇人)

这里的问题是匹配
ObjectId
数组中的所有对象(位于
本地
字段/prop
产品中的外来
\u id


正如上面的评论所示,您还可以使用附加的
阶段
清理或投影外来记录。

我不同意,如果我们在$match阶段之前使用$lookup,我们可以使用IDs数组进行$lookup

//用查找结果替换IDs数组
db.products.aggregate([
{$match:{products:{$exists:true}}},
{
$lookup:{
来自:“产品”,
localField:“产品”,
foreignField:“\u id”,
as:“产品对象”
}
}

])
我的订单文档示例是否不够清晰?您需要产品的示例文档吗?SERVER-22881将跟踪使数组按预期工作(而不是按文字值)。谢谢,它可以工作!这是否表明我的数据结构/规范化不正确?@JasonLin没有“好/坏”那么严格,因此答案中添加了更多的解释。这取决于什么适合您。当前的实现有些无意。在本地数组中查找所有值是有意义的
{
    "_id" : ObjectId("59c3983a647101ec58ddcf90"),
    "vehicleId" : "45680",
    "regionId" : 1.0,
    "vehicleTypeId" : "10TONBOX",
    "locationId" : "100",
    "description" : "Isuzu/2003-10 Ton/Box",
    "deviceId" : "",
    "earliestStart" : 36000.0,
    "latestArrival" : 54000.0,
    "status" : "AVAILABLE",
    "accountId" : 1.0,
    "locations" : {
        "_id" : ObjectId("59c3afeab7799c90ebb3291f"),
        "serviceLocationId" : "100",
        "regionId" : 1.0,
        "zoneId" : "DXBZONE1",
        "description" : "Masafi Park Al Quoz",
        "locationPriority" : 1.0,
        "accountTypeId" : 0.0,
        "locationType" : "DEPOT",
        "location" : {
            "makani" : "",
            "lat" : 25.123091,
            "lng" : 55.21082
        },
        "deliveryDays" : "MTWRFSU",
        "timeWindow" : {
            "timeWindowTypeId" : "1"
        },
        "address1" : "",
        "address2" : "",
        "phone" : "",
        "city" : "",
        "county" : "",
        "state" : "",
        "country" : "",
        "zipcode" : "",
        "imageUrl" : "",
        "contact" : {
            "name" : "",
            "email" : ""
        },
        "status" : "",
        "createdBy" : "",
        "updatedBy" : "",
        "updateDate" : "",
        "accountId" : 1.0,
        "serviceTimeTypeId" : "1"
    }
}


{
    "_id" : ObjectId("59c3983a647101ec58ddcf91"),
    "vehicleId" : "81765",
    "regionId" : 1.0,
    "vehicleTypeId" : "10TONBOX",
    "locationId" : "100",
    "description" : "Hino/2004-10 Ton/Box",
    "deviceId" : "",
    "earliestStart" : 36000.0,
    "latestArrival" : 54000.0,
    "status" : "AVAILABLE",
    "accountId" : 1.0,
    "locations" : {
        "_id" : ObjectId("59c3afeab7799c90ebb3291f"),
        "serviceLocationId" : "100",
        "regionId" : 1.0,
        "zoneId" : "DXBZONE1",
        "description" : "Masafi Park Al Quoz",
        "locationPriority" : 1.0,
        "accountTypeId" : 0.0,
        "locationType" : "DEPOT",
        "location" : {
            "makani" : "",
            "lat" : 25.123091,
            "lng" : 55.21082
        },
        "deliveryDays" : "MTWRFSU",
        "timeWindow" : {
            "timeWindowTypeId" : "1"
        },
        "address1" : "",
        "address2" : "",
        "phone" : "",
        "city" : "",
        "county" : "",
        "state" : "",
        "country" : "",
        "zipcode" : "",
        "imageUrl" : "",
        "contact" : {
            "name" : "",
            "email" : ""
        },
        "status" : "",
        "createdBy" : "",
        "updatedBy" : "",
        "updateDate" : "",
        "accountId" : 1.0,
        "serviceTimeTypeId" : "1"
    }
}
db.products.aggregate([
  { '$lookup': {
      'from': 'products',
      'let': { 'pid': '$products' },
      'pipeline': [
        { '$match': { '$expr': { '$in': ['$_id', '$$pid'] } } }
        // Add additional stages here 
      ],
      'as':'productObjects'
  }
])