Golang中如何使用MongoDB设置外键和关系

Golang中如何使用MongoDB设置外键和关系 我希望用户能够用附加信息更新他们的个人资料。

我只想将用户映射到指定的集合。

在MGO(或任何其他驱动程序中)是否有办法为表设置关系/外键?

谢谢!

现有应用示例:

用户名 密码

我想为当前用户添加地址、联系电话、年龄、■■■、出生日期详细信息。

如何使用MGO或任何其他能够帮助实现此功能的驱动程序来实现这一点?

已经使用Ruby on Rails和Mongo作为数据库实现了相同的功能,但无法使用Go语言完成此操作。

请提供建议,谢谢!

3 回复

非常感谢你 @CurtGreen 😊

现在我对设置引用有了更清晰的理解。我会尝试这些方法 😊

再次感谢!

更多关于Golang中如何使用MongoDB设置外键和关系的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


MongoDB作为NoSQL数据库,在实现方式上与SQL方法略有不同:[https://medium.com/@dis_is_patrick/mongodb-relations-26201385b919](https://medium.com/@dis_is_patrick/mongodb-relations-26201385b919)

这里有几个选择:引用或嵌入。但如果您想了解更多关于在mgo中使用MongoDB引用的信息,您需要了解DBRef类型:https://github.com/go-mgo/mgo/blob/v2/session.go#L3295

以下是MongoDB官方文档中关于引用主题的页面:https://docs.mongodb.com/manual/reference/database-references/

在Go语言中,MongoDB是一个文档型数据库,不直接支持传统关系型数据库中的外键约束。不过,你可以通过嵌入文档或引用文档的方式来模拟关系。以下是使用MongoDB官方驱动(go.mongodb.org/mongo-driver)实现用户和附加信息关联的示例。

方法1:嵌入文档(适用于一对一关系)

将用户的附加信息直接嵌入到用户文档中。这种方式适合数据频繁一起查询且更新不频繁的场景。

package main

import (
    "context"
    "time"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

// 用户结构体,包含基本信息和嵌入的附加信息
type User struct {
    ID       primitive.ObjectID `bson:"_id,omitempty"`
    Username string             `bson:"username"`
    Password string             `bson:"password"`
    Profile  Profile            `bson:"profile"` // 嵌入文档
}

// 附加信息结构体
type Profile struct {
    Address    string    `bson:"address"`
    Phone      string    `bson:"phone"`
    Age        int       `bson:"age"`
    BirthDate  time.Time `bson:"birth_date"`
}

// 更新用户附加信息的函数
func updateUserProfile(client *mongo.Client, userID primitive.ObjectID, profile Profile) error {
    collection := client.Database("testdb").Collection("users")
    filter := bson.M{"_id": userID}
    update := bson.M{"$set": bson.M{"profile": profile}}
    _, err := collection.UpdateOne(context.TODO(), filter, update)
    return err
}

方法2:引用文档(适用于一对多或数据独立更新)

将附加信息存储在独立的集合中,并在用户文档中存储引用ID。这种方式更灵活,适合数据可能独立更新或一对多的场景。

package main

import (
    "context"
    "time"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

// 用户结构体,包含基本信息和对附加信息的引用
type User struct {
    ID        primitive.ObjectID `bson:"_id,omitempty"`
    Username  string             `bson:"username"`
    Password  string             `bson:"password"`
    ProfileID primitive.ObjectID `bson:"profile_id"` // 引用附加信息文档的ID
}

// 附加信息结构体,存储在独立集合
type Profile struct {
    ID        primitive.ObjectID `bson:"_id,omitempty"`
    Address   string             `bson:"address"`
    Phone     string             `bson:"phone"`
    Age       int                `bson:"age"`
    BirthDate time.Time          `bson:"birth_date"`
}

// 创建或更新附加信息,并关联到用户
func updateUserProfile(client *mongo.Client, userID primitive.ObjectID, profile Profile) error {
    profileCollection := client.Database("testdb").Collection("profiles")
    userCollection := client.Database("testdb").Collection("users")

    // 插入或更新附加信息文档
    profile.ID = primitive.NewObjectID()
    _, err := profileCollection.InsertOne(context.TODO(), profile)
    if err != nil {
        return err
    }

    // 更新用户文档,设置引用ID
    filter := bson.M{"_id": userID}
    update := bson.M{"$set": bson.M{"profile_id": profile.ID}}
    _, err = userCollection.UpdateOne(context.TODO(), filter, update)
    return err
}

// 查询用户及其附加信息
func getUserWithProfile(client *mongo.Client, userID primitive.ObjectID) (User, Profile, error) {
    userCollection := client.Database("testdb").Collection("users")
    profileCollection := client.Database("testdb").Collection("profiles")

    var user User
    err := userCollection.FindOne(context.TODO(), bson.M{"_id": userID}).Decode(&user)
    if err != nil {
        return User{}, Profile{}, err
    }

    var profile Profile
    err = profileCollection.FindOne(context.TODO(), bson.M{"_id": user.ProfileID}).Decode(&profile)
    return user, profile, err
}

连接MongoDB的示例代码

func main() {
    client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        panic(err)
    }
    defer client.Disconnect(context.TODO())

    // 使用上述函数进行更新或查询操作
    userID, _ := primitive.ObjectIDFromHex("507f1f77bcf86cd799439011")
    profile := Profile{
        Address:   "123 Main St",
        Phone:     "555-1234",
        Age:       30,
        BirthDate: time.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC),
    }

    err = updateUserProfile(client, userID, profile)
    if err != nil {
        panic(err)
    }
}

说明:

  • 嵌入文档:数据存储在单一文档中,查询高效,但可能导致文档变大。
  • 引用文档:数据分散,适合独立更新或一对多关系,但需要多次查询来获取完整数据。
  • MongoDB不强制外键约束,应用层需确保引用的一致性。

根据你的需求选择合适的方法。如果附加信息频繁更新或可能被多个用户共享,推荐使用引用方式;如果数据稳定且主要与单一用户关联,嵌入方式更简单高效。

回到顶部