使用$lookup进行MongoDB嵌套查询



我的项目需要一个相当复杂的查询,但似乎找不到一个有效的实现。下面的函数可以工作,但现在我需要添加一个$lookup语句来填充配置文件对象。正如您所看到的,每个Match都有一个类型为PartnerPartnerApartnerB。每个Partner都有一个LiteOffer,它有一个配置文件。需要使用PartnerA.IDPartnerB.ID作为查找的localFields从配置文件集合中添加配置文件。

基本上问题是:我在函数中收到一个Partner.ID,它也是profile集合中配置文件的ID。然后,我需要从matches集合中筛选出在PartnerA或PartnerB中具有相同Partner.ID的所有matches(只有一个合作伙伴具有相同的ID。另一个将具有不同的ID(。最后,我必须使用mongo$lookup来检索PartnerA.Offer.Profile和PartnerB.Offer.Profile的配置文件,并使用相应的Partner.ID

这是Match结构(我去掉了许多字段,这样更容易阅读(:

type Match struct {
ID        primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
PartnerA  Partner            `json:"partnerA" bson:"partnerA"`
PartnerB  Partner            `json:"partnerB" bson:"partnerB"`
Unlocked  bool               `json:"unlocked" bson:"unlocked"`
DeletedAt time.Time          `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
}
type Partner struct {
ID               primitive.ObjectID `json:"id,omitempty" bson:"id,omitempty"`
Offer            LiteOffer          `json:"offer,omitempty" bson:"offer,omitempty"`
LooksInteresting bool               `json:"looksInteresting" bson:"looksInteresting"`
Unlocked         bool               `json:"unlocked" bson:"unlocked"`
}
type LiteOffer struct {
ID           primitive.ObjectID `json:"id,omitempty" bson:"id,omitempty"`
Tags         []Tag              `json:"tags" bson:"tags,omitempty"`
Profile      Profile            `json:"profile,omitempty" bson:"profile,omitempty"`
}
type Profile struct {
ID          primitive.ObjectID `json:"id,omitempty" bson:"id" diff:"-"`
Name        string             `json:"name,omitempty" bson:"name,omitempty" diff:"-"`
Surname     string             `json:"surname,omitempty" bson:"surname,omitempty" 
}

这是我的功能:

func getMatchesByProfileId(id primitive.ObjectID) (*[]Match, error) {
var matches []Match
filter := bson.M{
"$or": []bson.M{
{
"partnerA.id":               id,
"partnerA.looksInteresting": false,
},
{
"partnerB.id":               id,
"partnerB.looksInteresting": false,
},
},
"unlocked":  false,
"deletedAt": nil,
}
ctx, _ := db.GetTimeoutContext()
result, err := getMatchCollection().Find(ctx, filter)
if err != nil {
log.Error("Could not find matches, Error: ", err)
return nil, err
}
for result.Next(ctx) {
var m Match
if err = result.Decode(&m); err != nil {
log.Error("Could not decode offer in getMatchesByProfileId", err)
return nil, err
}
matches = append(matches, m)
}
return &matches, nil
}

这是我使用过的一个管道,可以工作,但现在我需要以某种方式将这两个查询合并为一个:

pipeline := mongo.Pipeline{
{{"$match", match}},
{{"$lookup", bson.M{
"from":         "profile",
"localField":   "partnerA.id",
"foreignField": "_id",
"as":           "profile",
}}},
{{"$unwind", "$profile"}},
}

我希望这能解释一切。我花了好几个小时都找不到解决办法。如有任何帮助,我们将不胜感激。

如果你有任何问题,请随时提问。

谢谢!

所以我自己设法解决了这个问题,下面是函数代码:

func getMatchesByProfileId(id primitive.ObjectID) (*[]Match, error) {
var matches []Match
match := bson.D{
{"unlocked", false},
{"deletedAt", nil},
{"$or", []bson.M{
{
"partnerA.id":               id,
"partnerA.looksInteresting": false,
},
{
"partnerB.id":               id,
"partnerB.looksInteresting": false,
},
}},
}
pipeline := mongo.Pipeline{
{{"$match", match}},
{{"$lookup", bson.M{
"from":         "profile",
"localField":   "partnerA.id",
"foreignField": "_id",
"as":           "partnerA.offer.profile",
}}},
{{"$unwind", "$partnerA.offer.profile"}},
{{"$lookup", bson.M{
"from":         "profile",
"localField":   "partnerB.id",
"foreignField": "_id",
"as":           "partnerB.offer.profile",
}}},
{{"$unwind", "$partnerB.offer.profile"}},
}
ctx, _ := db.GetTimeoutContext()
cursor, err := getMatchCollection().Aggregate(ctx, pipeline)
if err != nil {
log.Error("Could not aggregate matches, Error: ", err)
return nil, err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var m Match
if err = cursor.Decode(&m); err != nil {
log.Error("Could not decode matches in getMatchesByProfileId error: ", err)
return nil, err
}
matches = append(matches, m)
}
return &matches, nil
}

带查找的聚合查询(在mongoshmongoshell中运行(:

var ID = 'some_value'  // to match with the partner A and B ids
var pipeline = [
// Initial filter on the 'match' collection
{ 
$match: { 
unlocked:  false, 
deletedAt: null,
$or: [
{ $and: [ { 'partnerA.id': ID }, { 'partnerA.looksInteresting': false } ] },
{ $and: [ { 'partnerB.id': ID }, { 'partnerA.looksInteresting': false } ] },
]
} },
// Lookup 'profile' collection and get the matching details for the corresponding partner A and B (of 'match')
{ 
$lookup: {
from: 'profile',
let: { pa_id: '$partnerA.id', pb_id: '$partnerB.id' },
as: 'matched_partner_profiles',
pipeline: [
{ 
$match: { 
$expr: {
$or: [
{ $eq: [ '$$pa_id', '$_id' ] },
{ $eq: [ '$$pb_id', '$_id' ] },
]
}
}},
]
}},
]
// Run the aggregation query, using the pipeline defined above
db.getCollection('match').aggregate(pipeline)

最新更新