使用 $lookup 进行 MongoDB 嵌套查询
Go MongoDB nested query with $lookup
我的项目需要一个相当复杂的查询,但似乎找不到适用于它的有效实现。我下面的函数有效,但现在我需要添加一个 $lookup
语句来填充配置文件对象。如您所见,每个 Match 都有 PartnerA
和 partnerB
类型 Partner
。每个 Partner
都有一个 LiteOffer
有个人资料。需要使用 PartnerA.ID
和 PartnerB.ID
作为查找的 localFields 从配置文件集合中添加配置文件。
基本上问题是这样的:我在我的函数中收到一个 Partner.ID,它也是 profile
集合中配置文件的 ID。然后我需要从 matches
集合中过滤掉在 PartnerA 或 PartnerB 中具有相同 Partner.ID 的所有 matches
(只有一个合作伙伴具有相同的 ID。另一个会有不同的)。最后,我必须使用 mongo $lookup
使用相应的 Partner.ID
检索 PartnerA.Offer.Profile 和 PartnerB.Offer.Profile 的配置文件
这是 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"}},
}
我希望这能解释一切。我花了几个小时,但找不到解决方案。任何帮助将不胜感激。
如果您有任何问题,请随时提出。
谢谢!
带查找的聚合查询(在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)
所以我设法自己解决了这个问题,这是功能代码:
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
}
我的项目需要一个相当复杂的查询,但似乎找不到适用于它的有效实现。我下面的函数有效,但现在我需要添加一个 $lookup
语句来填充配置文件对象。如您所见,每个 Match 都有 PartnerA
和 partnerB
类型 Partner
。每个 Partner
都有一个 LiteOffer
有个人资料。需要使用 PartnerA.ID
和 PartnerB.ID
作为查找的 localFields 从配置文件集合中添加配置文件。
基本上问题是这样的:我在我的函数中收到一个 Partner.ID,它也是 profile
集合中配置文件的 ID。然后我需要从 matches
集合中过滤掉在 PartnerA 或 PartnerB 中具有相同 Partner.ID 的所有 matches
(只有一个合作伙伴具有相同的 ID。另一个会有不同的)。最后,我必须使用 mongo $lookup
使用相应的 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"}},
}
我希望这能解释一切。我花了几个小时,但找不到解决方案。任何帮助将不胜感激。
如果您有任何问题,请随时提出。
谢谢!
带查找的聚合查询(在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)
所以我设法自己解决了这个问题,这是功能代码:
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
}