Go With MongoDB 2
11276 ワード
MongoDBはより柔軟な構造を持っている.したがって、データモデルは、同じオブジェクトを取得することを異なる方法で定義することができる.アプリケーションのコンテキストに基づいて正しいモードを選択してデータモデルを定義することができます.リンクデータの関係を確立するためには、主なdocumentにdocumentを埋め込むか、2つのdocument間で参照することができる.この両者の応用状況は実際の状況に応じて応用する.
以下の例は、リンクデータの関係を記述するために埋め込まれたdocumentを用いるデータモデルを示す.CategoryとTaskデータの連絡Categoryには複数のTaskエンティティがある.
// mongodb
package main
import (
"log"
"time"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type Task struct {
Description string
Due time.Time
}
type Category struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string
Description string
Tasks []Task
}
func main() {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
//
c := session.DB("taskdb").C("categories")
//
doc := Category{
bson.NewObjectId(),
"Open-Source",
"Task for open-source prijects",
[]Task{
Task{"Create project in mgo", time.Date(2016, time.May, 10, 0, 0, 0, 0, time.UTC)},
Task{"Create REST API", time.Date(2016, time.May, 20, 0, 0, 0, 0, time.UTC)},
},
}
err = c.Insert(&doc)
if err != nil {
log.Fatal(err)
}
}
Task要素タイプの配列を含むCategory構造体が作成された.ドキュメントの埋め込みは、親ドキュメントと関連するサブドキュメントを取得することができ、クエリーを1回だけ行う必要があります.
CollectionのFindメソッドでは、MongoDBのcollectionsをクエリーできます.このメソッドを呼び出すと、collectionデータをフィルタするドキュメントを提供することができる.Findメソッドはdocumentを使用してクエリを行う.documentクエリーcollectionを提供するには、map、struct値などのBSONデータにシーケンス化可能なオブジェクトを提供する必要がある.
iter := c.Find(nil).Iter()
result := Category{}
for iter.Next(&result) {
fmt.Printf("Category:%s,decription:%Ss
", result.Name, result.Description)
tasks := result.Tasks
for _, v := range tasks {
fmt.Printf("Task:%s Due:%s
", v.Description, v.Due)
}
}
if err = iter.Close(); err != nil {
log.Fatal(err)
}
Iterメソッドはdocumentsを列挙するために使用される.Iterはクエリーを実行し、列挙可能なすべての値を得る.documentに親子関係が埋め込まれている場合、クエリー文でアクセスできます.
//sort
iter := c.Find(nil).Sort("name").Iter()
for iter.Next(&result) {
fmt.Printf("Category:%s,decription:%Ss
", result.Name, result.Description)
tasks := result.Tasks
for _, v := range tasks {
fmt.Printf("Task:%s Due:%s
", v.Description, v.Due)
}
}
if err = iter.Close(); err != nil {
log.Fatal(err)
}
フィールドに基づいて逆ソートする場合は、フィールド名に「-」を付けるだけです.
iter := c.Find(nil).Sort("-name").Iter()
result := Category{}
err := c.Find(bson.M{"name":"Open-Source"}).One(result)
if err != nil{
log.Fatal(err)
}
fmt.Printf("Category:%s,Description:%s
",result.name,result.Description)
task := result.Tasks
for _,v := range tasks{
fmt.Printf("Task:%s Due:%v
",v.Description,v.Due)
}
bson.M(M->map)タイプは、データの問合せに用いる.ここで、nameフィールドを使用してcollectionをクエリーします.Oneメソッドはクエリを実行するresultに解析する.もう1つのFindIdメソッドは、単一のデータのクエリーをより便利にする.idクエリcollectionで対応するdocumentを直接使用
query := c.Find(bson.M{"_id":id})
result := Category{}
err = c.FindId(obj_id).One(&result)
func (c *Collection) Update(selectorinterface{},updateinterface{}) error
Updateメソッドはcollectionからdocumentを検索、提供するセレクタで検索し、提供するdocumentで更新する.一部の更新は「%set」キーを使用してdocumentを更新することができる.
//update a document
err := c.Update(bson.M{"_id": id},
bson.M{"$set":bson.M{
"description":"Create open-source projects",
"tasks":[]Task{
Task{"Evaluate Negroni Project", time.Date(2015, time.August, 15, 0, 0, 0,
0, time.UTC)},
Task{"Explore mgo Project", time.Date(2015, time.August, 10, 0, 0, 0, 0,
time.UTC)},
Task{"Explore Gorilla Toolkit", time.Date(2015, time.August, 10, 0, 0, 0, 0,
time.UTC)},
},
}}
)
部分更新:descriptionとtasks.Updateメソッドは、提供するidに基づいて検索を行い、その後、対応するフィールドを変更し、提供するdocumentの対応する値を書き込む.
Removeメソッドはcollectionからdocumentを削除することができる.RemoveAllメソッドはすべてのdocumentを削除し、パラメータがnilの場合はc.RemoveAll(nil)をすべて削除します.
func (c *Collection) Remove(selector interface{}) error //err := c.Remove(bson.M{"_id": id})
func (c *Collection) RemoveAll(selector interface{}) (info *ChangeInfo, err error)
MongoDBの下付き索引
MongoDBデータベースとリレーショナルデータベースは、効率的な読み取り操作を行うため、MongoDBデータベースをよりよく操作するために、collectionにインデックスを追加することで効率を提供することもできる.collectionのインデックスは、効率的なクエリー操作を行うことができる.MongoDBは、collectionレベルでインデックスを定義するもよいし、collection張documentまたは任意のフィールドでインデックスを定義してもよい.
すべてのcollectionはデフォルトで1つあります.idフィールドのため.このフィールドを定義しないと、MongoDBプロセスは自動的に_を作成します.idフィールド、値タイプはObjectId.idインデックスは一意です.
フィルタ動作を頻繁に使用してcollectionsをクエリーする場合は、インデックスを使用してより良い操作を検討する必要があります.mgoデータベース駆動はEnsureIndex方法を提供し、インデックスを作成し、パラメータはmgoである.Indexタイプ
例:
// mongodb
package main
import (
"fmt"
"log"
"time"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type Task struct {
Description string
Due time.Time
}
type Category struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string
Description string
//Tasks []Task
}
func main() {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
//
c := session.DB("taskdb").C("categories")
c.RemoveAll(nil)
//index
index := mgo.Index{
Key: []string{"name"},
Unique: true,
DropDups: true,
Background: true,
Sparse: true,
}
//create Index
err = c.EnsureIndex(index)
if err != nil {
panic(err)
}
//
err = c.Insert(&Category{bson.NewObjectId(), "R & D", "R & D Tasks"},
&Category{bson.NewObjectId(), "Project", "Project Tasks"},
&Category{bson.NewObjectId(), "Open Source", "Tasks for open-source projects"})
if err != nil {
panic(err)
}
result := Category{}
err = c.Find(bson.M{"name": "Open-Source"}).One(&result)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("Description:", result.Description)
}
mgoを作成しました.Index,メソッドEnsureIndexメソッドが呼び出された.IndexタイプのKey属性は、1つのスライスをフィールド名として使用することができる.ここでnameフィールドはindexとして使用する.フィールドはスライスであるため、インスタンスIndexに複数のフィールドを提供することができる.Uniqueプロパティは、1つのdocumentに1つのindexがあることを確認します.デフォルトのindexは昇順です.降順が必要な場合は、フィールドの前に「-」を付けることができます.
key : []string{"-name"}
推奨されるsessionオブジェクトの管理プロセス:1.Dialメソッドを使用してセッションオブジェクトを取得する.2.独立したHTTPリクエストのライフサイクルにおいて、New、CopyまたはCloneメソッドを使用してセッションを作成すると、Dialメソッドによって作成されたセッションが取得する.これにより接続プール内のセッションオブジェクトを正しく使用することができる.3.HTTPリクエストのライフサイクルにおいて、取得したセッションオブジェクトを用いてCRUD操作を行う.
Newメソッドは、同じパラメータを持つ新しいSessionオブジェクトを作成します.CopyメソッドはNewメソッドと同様に動作するが、copyはセッションの元の情報を保持する.CloneメソッドはCopyと同じですが、元のSesssionのsocketから
次の例は、HTTPサーバがcopyを使用するセッションオブジェクトである.1つのstructタイプはSessionオブジェクトを持っていて、要求されたHandlerの中で非常に簡単にデータベースの操作を管理します.
// mongodb
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
var session *mgo.Session
type Category struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string
Description string
//Tasks []Task
}
type DataStore struct {
session *mgo.Session
}
/*
type Task struct {
Description string
Due time.Time
}
*/
// collection
func (d *DataStore) C(name string) *mgo.Collection {
return d.session.DB("taskdb").C(name)
}
// HTTP DataStore
func NewDataStore() *DataStore {
ds := &DataStore{
session: session.Copy(),
}
return ds
}
func (d *DataStore) Close() {
d.session.Close()
}
//
func PostCategory(w http.ResponseWriter, r *http.Request) {
var category Category
err := json.NewDecoder(r.Body).Decode(&category)
if err != nil {
panic(err)
}
ds := NewDataStore()
defer ds.Close()
c := ds.C("categories")
err = c.Insert(&category)
if err != nil {
panic(err)
}
w.WriteHeader(http.StatusCreated)
}
func GetCategories(w http.ResponseWriter, r *http.Request) {
var categories []Category
ds := NewDataStore()
defer ds.Close()
c := ds.C("categories")
iter := c.Find(nil).Iter()
result := Category{}
for iter.Next(&result) {
categories = append(categories, result)
}
w.Header().Set("Content-Type", "application/json")
j, err := json.Marshal(categories)
if err != nil {
panic(err)
}
w.WriteHeader(http.StatusOK)
w.Write(j)
}
func main() {
var err error
session, err = mgo.Dial("localhost")
if err != nil {
panic(err)
}
r := mux.NewRouter()
r.HandleFunc("/api/categories", GetCategories).Methods("GET")
r.HandleFunc("/api/categories", PostCategory).Methods("POST")
server := &http.Server{
Addr: ":9090",
Handler: r,
}
log.Println("Listening...")
server.ListenAndServe()
}
DataStoreの構造体を定義し、mgoを管理する.Session、更に2つの方法があります:CloseとC、Closeの方法は主にSessionオブジェクトがCloseの方法を呼び出して、資源の解放を行います.defer関数は、HTTPリクエストのライフサイクル終了時に呼び出す.
NewDataStoreメソッドは、新しいDataStoreオブジェクトを作成し、Copy関数でDial関数のSessionオブジェクトを取得します.各ルーティングのhandlerについて、新しいセッションオブジェクトはDataStoreタイプによって使用する.簡単に言えば、グローバルなSessionオブジェクトを使用する方法はよくないので、HTTPリクエストの宣言周期でCopy 1つのSessionオブジェクトを使用する方法をお勧めします.この方法では複数のセッションオブジェクトが存在する.