ऊपर दी गई क्वेरी User
. से "लगभग" मेल खाने वाले दस्तावेज़ लौटाती है दस्तावेज़, लेकिन उनके पास प्रत्येक उपयोगकर्ता के पद भी हैं। तो मूल रूप से परिणाम User
. की एक श्रृंखला है Post
. के साथ दस्तावेज़ सरणी या टुकड़ा एम्बेडेड ।
एक तरीका यह होगा कि Posts []*Post
जोड़ें User
. के लिए फ़ील्ड स्वयं, और हम हो जाएंगे:
type User struct {
ID string `bson:"_id"`
Name string `bson:"name"`
Registered time.Time `bson:"registered"`
Posts []*Post `bson:"posts,omitempty"`
}
हालांकि यह काम करता है, यह User
. का विस्तार करने के लिए "ओवरकिल" लगता है Post
के साथ सिर्फ एक प्रश्न के लिए। अगर हम इस रास्ते को जारी रखते हैं, तो हमारा User
प्रकार विभिन्न प्रश्नों के लिए बहुत सारे "अतिरिक्त" फ़ील्ड के साथ फूला हुआ हो जाएगा। उल्लेख नहीं है कि क्या हम Post
भरते हैं फ़ील्ड और उपयोगकर्ता को सहेजें, वे पोस्ट अंत में User
. के अंदर सहेजे जाएंगे दस्तावेज़। वह नहीं जो हम चाहते हैं।
दूसरा तरीका यह होगा कि एक UserWithPosts
बनाया जाए कॉपी टाइप करें User
, और एक Posts []*Post
adding जोड़ना खेत। कहने की जरूरत नहीं है कि यह बदसूरत और अनम्य है (User
. में किए गए कोई भी परिवर्तन UserWithPosts
. पर प्रतिबिंबित करना होगा मैन्युअल रूप से)।
स्ट्रक्चर एंबेडिंग के साथ
मूल User
को संशोधित करने के बजाय , और एक नया UserWithPosts
. बनाने के बजाय "स्क्रैच" से टाइप करें, हम स्ट्रक्चर एम्बेडिंग
का उपयोग कर सकते हैं (मौजूदा User
. का पुन:उपयोग करना और Post
प्रकार) एक छोटी सी चाल के साथ:
type UserWithPosts struct {
User `bson:",inline"`
Posts []*Post `bson:"posts"`
}
नोट करें bson टैग मान
",inline"
. यह bson.Marshal()
पर प्रलेखित है।
और bson.Unmarshal()
(हम इसे अनमर्शलिंग के लिए इस्तेमाल करेंगे):
एम्बेडिंग और ",inline"
. का उपयोग करके टैग मान, UserWithPosts
टाइप स्वयं ही अनमर्शलिंग User
. के लिए एक मान्य लक्ष्य होगा दस्तावेज़, और उसका Posts []*Post
खोजे गए "posts"
. के लिए फ़ील्ड एक आदर्श विकल्प होगा ।
इसका उपयोग करना:
var uwp *UserWithPosts
it := pipe.Iter()
for it.Next(&uwp) {
// Use uwp:
fmt.Println(uwp)
}
// Handle it.Err()
या एक ही चरण में सभी परिणाम प्राप्त करना:
var uwps []*UserWithPosts
err := pipe.All(&uwps)
// Handle error
UserWithPosts
. की टाइप डिक्लेरेशन स्थानीय घोषणा हो भी सकती है और नहीं भी। यदि आपको कहीं और इसकी आवश्यकता नहीं है, तो यह उस फ़ंक्शन में एक स्थानीय घोषणा हो सकती है जहां आप एकत्रीकरण क्वेरी को निष्पादित और संसाधित करते हैं, इसलिए यह आपके मौजूदा प्रकारों और घोषणाओं को ब्लोट नहीं करेगा। यदि आप इसका पुन:उपयोग करना चाहते हैं, तो आप इसे पैकेज स्तर (निर्यात या गैर-निर्यात) पर घोषित कर सकते हैं, और जहां भी आपको इसकी आवश्यकता हो, इसका उपयोग कर सकते हैं।
एकत्रीकरण को संशोधित करना
एक अन्य विकल्प MongoDB के $replaceRoot
का उपयोग करना है
परिणाम दस्तावेज़ों को "पुनर्व्यवस्थित" करने के लिए, इसलिए एक "सरल" संरचना पूरी तरह से दस्तावेज़ों को कवर करेगी:
// Query users with their posts:
pipe := collUsers.Pipe([]bson.M{
{
"$lookup": bson.M{
"from": "posts",
"localField": "_id",
"foreignField": "userID",
"as": "posts",
},
},
{
"$replaceRoot": bson.M{
"newRoot": bson.M{
"user": "$$ROOT",
"posts": "$posts",
},
},
},
})
इस रीमैपिंग के साथ, परिणाम दस्तावेजों को इस तरह से तैयार किया जा सकता है:
type UserWithPosts struct {
User *User `bson:"user"`
Posts []*Post `bson:"posts"`
}
ध्यान दें कि जब तक यह काम करता है, Post
सभी दस्तावेज़ों का फ़ील्ड सर्वर से दो बार प्राप्त किया जाएगा:एक बार Post
. के रूप में लौटाए गए दस्तावेज़ों का क्षेत्र, और एक बार User
. के क्षेत्र के रूप में; हम इसका नक्शा / उपयोग नहीं करते हैं लेकिन यह परिणाम दस्तावेजों में मौजूद है। इसलिए यदि यह समाधान चुना जाता है, तो user.posts
फ़ील्ड को हटा दिया जाना चाहिए उदा। एक $project
. के साथ मंच:
pipe := collUsers.Pipe([]bson.M{
{
"$lookup": bson.M{
"from": "posts",
"localField": "_id",
"foreignField": "userID",
"as": "posts",
},
},
{
"$replaceRoot": bson.M{
"newRoot": bson.M{
"user": "$$ROOT",
"posts": "$posts",
},
},
},
{"$project": bson.M{"user.posts": 0}},
})