Skip to content

MongoDB

MongoDB 는 문서 데이터베이스로, 기본 데이터 단위는 문서이며 저장 형식은 BSON(Binary JSON) 으로 JSON 과 유사한 구조입니다. 느슨한 구조로 다양한 유형의 데이터를 저장할 수 있어 관계형 데이터베이스보다 더 유연하며, 스크립트 언어로 js 를 사용하여 스크립트를 통해 조합 작업을 수행할 수 있습니다. 이 문서에서는 Go 에서 공식 mongo 드라이버를 사용하여 mongodb 데이터베이스를 조작하는 방법을 주로 소개하며, mongodb 튜토리얼은 아닙니다. mongo 기초가 없다면 먼저 직접 이해하고 학습하시기 바랍니다.

mongodb 문서: Introduction to MongoDB — MongoDB Manual

드라이버

mongodb 의 go 관련 라이브러리는 비교적 적으며, 초기에 커뮤니티에서 유지보수하던 라이브러리들도 나중에 유지보수가 중단되었지만, 공식 mongo 드라이버 라이브러리는 이미 충분히 사용할 만합니다.

오픈소스 저장소: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)

문서 주소: mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)

설치

의존성을 다운로드하려면 아래 주소를 사용하면 됩니다.

sh
$ go get go.mongodb.org/mongo-driver/mongo

연결

아래는 mongo 클라이언트와 서버 간 연결을 설정하는 간단한 예제입니다.

go
package main

import (
   "context"
   "fmt"
   "go.mongodb.org/mongo-driver/mongo"
   "go.mongodb.org/mongo-driver/mongo/options"
   "go.mongodb.org/mongo-driver/mongo/readpref"
   "log"
)

func main() {
   ctx := context.Background()
   // URI 를 사용하여 연결 설정
   client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://admin:123456@192.168.48.138:27017/"))
   if err != nil {
      log.Panicln(err)
   }
   // 연결 닫기
   defer client.Disconnect(ctx)
   // ping 테스트로 연결 여부 확인
   fmt.Println(client.Ping(ctx, readpref.Primary()))
}

bson

mongodb 는 go 에서 다음 몇 가지 유형을 사용하여 데이터베이스의 문서를 매핑하며, bson/bson.go에 위치합니다.

go
// BSON 문서의 순서 있는 표현
type D = primitive.D

// 한 쌍의 키 - 값, BSON 문서의 순서 있는 표현의 기본 단위
type E = primitive.E

// BSON 문서의 순서 없는 표현
type M = primitive.M

// BSON 데이터의 순서 있는 표현
type A = primitive.A

실제 유형은 다음과 같습니다.

go
// BSON 문서의 순서 있는 표현
type D []E

// 한 쌍의 키 - 값, BSON 문서의 순서 있는 표현의 기본 단위
type E struct {
  Key   string
  Value interface{}
}

// BSON 문서의 순서 없는 표현
type M map[string]interface{}

// BSON 데이터의 순서 있는 표현
type A []interface{}

이러한 유형을 통해 조회 SQL 을 구성할 수 있으며 데이터를 매핑하는 데에도 사용할 수 있습니다.

TIP

드라이버 examples 디렉토리에는 상당히 많은 사용 예제가 있으며, 공식에서 위 네 가지 유형을 사용하는 방법을 매우 상세히 시연하고 있습니다.

주소: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

문서 조회

공식 조회 예제: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

먼저 user 데이터베이스를 생성하고 users 컬렉션에 다음 데이터를 삽입합니다.

sql
> use user
> db.users.insertMany([
    {
        name: "mike",
        age: 12,

    },
    {
        name: "jenny",
        age: 14,

    },
    {
        name: "jack",
        age: 18,
        address: "usa"
    }
])

단일 조회

go
type User struct {
    Name    string `bson:"name"`
    Age     int    `bson:"age"`
    Address string `bson:"address"`
}

var user User

result := client.Database("user"). // 데이터베이스 선택
                    Collection("users").                     // 컬렉션 선택
                    FindOne(ctx, bson.D{{"address", "usa"}}) // 필터 조건

// 역직렬화
if err := result.Decode(&user); err != nil {
    log.Panicln(err)
}

fmt.Printf("%+v\n", user)

위 조회 코드는 다음과 동일합니다.

sql
db.users.findOne({
    address: "usa"
})

출력 결과

{Name:jack Age:18 Address:usa}

복수 조회

go
type User struct {
   Name    string `bson:"name"`
   Age     int    `bson:"age"`
   Address string `bson:"address"`
}

var users []User

cursor, err := client.Database("user"). // 데이터베이스 선택
               Collection("users"). // 컬렉션 선택
               Find(ctx, bson.D{})  // 필터 조건

if err != nil {
   log.Panicln(err)
}

if err := cursor.All(ctx, &users); err != nil {
   log.Panicln(err)
}

fmt.Printf("%+v\n", users)

다음과 동일합니다.

sql
db.users.find({})

출력

[{Name:jack Age:18 Address:usa} {Name:mike Age:12 Address:} {Name:jenny Age:14 Address:}]

조회 조건을 구성할 때 options 를 사용할 수도 있습니다.

go
type User struct {
    Name    string `bson:"name"`
    Age     int    `bson:"age"`
    Address string `bson:"address"`
}

var users []User

find := options.Find()
find.SetSort(bson.M{"age": 1})
find.SetLimit(1)

cursor, err := client.Database("user"). // 데이터베이스 선택
                    Collection("users").      // 컬렉션 선택
                    Find(ctx, bson.D{}, find) // 필터 조건

if err != nil {
    log.Panicln(err)
}

if err := cursor.All(ctx, &users); err != nil {
    log.Panicln(err)
}

fmt.Printf("%+v\n", users)

다음과 동일합니다.

db.users.find({}).sort({age:1}).limit(1)

출력

[{Name:mike Age:12 Address:}]

문서 생성

공식 생성 예제: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

아래는 문서를 생성하는 예제입니다.

go
one, err := client.Database("user").Collection("users").InsertOne(ctx, User{
    Name:    "lili",
    Age:     20,
    Address: "china",
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(one.InsertedID)

생성 성공 후 문서의 ObjectID 가 반환됩니다.

ObjectID("64c60fa01e2548d9e4de6cf4")

아래는 여러 문서를 생성하는 예제입니다.

go
users := []any{User{
    Name:    "john",
    Age:     10,
    Address: "usa",
}, User{
    Name:    "pop",
    Age:     30,
    Address: "uk",
}}

one, err := client.Database("user").Collection("users").InsertMany(ctx, users)
if err != nil {
    log.Panicln(err)
}
fmt.Println(one.InsertedIDs)

생성 성공 후 한 세트의 ObjectID 가 반환됩니다.

[ObjectID("64c610d5aec2618d6ca0b515") ObjectID("64c610d5aec2618d6ca0b516")]

위 두 코드 세그먼트는 각각 db.users.insertOnedb.users.insertMany와 동일합니다.

문서 업데이트

공식 업데이트 예제: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

아래는 단일 문서를 업데이트하는 예제로, 이름이 lili 인 사람을 mark 로 변경합니다.

go
upres, err := client.Database("user").Collection("users").UpdateOne(ctx, bson.D{
    {"name", "mark"},
},
    bson.D{
       {"$set", bson.D{
          {"name", "lili"},
       }},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

다음과 동일합니다.

sql
db.users.updateOne({
    name: "lili"
}, {
    $set: {
        name: "mark"
    },
})

출력

&{MatchedCount:1 ModifiedCount:1 UpsertedCount:0 UpsertedID:<nil>}

아래는 여러 문서를 업데이트하는 예제로, 나이가 10 인 사람의 주소를 cn 으로 업데이트합니다.

go
upres, err := client.Database("user").Collection("users").UpdateMany(ctx, bson.D{
    {"age", 10},
},
    bson.D{
        {"$set", bson.D{
            {"address", "cn"},
        }},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

Update를 사용하는 것 외에도 mongo 는 Replace를 제공합니다. 둘의 차이는 전자는 문서 필드를 업데이트하고 후자는 문서를 직접 교체한다는 것입니다. 예를 들어 아래 코드에서는 더 이상 연산자가 필요하지 않습니다.

go
upres, err := client.Database("user").Collection("users").ReplaceOne(ctx, bson.D{
    {"age", 10},
},
    bson.D{
       {"address", "cn"},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

동시에 mongo 는 FindOneAndUpdateFindOneAndReplace를 제공하여 문서를 가져오고 업데이트합니다. 다음과 같습니다.

go
res := client.Database("user").Collection("users").FindOneAndReplace(ctx, bson.D{
    {"address", "cn"},
},
    bson.D{
       {"address", "uk"},
    })
if err := res.Err(); err != nil {
    log.Panicln(err)
}

var user User

res.Decode(&user)

fmt.Printf("%+v", user)

출력

Name: Age:0 Address:cn}

이 작업은 문서를 조회한 후 수정합니다.

문서 삭제

공식 삭제 예제: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

아래는 하나의 문서를 삭제하는 예제입니다.

go
result, err := client.Database("user").Collection("users").DeleteOne(ctx, bson.D{
    {"name", "jack"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

아래는 여러 문서를 삭제하는 예제입니다.

go
result, err := client.Database("user").Collection("users").DeleteMany(ctx, bson.D{
    {"age", "10"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

집계

공식 집계 예제: mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

집계 작업에는 mongo.Pipeline 유형이 사용되며, 본질은 []bson.D입니다.

go
type Pipeline []bson.D
go
pipline := mongo.Pipeline{
    {
       {"$match", bson.D{
          {"address", "uk"},
       }},
    },
    {
       {"$sort", bson.D{
          {"age", 1},
       }},
    },
}
aggregate, err := client.Database("user").Collection("users").Aggregate(ctx, pipline)
if err != nil {
    log.Panicln(err)
}
var users []User
if err := aggregate.All(ctx, &users); err != nil {
    log.Panicln(err)
}
log.Println(users)

출력

[{lili 20 uk} {kak 30 uk}]

이 집계 작업은 address 가 uk 인 모든 사용자를 매칭한 후 나이순으로 정렬합니다.

Golang by www.golangdev.cn edit