sql >> Database >  >> NoSQL >> MongoDB

Mgo-aggregatie:hoe modeltypen opnieuw te gebruiken om gemengde resultaten op te vragen en te ontrafelen?

De bovenstaande query retourneert documenten die "bijna" overeenkomen met User documenten, maar ze hebben ook de berichten van elke gebruiker. Dus eigenlijk is het resultaat een reeks User documenten met een Post matrix of segment ingesloten .

Een manier zou zijn om een ​​Posts []*Post . toe te voegen veld naar de User zelf, en we zouden klaar zijn:

type User struct {
    ID         string    `bson:"_id"`
    Name       string    `bson:"name"`
    Registered time.Time `bson:"registered"`
    Posts      []*Post   `bson:"posts,omitempty"`
}

Hoewel dit werkt, lijkt het "overkill" om User uit te breiden met Posts gewoon omwille van een enkele vraag. Als we op deze weg zouden doorgaan, zou onze User type zou opgeblazen worden met veel "extra" velden voor verschillende zoekopdrachten. Om nog maar te zwijgen van het feit of we de Posts vullen veld en sla de gebruiker op, die berichten zouden uiteindelijk worden opgeslagen in de User document. Niet wat we willen.

Een andere manier zou zijn om een ​​UserWithPosts . te maken type kopiëren User , en het toevoegen van een Posts []*Post veld. Onnodig te zeggen dat dit lelijk en inflexibel is (alle wijzigingen aangebracht in User zou moeten worden weerspiegeld in UserWithPosts handmatig).

Met structurele inbedding

In plaats van de originele User te wijzigen , en in plaats van een nieuwe UserWithPosts . te maken type from "scratch", we kunnen struct embedding gebruiken (hergebruik van de bestaande User en Post typen) met een klein trucje:

type UserWithPosts struct {
    User  `bson:",inline"`
    Posts []*Post `bson:"posts"`
}

Let op de bson tagwaarde ",inline" . Dit is gedocumenteerd op bson.Marshal() en bson.Unmarshal() (we zullen het gebruiken voor het ontrafelen):

Door insluiten te gebruiken en de ",inline" tagwaarde, de UserWithPosts type zelf zal een geldig doelwit zijn voor het ontrappen van User documenten, en zijn Post []*Post veld zal een perfecte keuze zijn voor de opgezochte "posts" .

Het gebruiken:

var uwp *UserWithPosts
it := pipe.Iter()
for it.Next(&uwp) {
    // Use uwp:
    fmt.Println(uwp)
}
// Handle it.Err()

Of alle resultaten in één stap krijgen:

var uwps []*UserWithPosts
err := pipe.All(&uwps)
// Handle error

De typedeclaratie van UserWithPosts kan al dan niet een lokale aangifte zijn. Als je het nergens anders nodig hebt, kan het een lokale declaratie zijn in de functie waar je de aggregatiequery uitvoert en verwerkt, zodat het je bestaande typen en declaraties niet opzwelt. Als u het opnieuw wilt gebruiken, kunt u het op pakketniveau declareren (geëxporteerd of niet-geëxporteerd) en gebruiken waar u het nodig heeft.

De aggregatie wijzigen

Een andere optie is om MongoDB's $replaceRoot te gebruiken om de resultaatdocumenten te "herschikken", zodat een "eenvoudige" structuur de documenten perfect dekt:

// 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",
            },
        },
    },
})

Met deze remapping kunnen de resultaatdocumenten als volgt worden gemodelleerd:

type UserWithPosts struct {
    User  *User   `bson:"user"`
    Posts []*Post `bson:"posts"`
}

Merk op dat hoewel dit werkt, de posts veld van alle documenten wordt twee keer van de server opgehaald:eenmaal als de posts veld van de geretourneerde documenten, en eenmaal als het veld van user; we brengen het niet in kaart / gebruiken het niet, maar het is aanwezig in de resultaatdocumenten. Dus als deze oplossing wordt gekozen, worden de user.posts veld moet worden verwijderd, b.v. met een $project stadium:

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}},
})



  1. DateTime in UTC converteren naar mijn lokale tijd?

  2. Hoe een veel-op-veel-relatie van SQL naar mongoDB-verzameling te converteren?

  3. Mongoose findOneAndUpdate en runValidators werken niet

  4. Hoe herstart ik de belofteketen voorwaardelijk vanaf het begin?