Jas*_*Lin 86 mongodb mongodb-query aggregation-framework
在一个ObjectIds数组而不仅仅是一个ObjectId的字段上执行$ lookup的语法是什么?
示例订单文档:
{
_id: ObjectId("..."),
products: [
ObjectId("..<Car ObjectId>.."),
ObjectId("..<Bike ObjectId>..")
]
}
Run Code Online (Sandbox Code Playgroud)
不工作查询:
db.orders.aggregate([
{
$lookup:
{
from: "products",
localField: "products",
foreignField: "_id",
as: "productObjects"
}
}
])
Run Code Online (Sandbox Code Playgroud)
期望的结果
{
_id: ObjectId("..."),
products: [
ObjectId("..<Car ObjectId>.."),
ObjectId("..<Bike ObjectId>..")
],
productObjects: [
{<Car Object>},
{<Bike Object>}
],
}
Run Code Online (Sandbox Code Playgroud)
Bla*_*ven 128
该$lookup聚集流水线阶段不会与数组直接工作.该设计的主要目的是将"左连接"作为可能相关数据的"一对多"类型的连接(或实际上是"查找").但该值旨在是单数而不是数组.
因此,在执行$lookup操作之前,必须先对内容进行"反规范化" ,以使其正常工作.这意味着使用$unwind:
db.orders.aggregate([
// Unwind the source
{ "$unwind": "$products" },
// Do the lookup matching
{ "$lookup": {
"from": "products",
"localField": "products",
"foreignField": "_id",
"as": "productObjects"
}},
// Unwind the result arrays ( likely one or none )
{ "$unwind": "$productObjects" },
// Group back to arrays
{ "$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"productObjects": { "$push": "$productObjects" }
}}
])
Run Code Online (Sandbox Code Playgroud)
之后$lookup每个数组成员的比赛结果本身就是一个数组,让你$unwind再次$group来$push对最终结果新的阵列.
请注意,任何未找到的"左连接"匹配将为给定产品上的"productObjects"创建一个空数组,从而在$unwind调用第二个产品时取消"product"元素的文档.
虽然直接应用到阵列将是很好的,这是目前这只是如何工作的匹配奇异值的可能许多.
由于$lookup基本上是很新的,它目前是将熟悉的那些谁是熟悉的猫鼬作为一个"可怜的芒版" .populate()提供有方法.不同之处在于$lookup提供"连接"的"服务器端"处理而不是客户端,并且$lookup当前缺少某些"成熟度" .populate()(例如直接在数组上插入查找).
这实际上是改进SERVER-22881的一个指定问题,所以如果运气好,这将会在下一个版本或之后很快发布.
作为一种设计原则,您当前的结构既不好也不坏,但在创建任何"连接"时只需要管理费用.因此,MongoDB在开始时的基本原则适用,如果您"可以"在一个集合中"预先加入"数据,那么最好这样做.
可以说$lookup作为一般原则的另一件事是,这里"加入"的意图是反过来而不是这里所示.因此,不是将其他文档的"相关ID"保留在"父"文档中,最有效的一般原则是"相关文档"包含对"父"的引用.
所以$lookup可以说"最好的工作"与"关系设计"相反,这与像猫鼬一样.populate()执行它的客户端连接是相反的.通过识别每个"很多"中的"一个",您只需先拉入相关项而不需要$unwind数组.
小智 45
该$lookup聚集流水线阶段现在所拥有的阵列直接运行(3.3.4版本).
小智 9
您还可以使用该pipeline阶段对子文档阵列执行检查
这是使用示例python(抱歉,我是蛇人)。
db.products.aggregate([
{ '$lookup': {
'from': 'products',
'let': { 'pid': '$products' },
'pipeline': [
{ '$match': { '$expr': { '$in': ['$_id', '$$pid'] } } }
// Add additional stages here
],
'as':'productObjects'
}
])
Run Code Online (Sandbox Code Playgroud)
这里的前提条件是,以匹配所有对象ObjectId array(国外_id也就是在local现场/道具products)。
您还可以使用其他stages 清理或投影外部记录,如上面的注释所示。
使用$ 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"
}
]);
Run Code Online (Sandbox Code Playgroud)
结果:
{
"_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"
}
}
Run Code Online (Sandbox Code Playgroud)
我不同意,如果我们以 $match 阶段作为前缀,我们可以使 $lookup 与 IDs 数组一起工作。
// replace IDs array with lookup results
db.products.aggregate([
{ $match: { products : { $exists: true } } },
{
$lookup: {
from: "products",
localField: "products",
foreignField: "_id",
as: "productObjects"
}
}
])Run Code Online (Sandbox Code Playgroud)
如果我们想将查找结果传递给管道,事情会变得更加复杂。但话又说回来,有一种方法可以做到这一点(@user12164 已经建议):
// replace IDs array with lookup results passed to pipeline
db.products.aggregate([
{ $match: { products : { $exists: true } } },
{
$lookup: {
from: "products",
let: { products: "$products"},
pipeline: [
{ $match: { $expr: {$in: ["$_id", "$$products"] } } },
{ $project: {_id: 0} } // suppress _id
],
as: "productObjects"
}
}
])Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
80558 次 |
| 最近记录: |