# 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(&params); 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) 完結