我的项目需要一个相当复杂的查询,但似乎找不到一个有效的实现。下面的函数可以工作,但现在我需要添加一个$lookup
语句来填充配置文件对象。正如您所看到的,每个Match都有一个类型为Partner
的PartnerA
和partnerB
。每个Partner
都有一个LiteOffer
,它有一个配置文件。需要使用PartnerA.ID
和PartnerB.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
}
带查找的聚合查询(在mongosh
或mongo
shell中运行(:
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)