英文:
Go MongoDB nested query with $lookup
问题
我有一个非常复杂的查询,需要在我的项目中使用,但似乎找不到一个可行的实现方法。下面是我的函数,它可以工作,但现在我需要添加一个$lookup
语句来填充profile对象。如你所见,每个Match都有一个类型为Partner的PartnerA和partnerB。每个Partner都有一个LiteOffer,其中包含一个profile。需要使用PartnerA.ID和PartnerB.ID作为lookup的localFields,从profile集合中添加profile。
基本上问题是这样的:我在函数中收到一个Partner.ID,它也是profile集合中一个profile的id。然后,我需要过滤掉所有在matches集合中具有相同Partner.ID的matches,无论是在PartnerA还是PartnerB中(只有一个Partner将具有相同的ID,另一个将具有不同的ID)。最后,我必须使用mongo的$lookup
来检索PartnerA.Offer.Profile和PartnerB.Offer.Profile的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" diff:"-"
}
这是我的函数:
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,但现在我需要将这两个查询合并为一个:
pipeline := mongo.Pipeline{
{{"$match", match}},
{{"$lookup", bson.M{
"from": "profile",
"localField": "partnerA.id",
"foreignField": "_id",
"as": "profile",
}}},
{{"$unwind", "$profile"}},
}
希望这样解释清楚了。我花了几个小时,找不到解决办法。非常感谢您的帮助。
如果您有任何问题,请随时提问。
谢谢!
英文:
I have quite a complex query that I need for my project and can't seem to find a working implementation for it. My function below works, but now I need to add a $lookup
statement to populate the profile object. As you can see every Match has a PartnerA
and partnerB
of type Partner
. Every Partner
has a LiteOffer
witch has a profile. The profile needs to be added from the profile collection using PartnerA.ID
and PartnerB.ID
as the localFields for lookup.
Basically the issue is this: I receive one Partner.ID in my function which is also an id of a profile in a profile
collection. Then I need to filter out all matches
from the matches
collection that have the same Partner.ID either in PartnerA or in PartnerB (only one of the Partners will have the same ID. The other one will have a different one). Finally I have to use mongo $lookup
to retrieve the profiles for both PartnerA.Offer.Profile and PartnerB.Offer.Profile using the corresponding Partner.ID
Here is the Match struct (I cut out many of the fields so it would be easier to read):
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"
}
Here is my function:
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
}
Here is a pipeline that I used that works, but now I need to somehow combine these two queries into one:
pipeline := mongo.Pipeline{
{{"$match", match}},
{{"$lookup", bson.M{
"from": "profile",
"localField": "partnerA.id",
"foreignField": "_id",
"as": "profile",
}}},
{{"$unwind", "$profile"}},
}
I hope this explained everything. It has taken me hours and I can't find the solution. any help would be highly appreciated.
If you have any questions feel free to ask.
Thank you!
答案1
得分: 1
所以我成功解决了这个问题,以下是函数代码:
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
}
英文:
So I managed To solve this issue myself and here is the function code:
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
}
答案2
得分: 0
聚合查询与查找(在mongosh
或mongo
shell中运行):
var ID = 'some_value' // 与合作伙伴A和B的ID匹配
var pipeline = [
// 在'match'集合上进行初始过滤
{
$match: {
unlocked: false,
deletedAt: null,
$or: [
{ $and: [ { 'partnerA.id': ID }, { 'partnerA.looksInteresting': false } ] },
{ $and: [ { 'partnerB.id': ID }, { 'partnerA.looksInteresting': false } ] },
]
} },
// 查找'profile'集合,并获取相应合作伙伴A和B(来自'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' ] },
]
}
}},
]
}},
]
// 运行聚合查询,使用上面定义的管道
db.getCollection('match').aggregate(pipeline)
以上是使用查找的聚合查询代码。
英文:
The aggregation query with lookup (runs in mongosh
or 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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论