我有这样的收藏集matches
。我正在使用players
对象{key: ObjectId, key: ObjectID}
而不是经典数组[ObjectId, ObjectID]
作为参考players
集合
{
"_id": ObjectId("5eb93f8efd259cd7fbf49d55"),
"date": "01/01/2020",
"players": {
"home": ObjectId("5eb93f8efd259cd7fbf49d59"),
"away": ObjectId("5eb93f8efd259cd7fbf49d60")
}
},
{...}
和players
集合:
{
"_id": ObjectId("5eb93f8efd259cd7fbf49d59"),
"name": "Roger Federer"
"country": "Suiza"
},
{
"_id": ObjectId("5eb93f8efd259cd7fbf49d60"),
"name": "Rafa Nadal"
"country": "España"
},
{...}
做mongoDB lookup
的更好方法是什么?像这样的东西正确吗?
const rows = await db.collection('matches').aggregate([
{
$lookup: {
from: "players",
localField: "players.home",
foreignField: "_id",
as: "players.home"
}
},
{
$lookup: {
from: "players",
localField: "players.away",
foreignField: "_id",
as: "players.away"
},
{ $unwind: "$players.home" },
{ $unwind: "$players.away" },
}]).toArray()
我想要这样的输出:
{
_id: 5eb93f8efd259cd7fbf49d55,
date: "12/05/20",
players: {
home: {
_id: 5eb93f8efd259cd7fbf49d59,
name: "Roger Federer",
country: "Suiza"
},
away: {
_id: 5eb93f8efd259cd7fbf49d60,
name: "Rafa Nadal",
country: "España"
}
}
}
{...}
您可以在下面的汇总查询中尝试:
db.matches.aggregate([
{
$lookup: {
from: "players",
localField: "players.home",
foreignField: "_id",
as: "home"
}
},
{
$lookup: {
from: "players",
localField: "players.away",
foreignField: "_id",
as: "away"
}
},
/** Check output of lookup is not empty array `[]` & get first doc & write it to respective field, else write the same value as original */
{
$project: {
date: 1,
"players.home": { $cond: [ { $eq: [ "$home", [] ] }, "$players.home", { $arrayElemAt: [ "$home", 0 ] } ] },
"players.away": { $cond: [ { $eq: [ "$away", [] ] }, "$players.away", { $arrayElemAt: [ "$away", 0 ] } ] }
}
}
])
Test: mongoplayground
当前查询的更改或问题:
1]当您一个接一个地使用两个$unwind
阶段时,如果home
或away
字段中的任何一个在players
集合中均没有匹配的文档,则结果为甚至也没有得到实际的match
文件,但是为什么呢?这是因为,如果在$unwind
上执行[]
(由查找阶段返回),则展开将从结果中删除该父文档。要解决此问题,您需要在preservenullandemptyarrays阶段中使用unwind
选项。
2]好吧,还有另一种方法可以不用实际使用$unwind
。因此,请勿使用as: "players.home"
或as: "players.away"
,因为您实际上是在写回原始字段,以防万一,如果找不到匹配的文档,则会将一个空数组[]
写入到"home"
的实际字段中]或"away"
到不匹配的地方(在这种情况下,您会丢失匹配文档中特定字段中存在的实际ObjectId()
值)。因此,将查找输出写入新字段。
或者甚至更有效的方法,您可以尝试使用$lookup
进行一次查找,而不是两个multiple-join-conditions-with-lookup阶段:
mongoplaygrounddb.matches.aggregate([ { $lookup: { from: "players", let: { home: "$players.home", away: "$players.away" }, pipeline: [ { $match: { $expr: { $or: [ { $eq: [ "$_id", "$$home" ] }, { $eq: [ "$_id", "$$away" ] } ] } } } ], as: "data" } } ])
Test:
注意:
此处将所有与players
匹配且与away
或home
字段无关的匹配文档都推入data
数组,因此,为了使数据库操作简单,可以从数据库以及实际的匹配文档&您可以分担代码的工作,以将各个对象从data
数组映射到players.home
&players.away
。