# Go and MongoDB
介紹對 MongoDB 的操作方法。
## models/db.go
### Initial
```go=
func init() {
dialInfo := &mgo.DialInfo{
Addrs: []string{host},
Source: source,
Username: user,
Password: pass,
}
s, err := mgo.DialWithInfo(dialInfo)
if err != nil {
log.Fatalln("create session error", err)
}
globalS = s
}
```
- dialInfo
- 儲存各種資訊
- mgo.DialWithInfo
- 建立連線
- return value
- mgo.Session
- error
### Connect
```go=
func connect(db, collection string) (*mgo.Session, *mgo.Collection) {
s := globalS.Copy()
c := s.DB(db).C(collection)
return s, c
}
```
- mgo.Session.Copy()
- mgo.Session 正確用法為,每次使用皆需從初始 Session copy 出來使用
- 並且在用完(函式結束時)要關閉 mgo.Session
- call connect 的函式用完後會關閉 Session: defer ms.close()
- [reference](https://cardinfolink.github.io/2017/05/17/mgo-session/)
- return value
- mgo.Session
- 與 mongoDB 聯繫
- mgo.Collection
- 來自 mgo.Session.DB(資料庫名稱).C(collection名稱)
- s.DB(db).C(collection)
- s a mgo.Session
- .DB() return a database
- .C() return a collection
### Insert
```go=
func Insert(db, collection string, docs ...interface{}) error {
ms, c := connect(db, collection)
defer ms.Close()
return c.Insert(docs...)
}
```
- 可變參數個數函式
- 名為 docs 的 slice, 型別為 interface{}
- 以 docs... 作為參數丟入 mgo.collection.Insert
### others
```go=
func FindOne(db, collection string, query, selector, result interface{}) error {
ms, c := connect(db, collection)
defer ms.Close()
return c.Find(query).Select(selector).One(result)
}
func FindAll(db, collection string, query, selector, result interface{}) error {
ms, c := connect(db, collection)
defer ms.Close()
return c.Find(query).Select(selector).All(result)
}
func Update(db, collection string, query, update interface{}) error {
ms, c := connect(db, collection)
defer ms.Close()
return c.Update(query, update)
}
func Remove(db, collection string, query interface{}) error {
ms, c := connect(db, collection)
defer ms.Close()
return c.Remove(query)
}
```
- c.Find(query)
- query 字串,作為搜尋條件
- query 可以是
- map
- bson.M 整理過的 struct value
- .Select(selector)
- 指定欄位某個值的查詢(?)
- .All(result)
- 回傳所有結果
- 傳遞記憶體位置,而不是值(是指 result 嗎?)
- .One(result)
- 回傳一個結果
- 傳遞記憶體位置,而不是值
- .Update(query, update)
- 更新(替換) query 到的結果
- .Remove(query)
- 刪除 query 到的結果
## models/movies.go
```go=
type Movies struct {
Id bson.ObjectId `bson:"_id" json:"id"`
Name string `bson:"name" json:"name"`
CoverImage string `bson:"cover_image" json:"cover_image"`
Description string `bson:"description" json:"description"`
}
const (
db = "Movies"
collection = "MovieModel"
)
```
- type `Movies`
- bson 基於 json 修改的數據格式,用於 MongoDB
```go=
func (m *Movies) InsertMovie(movie Movies) error {
return Insert(db, collection, movie)
}
func (m *Movies) FindAllMovies() ([]Movies, error) {
var result []Movies
err := FindAll(db, collection, nil, nil, &result)
return result, err
}
func (m *Movies) FindMovieById(id string) (Movies, error) {
var result Movies
err := FindOne(db, collection, bson.M{"_id": bson.ObjectIdHex(id)}, nil, &result)
return result, err
}
func (m *Movies) UpdateMovie(movie Movies) error {
return Update(db, collection, bson.M{"_id": movie.Id}, movie)
}
func (m *Movies) RemoveMovie(id string) error {
return Remove(db, collection, bson.M{"_id": bson.ObjectIdHex(id)})
}
```
- func (m \*`Movies`)
- 表示此函式是宣告在 `Movies` 這個 type 中
- bson.M
- map 型別的縮寫
- bson.ObjectIdHex
- 回傳 16進制表示的 obj id
## controllers/movies.go
一些實做邏輯跟上面函式的用法
```go=
func responseWithJson(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
func CreateMovie(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var movie models.Movies
if err := json.NewDecoder(r.Body).Decode(&movie); err != nil {
responseWithJson(w, http.StatusBadRequest, "Invalid request payload")
return
}
movie.Id = bson.NewObjectId()
if err := dao.InsertMovie(movie); err != nil {
responseWithJson(w, http.StatusInternalServerError, err.Error())
return
}
responseWithJson(w, http.StatusCreated, movie)
}
func AllMovies(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var movies []models.Movies
movies, err := dao.FindAllMovies()
if err != nil {
responseWithJson(w, http.StatusInternalServerError, err.Error())
return
}
responseWithJson(w, http.StatusOK, movies)
}
func FindMovie(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
result, err := dao.FindMovieById(id)
if err != nil {
responseWithJson(w, http.StatusInternalServerError, err.Error())
return
}
responseWithJson(w, http.StatusOK, result)
}
func UpdateMovie(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var params models.Movies
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
responseWithJson(w, http.StatusBadRequest, "Invalid request payload")
return
}
if err := dao.UpdateMovie(params); err != nil {
responseWithJson(w, http.StatusInternalServerError, err.Error())
return
}
responseWithJson(w, http.StatusOK, map[string]string{"result": "success"})
}
func DeleteMovie(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
if err := dao.RemoveMovie(id); err != nil {
responseWithJson(w, http.StatusBadRequest, "Invalid request payload")
return
}
responseWithJson(w, http.StatusOK, map[string]string{"result": "success"})
}
```
- mux.Vars(r)
- 拿出 URL 送來的參數
## Reference
[[前端攻城詩] Let`s Golang ! — 6. Golang 開 API (上)](https://medium.com/@intheblackworld/%E5%89%8D%E7%AB%AF%E6%94%BB%E5%9F%8E%E8%A9%A9-let-s-golang-6-golang-%E9%96%8B-api-%E4%B8%8A-5e82cde62853)
完結