MongoDB in golang
MongoDB Query
MongoDB
MongoDB๋ document ์งํฅ์ ์ธ NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค์ด๋ค.
๋ค์ํ ์ดํ๋ฆฌ์ผ์ด์ ์์ MongoDB๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ๊ทธ ํ์ฅ์ฑ๊ณผ ๊ฐ๋ฐ์ ์ฉ์ด์ฑ ๋๋ฌธ์ด๋ค.
Simple Query
๊ฐ๋จํ CRUD Query ์ ๋ํด์ ์ดํด๋ณด์.
ํผํฉํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๊ณ , ๊ทธ ์์ ๊ฐ ๋งค์ฐ ๋ค์ํ๋ค.
์์ธํ ๊ฒ์ ๊ณต์ ๋ฌธ์์ฐธ๊ณ ํ์.
Find
bson.M
์ ์ฌ์ฉํด๋ ๋๊ณ , map[string]interface{}
ํน์ map[string]any
์ ๊ฐ์ด ํํฐ ์กฐ๊ฑด์ ์ถ๊ฐํ ์ ์๋ค.
ctx := context.TODO()
collection.Find(ctx, bson.M{"name": "primrose"})
collection.Find(ctx, map[string]interface{}{"name": "primrose"})
collection.FindOne(ctx, bson.M{"name": "primrose"})
collection.FindOne(ctx, map[string]interface{}{"name": "primrose"})
Insert
๊ตฌ์กฐ์ฒด๋ฅผ ์ฌ์ฉํด๋ ๋๊ณ , ๋จ์ํ document ํํ๋ผ๋ฉด bson.M ์ ๊ทธ๋๋ก ์ฌ์ฉํด๋ ๊ด์ฐฎ๋ค.
ctx = context.TODO()
collection.InsertOne(ctx, bson.M{"name": "primrose"})
collection.InsertMany(ctx, []interface{}{bson.M{"name": "primrose"}})
type User struct {
Name string `bson:"name"`
}
ctx = context.TODO()
collection.InsertOne(ctx, User{Name: "primrose"})
collection.InsertMany(ctx, []interface{}{User{Name: "primrose"}})
Update
MongoDB์์ ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋จ์ผ ๋ฌธ์ ์
๋ฐ์ดํธ(UpdateOne
)์ ์ฌ๋ฌ ๋ฌธ์ ๋์ ์
๋ฐ์ดํธ(UpdateMany
), ๊ทธ๋ฆฌ๊ณ ๋๋ ์
๋ฐ์ดํธ(BulkWrite
)๋ฅผ ํฌํจํ๋ค.
๊ฐ๊ฐ์ ์ฌ์ฉ ์๋ฅผ ์ดํด๋ณด๋ฉฐ, ํน์ ์ํฉ์์ ์ด๋ป๊ฒ ์ต์ ํํ ์ ์๋์ง ์์๋ณด์.
ctx = context.TODO()
// ๋จ์ผ ๋ฌธ์๋ฅผ ์
๋ฐ์ดํธ ํ ๋๋ UpdateOne ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
// ์ด ๋ฉ์๋๋ ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก ํํฐ ์กฐ๊ฑด์, ๋ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก ์
๋ฐ์ดํธํ ๋ด์ฉ์ ๋ฐ๋๋ค.
collection.UpdateOne(ctx, bson.M{"name": "primrose"}, bson.M{"$set": bson.M{"name": "primrose"}})
filter := bson.M{"name": "primrose"}
update := bson.M{"$set": bson.M{"name": "primrose"}}
ctx = context.TODO()
collection.UpdateOne(ctx, filter, update)
// ์ฌ๋ฌ ๋ฌธ์๋ฅผ ๋์์ ์
๋ฐ์ดํธ ํ ๋๋ UpdateMany ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
// ์ด ๋ฐฉ๋ฒ์ ์ฃผ๋ก ๋์ผํ ํํฐ ์กฐ๊ฑด์ ๋ถํฉํ๋ ์ฌ๋ฌ ๋ฌธ์์ ๋์ผํ ๋ณ๊ฒฝ์ ์ ์ฉํ ๋ ์ ์ฉํ๋ค.
collection.UpdateMany(ctx, filter, update)
UpdateMany ๋ ๋์ผํ ํํฐ๋ก ์ฌ๋ฌ ๊ฐ์ document ๋ฅผ ์์ ํด์ผ ํ ๋ ์ฌ์ฉ๋๋ค.
๊ทธ๋ฐ๋ฐ 500๊ฐ์ ๋ฌธ์๋ฅผ ๊ฐ๊ฐ ์กฐ๊ฑด์ ๋ง๊ฒ ์ ๋ฐ์ดํธ ํด์ผ ํ๋ค๋ฉด ์ด๋จ๊น?
500 ๋ฒ์ UpdateOne ๋ณด๋ค๋, BulkWrite ๋ฅผ ์ถ์ฒํ๋ค.
models := []mongo.WriteModel{
mongo.NewUpdateOneModel().SetFilter(bson.M{"name": "primrose"}).SetUpdate(bson.M{"$set": bson.M{"name": "rosie"}}),
mongo.NewUpdateOneModel().SetFilter(bson.M{"name": "daisy"}).SetUpdate(bson.M{"$set": bson.M{"name": "sunflower"}}),
// ์ถ๊ฐ์ ์ธ ์
๋ฐ์ดํธ ๋ชจ๋ธ์ ํฌํจํ ์ ์๋ค.
}
results, err := collection.BulkWrite(ctx, models)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Operations completed: %v\n", results.ModifiedCount)
์ด๋ ๊ฒ ๋๋ ์ ๋ฐ์ดํธ๋ฅผ ์งํํ ๋๋ ๊ฐ ๋ฌธ์์ ์ ํฉํ ํํฐ์ ์ ๋ฐ์ดํธ ์กฐ๊ฑด์ ๋ช ์ํด์ผ ํ๋ฉฐ, ์ด๋ ๋คํธ์ํฌ ํธ์ถ๊ณผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ถํ๋ฅผ ์ค์ด๋ ๋ฐ ๋์์ด ๋๋ค.
ํน์ ์
๋ฐ์ดํธ ์ต์
ํ์ฉ
$set
: ๋ฌธ์์ ํน์ ํ๋๋ฅผ ์ค์ ํ๊ฑฐ๋ ์ ๋ฐ์ดํธ ํ๋ค.$setOnInsert
: ๋ฌธ์๊ฐ ์ฝ์ ๋ ๋๋ง ํน์ ํ๋๋ฅผ ์ค์ ํ๋ค.
์ด ์ต์
๋ค์ ํ์ฉํ์ฌ, ์กฐ๊ฑด์ ๋ง๋ ๋ฌธ์๊ฐ ์กด์ฌํ์ง ์์ ๋ ์ฝ์
์์
์ ์ํํ๋ฉด์ ํน์ ํ๋๋ฅผ ์ค์ ํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด upsert
์ต์
์ ์ฌ์ฉํ๋ค.
updateOptions := options.Update().SetUpsert(true)
updateResult, err := collection.UpdateOne(ctx, filter, update, updateOptions)
if err != nil {
log.Fatal(err)
}
if updateResult.UpsertedCount > 0 {
fmt.Printf("One document was upserted.\n")
}
Delete
MongoDB์์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ๋ ์ฐ์ฐ์ ์ฃผ๋ก DeleteOne
๊ณผ DeleteMany
๋ฉ์๋๋ฅผ ํตํด ์ํ๋๋ค.
๊ฐ๊ฐ์ ๋ฉ์๋๋ ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ฌธ์๋ฅผ ์ญ์ ํ๋๋ฐ ์ฌ์ฉ๋๋ค.
๋จ์ผ ๋ฌธ์ ์ญ์ : DeleteOne
DeleteOne
DeleteOne
๋ฉ์๋๋ ์ฃผ์ด์ง ํํฐ์ ๋ง๋ ์ฒซ ๋ฒ์งธ ๋ฌธ์ ํ๋๋ง์ ์ญ์ ํ๋ค. ์ด ๋ฐฉ๋ฒ์ ํน์ ๋ํ๋จผํธ๋ฅผ ์ ํํ ์ง์ ํ ์ ์์ ๋ ์ฌ์ฉํ๋ฉฐ, ํํฐ๋ ์ผ๋ฐ์ ์ผ๋ก ์ ๋ํฌํ ํ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ ๋๋ค.
ctx := context.TODO()
filter := bson.M{"name": "rosie"}
result, err := collection.DeleteOne(ctx, filter)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted %v documents.\n", result.DeletedCount)
์ฌ๋ฌ ๋ฌธ์ ์ญ์ : DeleteMany
DeleteMany
DeleteMany
๋ฉ์๋๋ ์ฃผ์ด์ง ํํฐ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๋ชจ๋ ๋ฌธ์๋ฅผ ์ญ์ ํ๋ค. ์ด ๋ฐฉ๋ฒ์ ํน์ ์กฐ๊ฑด์ ๊ณต์ ํ๋ ์ฌ๋ฌ ๋ฌธ์๋ฅผ ํ ๋ฒ์ ์ญ์ ํ๊ณ ์ ํ ๋ ์ ์ฉํ๋ค.
filter := bson.M{"status": "inactive"}
deleteResult, err := collection.DeleteMany(ctx, filter)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted %v documents.\n", deleteResult.DeletedCount)
Aggregate
Aggregate
์ฐ์ฐ์ MongoDB์ ๊ฐ๋ ฅํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ธฐ๋ฅ ์ค ํ๋๋ก, ์ฌ๋ฌ ๋จ๊ณ์ ํ์ดํ๋ผ์ธ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๊ณ ์์ฝํ๋ ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์ํํ ์ ์๋ค.
๊ธฐ๋ณธ Aggregate ์ฌ์ฉ ์
๋ค์์ ๊ฐ๋จํ Aggregate
์์ ๋ก, ์ฌ์ฉ์์ ๋์ด์ ๋ฐ๋ฅธ ๊ทธ๋ฃน์ ๋ง๋ค๊ณ ๊ฐ ๊ทธ๋ฃน์ ํ๊ท ์ ์๋ฅผ ๊ณ์ฐํ๋ ์ฟผ๋ฆฌ์ด๋ค.
pipeline := mongo.Pipeline{
{{"$match", bson.D{{"age", bson.D{{"$gte", 18}}}}}},
{{"$group", bson.D{{"_id", "$age"}, {"averageScore", bson.D{{"$avg", "$score"}}}}}},
}
ctx := context.TODO()
cursor, err := collection.Aggregate(ctx, pipeline)
if err != nil {
log.Fatal(err)
}
var results []bson.M
if err = cursor.All(ctx, &results); err != nil {
log.Fatal(err)
}
fmt.Println("Age Groups and Average Scores:")
for _, result := range results {
fmt.Println(result)
}
Aggregate vs. Find
Find
๋ ๊ฐ๋จํ ๋ฌธ์ ๊ฒ์์ ์ฌ์ฉ๋๋ฉฐ, ํน์ ์กฐ๊ฑด์ ๋ถํฉํ๋ ๋ฌธ์๋ค์ ๋ฐํํ๋ค.Aggregate
๋ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ํ๊ณ ์์ฝํ์ฌ ๋ ๋ณต์กํ ์ฟผ๋ฆฌ์ ๋ฐ์ดํฐ ๋ถ์์ ๊ฐ๋ฅํ๊ฒ ํ๋ค. ์๋ฅผ ๋ค์ด, ๋ฌธ์๋ฅผ ๊ทธ๋ฃนํํ๊ณ ๊ฐ ๊ทธ๋ฃน์ ๋ํด ๊ณ์ฐ์ ์ํํ ์ ์๋ค.
Aggregate ์ฐ์ฐ์ Find ์ฐ์ฐ๋ณด๋ค ํจ์ฌ ๋ ์ ์ฐํ๊ณ ๊ฐ๋ ฅํ์ง๋ง, ๋ ๋ง์ ๋ฆฌ์์ค๋ฅผ ์๋ชจํ ์ ์์ผ๋ฏ๋ก ์ฌ์ฉ ์ ์ํฉ์ ๋ง๊ฒ ์ ์ ํ ์ ํํ๋ ๊ฒ์ด ์ค์ํ๋ค.
BSON
MongoDB์ ๋ฐ์ดํฐ ํ์์ธ BSON(Binary JSON)์ JSON๊ณผ ์ ์ฌํ์ง๋ง, ์ด์ง ํ์์ผ๋ก ํํ๋๋ฉฐ ํจ์ฌ ๋ ๋ง์ ๋ฐ์ดํฐ ํ์ ์ ์ง์ํ๋ค.
Golang์์๋ mongo-go-driver
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด bson.E
, bson.M
, bson.A
, bson.D
๋ฑ ๋ค์ํ BSON ํ์
์ ์ฌ์ฉํ ์ ์๋ค.
์ด๋ค ๊ฐ๊ฐ์ ํ์ ๊ณผ ์ฌ์ฉ ์์ ํน์ง์ ์ดํดํ๋ฉด, MongoDB ๋ฐ์ดํฐ ์์ ์ ๋ณด๋ค ํจ์จ์ ์ผ๋ก ์ํํ ์ ์๋ค.
BSON ํ์
๋ค
bson.M: ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ BSON ํ์ ์ผ๋ก,
map[string]interface{}
ํ์ ์ด๋ค. ๋ฌธ์์ ํ๋๊ฐ ๋ฌธ์์ด ํค์ ๊ด๋ จ๋ ๊ฐ์ผ๋ก ๊ตฌ์ฑ๋๋ฉฐ, ์์๋ฅผ ๋ณด์ฅํ์ง ์๋๋ค. ์ผ๋ฐ์ ์ธ CRUD ์์ ์ ๋๋ฆฌ ์ฌ์ฉ๋๋ค.// type M map[string]interface{} doc := bson.M{"name": "John", "age": 30}
bson.D: ๋ฌธ์์ ํ๋ ์์๊ฐ ์ค์ํ ๋ ์ฌ์ฉ๋๋ ํ์ ์ผ๋ก,
[]bson.E
์ ๋ณ์นญ์ด๋ค. ์ด ํ์ ์ ์ฟผ๋ฆฌ์ ์ธ๋ฑ์ค ์์ฑ ์ ์์๊ฐ ์ค์ํ ๊ฒฝ์ฐ ์ ์ฉํ๋ค.// type D []E // type E struct { // Key string // Value interface{} // } doc := bson.D{{"name", "John"}, {"age", 30}}
bson.A: ๋ฐฐ์ด์ ๋ํ๋ด๋ฉฐ,
[]interface{}
์ ์ ์ฌํ๋ค. BSON ๋ฐฐ์ด์ ์์ฑํ ๋ ์ฌ์ฉ๋๊ณ , ์ฌ๋ฌ ๊ฐ์ ๋ฆฌ์คํธ๋ฅผ ์ ์ฅํ ๋ ์ ์ฉํ๋ค.// type A []interface{} arr := bson.A{"apple", "banana", "cherry"}
bson.E: ๋ฌธ์์ ํ ํ๋๋ฅผ ๋ํ๋ด๋ ํ์ ์ผ๋ก, ํค์ ๊ฐ์ ์์ ์ ์ฅํ๋ค. ์ฃผ๋ก
bson.D
๋ด์์ ์ฌ์ฉ๋๋ค.// type E struct { // Key string // Value interface{} // } element := bson.E{Key: "name", Value: "John"}
๊ฐ ํ์
์ ์ฌ์ฉ ์์ ๊ณผ ํน์ง
bson.M์ ๊ฐ๋จํ๊ณ ์ง๊ด์ ์ธ ๋ฌธ์ ์์ ์ ์ ํฉํ๋ฉฐ, ํ๋ ์์๋ฅผ ๊ณ ๋ คํ ํ์๊ฐ ์๋ ๊ฒฝ์ฐ ์ ๋ฆฌํ๋ค. CRUD ์์ ์์์ ๋ฐ์ดํฐ ํํ๊ณผ ๊ฐ์ด ๋ณต์กํ์ง ์์ ์ฟผ๋ฆฌ์ ์ฃผ๋ก ์ฌ์ฉ๋๋ค.
bson.D๋ ํ๋ ์์๊ฐ ์ค์ํ ๊ฒฝ์ฐ, ์๋ฅผ ๋ค์ด ์ธ๋ฑ์ค ์์ฑ์ด๋ ๋ณตํฉ ์ฟผ๋ฆฌ์์ ํ๋ ์์์ ์์กดํ ๋ ์ฌ์ฉ๋๋ค. ์์๊ฐ ๊ฒฐ๊ณผ์ ์ํฅ์ ๋ฏธ์น๋ ์์ ์์ ๋งค์ฐ ์ ์ฉํ๋ค.
bson.A๋ ๋ฐฐ์ด ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋ ํ์ํ๋ฉฐ, MongoDB์์ ๋ฐฐ์ด ํ๋๋ฅผ ์ฟผ๋ฆฌํ๊ฑฐ๋ ์ ๋ฐ์ดํธํ ๋ ์ฌ์ฉ๋๋ค.
bson.E๋ ๋ณดํต
bson.D
๋ฅผ ๊ตฌ์ฑํ ๋ ์ฌ์ฉ๋๋ฉฐ, ๊ฐ๋ณ ํ๋๋ฅผ ๋ช ์์ ์ผ๋ก ํํํ ๋ ์ฌ์ฉ๋๋ค.
์ฌ์ฉ ์์
MongoDB์์ ์กฐ๊ฑด์ ๋ฐ๋ผ ํ๋ ์์๊ฐ ์ค์ํ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๊ฒฝ์ฐ bson.D
์ ์ฌ์ฉ ์๋ฅผ ๋ค ์ ์๋ค.
filter := bson.D{{"age", bson.D{{"$gt", 30}}}, {"name", "John"}}
update := bson.D{{"$set", bson.D{{"age", 32}}}}
result, err := collection.UpdateOne(ctx, filter, update)
if err != nil {
log.Fatal(err)
}
์ด ์์ ์์๋ bson.D
๋ฅผ ์ฌ์ฉํ์ฌ ํํฐ์ ์
๋ฐ์ดํธ ๋ฌธ์์ ํ๋ ์์๋ฅผ ์ ํํ ์ ์ดํ๋ค.
์ด๋ MongoDB์ ์ฟผ๋ฆฌ ํ๋๋๊ฐ ์ธ๋ฑ์ค๋ฅผ ๋ณด๋ค ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๊ฒ ํด ์ฃผ์ด ์ฑ๋ฅ ๊ฐ์ ์ ๋๋ชจํ ์ ์๋ค.
์ด๋ฌํ ๊ฐ๊ฐ์ BSON ํ์ ๋ค์ ์ดํดํ๊ณ ์ ์ ํ๊ฒ ํ์ฉํ๋ฉด, MongoDB ์์์ ๋ฐ์ดํฐ ์์ ์ด ๋ณด๋ค ์ ์ฐํ๊ณ ํจ์จ์ ์ผ๋ก ์ด๋ฃจ์ด์ง ์ ์๋ค.
Index
MongoDB์์ ์ธ๋ฑ์ค๋ ๋ฐ์ดํฐ ๊ฒ์ ์๋๋ฅผ ํฅ์์ํค๋ ์ค์ํ ๋๊ตฌ์ด๋ค.
์ธ๋ฑ์ค๋ฅผ ์ ์ ํ ์ฌ์ฉํ๋ฉด, ๋๊ท๋ชจ ๋ฐ์ดํฐ์ ์์๋ ๋น ๋ฅด๊ฒ ์ ๋ณด๋ฅผ ๊ฒ์ํ ์ ์์ผ๋ฉฐ, ์ฟผ๋ฆฌ ์ฑ๋ฅ์ ํฌ๊ฒ ๊ฐ์ ํ ์ ์๋ค.
์ด ์น์
์์๋ MongoDB์ ์ธ๋ฑ์ค๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ ์ธ๋ฑ์ฑ๋ ์ฟผ๋ฆฌ๋ฅผ Find
๋ฐ Aggregate
์์
์ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง์ ๋ํด ์์๋ณด์.
์ธ๋ฑ์ค ์ ์ฉ ๋ฐฉ๋ฒ
MongoDB์์ ์ธ๋ฑ์ค๋ฅผ ์์ฑํ๋ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ์ createIndex
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
์ด ๋ฉ์๋๋ ์ปฌ๋ ์ ์ ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๋ฉฐ, ์ฌ๋ฌ ์ต์ ์ ์ค์ ํ์ฌ ๋ค์ํ ์ ํ์ ์ธ๋ฑ์ค๋ฅผ ๊ตฌ์ฑํ ์ ์๋ค.
์์: ๋จ์ผ ํ๋ ์ธ๋ฑ์ค ์์ฑ
ctx := context.TODO()
collection := client.Database("testdb").Collection("documents")
indexModel := mongo.IndexModel{
Keys: bson.M{"name": 1}, // 1์ ์ค๋ฆ์ฐจ์ ์ธ๋ฑ์ฑ์ ์๋ฏธ
}
_, err := collection.Indexes().CreateOne(ctx, indexModel)
if err != nil {
log.Fatal(err)
}
์์: ๋ณตํฉ ์ธ๋ฑ์ค ์์ฑ
indexModel = mongo.IndexModel{
Keys: bson.D{
{Key: "lastname", Value: 1},
{Key: "firstname", Value: 1},
},
Options: options.Index().SetUnique(true), // ์ค๋ณต์ ํ์ฉํ์ง ์๋ ์ ๋ํฌ ์ธ๋ฑ์ค
}
_, err = collection.Indexes().CreateOne(ctx, indexModel)
if err != nil {
log.Fatal(err)
}
์ธ๋ฑ์ฑ๋ ์ฟผ๋ฆฌ ์ฌ์ฉ
์ธ๋ฑ์ค๋ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํ๋ ๋์ , ํจ์จ์ ์ผ๋ก ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ์ ์๋๋ก ๋๋๋ค.
Find
๋ฐ Aggregate
ํจ์์ ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ฌ ์ฑ๋ฅ์ ๊ฐ์ ํ ์ ์๋ค.
Find ์ฟผ๋ฆฌ์์ ์ธ๋ฑ์ค ์ฌ์ฉ
์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํ๋ฉด Find
์ฟผ๋ฆฌ์ ์คํ ์๊ฐ์ ์ค์ผ ์ ์๋ค. ์๋ฅผ ๋ค์ด, "name" ํ๋์ ์ธ๋ฑ์ค๊ฐ ์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ ์ ์๋ค.
filter := bson.M{"name": "John Doe"}
cursor, err := collection.Find(ctx, filter)
if err != nil {
log.Fatal(err)
}
Aggregate ์ฟผ๋ฆฌ์์ ์ธ๋ฑ์ค ์ฌ์ฉ
Aggregate
์ฟผ๋ฆฌ์์๋ ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ธ๋ฑ์ค๋ ํนํ $match
์ $sort
๋จ๊ณ์์ ์ ์ฉํ๋ค.
์๋ฅผ ๋ค์ด, "age" ํ๋์ ์ธ๋ฑ์ค๊ฐ ์๊ณ , ๋์ด๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ๊ฒฝ์ฐ ์ธ๋ฑ์ค๊ฐ ์ฟผ๋ฆฌ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ค.
pipeline := mongo.Pipeline{
{{"$match", bson.D{{"age", bson.D{{"$gte", 30}}}}}},
{{"$sort", bson.D{{"age", 1}}}},
}
cursor, err := collection.Aggregate(ctx, pipeline)
if err != nil {
log.Fatal(err)
}
MongoDB Query Plan
MongoDB์ ์ฟผ๋ฆฌ ํ๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ด๋ป๊ฒ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ์ง ๊ฒฐ์ ํ๋ ์ค์ํ ๋ถ๋ถ์ด๋ค.
์ด ํ๋์ ์ต์ ์ ์ฟผ๋ฆฌ ์ฑ๋ฅ์ ๋ณด์ฅํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ฐ๋ฅํ ๊ฒฝ๋ก ์ค์์ ์ต์ ์ ์ ํ์ ํ๋๋ก ์ค๊ณ๋์๋ค.
์ฟผ๋ฆฌ ํ๋์ ์ดํดํ๋ ๊ฒ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฑ๋ฅ ์ต์ ํ์ ์ค์ํ๋ฉฐ, MongoDB๋ ์ด๋ฅผ ์ํด ์ฌ๋ฌ ๋ด๋ถ ๋ฉ์ปค๋์ฆ๊ณผ ์ต์ ํ ๋๊ตฌ๋ฅผ ์ ๊ณตํ๋ค.
์ฟผ๋ฆฌ ํ๋๋์ ์๋ ๋ฐฉ์
MongoDB๋ ์ฟผ๋ฆฌ๋ฅผ ๋ฐ์ผ๋ฉด ๋จผ์ ์ฟผ๋ฆฌ ํ๋๋๋ฅผ ํตํด ์คํ ๊ณํ์ ๋ง๋ ๋ค. ์ฟผ๋ฆฌ ํ๋๋๋ ๋ค์๊ณผ ๊ฐ์ ๋จ๊ณ๋ก ์๋ํ๋ค:
์ฟผ๋ฆฌ ๋ถ์: MongoDB๋ ์ฃผ์ด์ง ์ฟผ๋ฆฌ๋ฅผ ๋ถ์ํ์ฌ ์ฌ์ฉ ๊ฐ๋ฅํ ์ธ๋ฑ์ค๋ฅผ ํ๊ฐํ๋ค. ์ด๋ ์ฟผ๋ฆฌ์ ์ฌ์ฉ๋ ์กฐ๊ฑด๊ณผ ํ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ธ๋ฑ์ค์ ์ ์ฉ์ฑ์ ํ๋จํ๋ค.
ํ๋ ํ๋ณด ์์ฑ: ์ฌ๋ฌ ์ธ๋ฑ์ค ์ค์์ ์ ํฉํ ํ๋ณด๋ฅผ ์ ์ ํ์ฌ ๊ฐ๋ฅํ ์คํ ๊ณํ์ ๋ง๋ ๋ค. ์ด ๊ณํ์ ์ธ๋ฑ์ค ์ค์บ, ์ปฌ๋ ์ ์ค์บ ๋ฑ ๋ค์ํ ์ ๊ทผ ๋ฐฉ์์ ํฌํจํ ์ ์๋ค.
ํ๋ ํ๊ฐ: ์์ฑ๋ ํ๋ณด๋ค ์ค์์ ์ต์ ์ ์ฑ๋ฅ์ ๋ณด์ผ ๊ฒ์ผ๋ก ์์๋๋ ํ๋์ ์ ํํ๊ธฐ ์ํด, MongoDB๋ ๊ฐ ํ๋์ ์ผ์ ์๊ฐ ๋์ ์คํํด ๋ณธ๋ค(์ด๋ฅผ ํ๋ ์บ์ฑ์ด๋ผ ํจ). ์ด ๊ณผ์ ์์ ์ค์ ๋ฐ์ดํฐ์ ๋ํ ์ฟผ๋ฆฌ ์คํ ์๊ฐ๊ณผ ์์ ์ฌ์ฉ๋์ ํ๊ฐํ๋ค.
์ต์ข ํ๋ ์ ํ: ๊ฐ์ฅ ํจ์จ์ ์ธ ํ๋์ด ์ ํ๋์ด ์ฟผ๋ฆฌ๊ฐ ๊ทธ์ ๋ฐ๋ผ ์คํ๋๋ค. ํ ๋ฒ ์ ํ๋ ํ๋์ ์ผ์ ๊ธฐ๊ฐ ๋์ ์บ์๋์ด, ๋์ผํ๊ฑฐ๋ ์ ์ฌํ ์ฟผ๋ฆฌ๊ฐ ๋ค์ ์คํ๋ ๋ ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
์ฟผ๋ฆฌ ํ๋์ ์คํ ๋ฐ ์ต์ ํ
์ฟผ๋ฆฌ ์คํ ๊ณํ ํ์ธ
MongoDB์์๋ explain()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ์ฟผ๋ฆฌ์ ์คํ ๊ณํ์ ๋ณผ ์ ์๋ค.
์ด ๋ฉ์๋๋ ์ฟผ๋ฆฌ๊ฐ ์ด๋ป๊ฒ ์คํ๋ ์ง, ์ด๋ค ์ธ๋ฑ์ค๊ฐ ์ฌ์ฉ๋๋์ง ๋ฑ์ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค.
db.collection.find({age: {$gt: 30}}).explain("executionStats")
์ด ๋ช ๋ น์ ์ฟผ๋ฆฌ๊ฐ ์คํ๋๋ ๋ฐฉ์๊ณผ ๊ด๋ จ๋ ํต๊ณ์ ํจ๊ป ์คํ ๊ณํ์ ์ธ๋ถ ์ ๋ณด๋ฅผ ๋ฐํํ๋ค.
์ด ์ ๋ณด๋ฅผ ํตํด ๊ฐ๋ฐ์๋ ์ฟผ๋ฆฌ ์ฑ๋ฅ์ ๋ณ๋ชฉ ํ์์ ์๋ณํ๊ณ ํ์ํ ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ฟผ๋ฆฌ ์์ฒด๋ฅผ ์์ ํ ์ ์๋ค.
์ฟผ๋ฆฌ ์ต์ ํ
์ฟผ๋ฆฌ ์ฑ๋ฅ์ ์ต์ ํํ๊ธฐ ์ํด ์ธ๋ฑ์ค๋ฅผ ์กฐ์ ํ๊ฑฐ๋ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ ์ธ์๋, MongoDB๋ ์ฟผ๋ฆฌ ํจํด์ ํ์ตํ์ฌ ๋ฐ๋ณต์ ์ธ ์ฟผ๋ฆฌ์ ๋ํด ๋ ๋น ๋ฅด๊ฒ ์๋ตํ ์ ์๋๋ก ํ๋์ ์กฐ์ ํ๋ค.
์ด๋ฌํ ๋์ ์ต์ ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ฐ์ ์ธ ํจ์จ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์๋ค.
Options
MongoDB์ Golang ๋๋ผ์ด๋ฒ์์ options
ํจํค์ง๋ ๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ค์ ์ ๊ฐ๋ฅํ๊ฒ ํด์ค๋ค.
์ด ์ค Projection
์ ํตํด ๊ฒ์ ๊ฒฐ๊ณผ์์ ํน์ ํ๋๋ง ์ถ์ถํ๋ ๋ฐฉ๋ฒ์ ์ค์ ์ ์ผ๋ก ๋ค๋ฃฌ๋ค.
Projection
Projection
์ ๊ฒ์๋ ๋ฌธ์์์ ํน์ ํ๋๋ง ๋ฐํํ๋๋ก ์ค์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ๋คํธ์ํฌ ์ ์ก ๋ฐ์ดํฐ๋์ ์ค์ด๊ณ , ์ฒ๋ฆฌ ์๋๋ฅผ ํฅ์์ํฌ ์ ์๋ค.
๋ค์ ์์ ์์๋ name
ํ๋๊ฐ "primrose"์ธ ๋ฌธ์์์ email
ํ๋๋ง ์กฐํํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋ค.
ctx := context.TODO()
collection := client.Database("your_database").Collection("your_collection")
// Find๋ฅผ ์ฌ์ฉํ Projection
opts := options.Find().SetProjection(bson.M{"email": 1, "_id": 0})
cursor, err := collection.Find(ctx, bson.M{"name": "primrose"}, opts)
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
// FindOne์ ์ฌ์ฉํ Projection
optsOne := options.FindOne().SetProjection(bson.M{"email": 1, "_id": 0})
result := collection.FindOne(ctx, bson.M{"name": "primrose"}, optsOne)
if err := result.Err(); err != nil {
log.Fatal(err)
}
var user bson.M
if err := result.Decode(&user); err != nil {
log.Fatal(err)
}
Registry
Registry
๋ BSON ํ์
๊ณผ Go ํ์
๊ฐ์ ๋ณํ์ ์ปค์คํฐ๋ง์ด์ฆํ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค.
ํนํ, primitive.Decimal128
ํ์
์ Go์ BigInt
๋ก ๋ณํํ๋ ์ปค์คํ
์ฝ๋ฑ์ ๋ฑ๋กํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณธ๋ค.
bsoncodec
bsoncodec
๋ฅผ ์ฌ์ฉํ์ฌ MongoDB์ Decimal128
๋ฐ์ดํฐ ํ์
์ Go์ big.Int
๋ก ๋ณํํ ์ ์๋ค.
๋ค์์ Decimal128
ํ์
์ BigInt
๋ก ๋ณํํ๋ ์ปค์คํ
์ฝ๋ฑ์ ๊ตฌํํ๊ณ , ์ด๋ฅผ Registry
์ ๋ฑ๋กํ๋ ๊ณผ์ ์ ๋ณด์ฌ์ค๋ค.
์์๊ฐ BigInt ์ผ ๋ฟ์ด๊ณ , ๋ค๋ฅธ ํํ๋ ๊ฐ๋ฅํ๋ค. ๊ฐ์ ์์ฉํด์ ์ฌ์ฉํ๋ค๋ฉด ์ข์ document ๋ฅผ ๋์์ธ ํ ์ ์๋ค.
type BigIntCodec struct{}
func (c *BigIntCodec) DecodeValue(decodeContext bsoncodec.DecodeContext, reader bsonrw.ValueReader, value reflect.Value) error {
decimal128, err := reader.ReadDecimal128()
if err != nil {
return err
}
bi, ok := new(big.Int).SetString(decimal128.String(), 10)
if !ok {
return fmt.Errorf("could not convert Decimal128 to BigInt")
}
value.Set(reflect.ValueOf(bi))
return nil
}
func (c *BigIntCodec) EncodeValue(encodeContext bsoncodec.EncodeContext, writer bsonrw.ValueWriter, value reflect.Value) error {
if !value.IsValid() || value.Type() != reflect.TypeOf((*big.Int)(nil)) {
return bsoncodec.ValueEncoderError{Name: "BigIntCodec.EncodeValue", Types: []reflect.Type{reflect.TypeOf((*big.Int)(nil))}, Received: value}
}
decimal128, err := primitive.ParseDecimal128(value.Interface().(*big.Int).String())
if err != nil {
return err
}
return writer.WriteDecimal128(decimal128)
}
๋จผ์ ์ BigInt Codec ์ ๊ตฌํํ๋ค.
๋จ์ํ Decimal128 ์ ์ฝ์ด์ BigInt ๋ก ๋ณ๊ฒฝํ๋๋ก ํ๋ค.
bigIntCodec := &BigIntCodec{}
opts := bson.NewRegistry()
opts.RegisterTypeEncoder(reflect.TypeOf((*big.Int)(nil)), bigIntCodec)
opts.RegisterTypeDecoder(reflect.TypeOf((*big.Int)(nil)), bigIntCodec)
collection := client.Database("test").Collection("test", options.Collection().
SetRegistry(opts),
)
์ปฌ๋ ์ ์ ์ฐ๊ฒฐํ ๋ ์์๊ฐ์ด ์ต์ ์ ์ฃผ๋ฉด ๋๋ค.
type BigIntValue struct {
Value *big.Int
}
if _, err = collection.InsertOne(ctx, BigIntValue{Value: big.NewInt(1234567890123456789)}); err != nil {
panic(err)
}
var result BigIntValue
if err = collection.FindOne(ctx, bson.M{"value": big.NewInt(1234567890123456789)}).Decode(&result); err != nil {
panic(err)
}
fmt.Println(result)
// {1234567890123456789}
Last updated