- 启动容器
# start mongodb by docker $ docker run -p 27017:27017 --restart always -v /data/mongodb/db:/data/db --name mongodb -d mongo:4.4.10- 创建登录账号
账户: root 密码: 123456
$ docker exec -it mongodb mongo > use admin > db.createUser({user:"root", pwd: "123456", roles: ["root"]}) > exit- 进入容器终端
$ docker exec -it mongodb bash # 在容器内安装wget root@072bedc2e6c5:/# apt-get update && apt-get install wget root@072bedc2e6c5:/# cd /tmp root@072bedc2e6c5:/tmp# - 基于经纬度的测试数据
restaurants.json是餐馆所在经纬度数据
https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/restaurants.json
# 下载保存到/tmp目录 root@072bedc2e6c5:/tmp# wget https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/restaurants.json- 基于GeoJSON的测试数据
neighborhoods.json数据是一些多边形范围数据(由N个点围成一圈)
https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/neighborhoods.json
# 下载保存到/tmp目录 root@072bedc2e6c5:/tmp# wget https://raw.githubusercontent.com/mongodb/docs-assets/geospatial/neighborhoods.json- 导入测试数据到test库
root@072bedc2e6c5:/tmp# ls *.json neighborhoods.json restaurants.json root@072bedc2e6c5:/tmp# mongoimport restaurants.json -c restaurants 2023-05-10T09:20:32.821+0000 connected to: mongodb://localhost/ 2023-05-10T09:20:35.517+0000 25359 document(s) imported successfully. 0 document(s) failed to import. root@072bedc2e6c5:/tmp# mongoimport neighborhoods.json -c neighborhoods 2023-05-10T09:21:05.400+0000 connected to: mongodb://localhost/ 2023-05-10T09:21:07.305+0000 195 document(s) imported successfully. 0 document(s) failed to import.- 登录mongo终端创建索引
root@072bedc2e6c5:/tmp# mongo # 切换到test库 > use test # 查询一条数据看看表结构 > db.restaurants.find().limit(1).pretty() { "_id" : ObjectId("55cba2476c522cafdb053ae2"), "location" : { "coordinates" : [ -73.98513559999999, 40.7676919 ], "type" : "Point" }, "name" : "Dj Reynolds Pub And Restaurant" } # 为restaurants表location字段创建2D球面索引 > db.restaurants.createIndex({"location":"2dsphere"}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } # 为neighborhoods表geometry字段建立2D球面索引 > db.neighborhoods.createIndex({ geometry: "2dsphere" }) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }- 所有的ORM操作必须是以Model方法开始,执行delete/update操作允许不传参
- Model方法会通过传入的模型结构体名称自动识别数据表名(小写蛇形),也可以通过Table方法指定表名。除了数据库层面的聚合操作不需要指定表名,其他操作都需要指定表名。
package main import ( "time" "github.com/civet148/log" "github.com/civet148/mgoc" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) type ExtraData struct { IdCard string `bson:"id_card"` Address string `bson:"address"` } type Student struct { Id string `bson:"_id,omitempty"` Name string `bson:"name"` Sex string `bson:"sex"` Age int `bson:"age"` ClassNo string `bson:"class_no"` Balance mgoc.Decimal `bson:"balance"` CreatedTime time.Time `bson:"created_time"` ExtraData ExtraData `bson:"extra_data"` } func main() { e, err := mgoc.NewEngine("mongodb://root:123456@127.0.0.1:27017/test?authSource=admin") if err != nil { log.Errorf(err.Error()) return } var students []*Student err = e.Model(&students). Table("student_info"). Options(&options.FindOptions{}). Desc("created_time"). Limit(10). Query() if err != nil { log.Errorf(err.Error()) return } for _, s := range students { log.Infof("student %+v", s) } }-
Options方法 [optional]
根据实际操作类型不同可输入不同的Option类型,比如查询时选填options.FindOptions,更新时可选填options.UpdateOptions 插入单条记录时可选填options.InsertOneOptions,插入多条则是options.InsertManyOptions等等(选填)
- 单条插入
var student = Student{ Name: "john", Sex: "male", Age: 13, ClassNo: "CLASS-3", Balance: mgoc.NewDecimal("532.324"), CreatedTime: time.Now(), } ids, err := e.Model(&student). Table("student_info"). Options(&options.InsertOneOptions{}). Insert() if err != nil { log.Errorf(err.Error()) return } log.Infof("[Single] insert id %+v", ids)- 多条插入(非事务)
var students = []*Student{ { Name: "lory", Sex: "male", Age: 14, ClassNo: "CLASS-1", CreatedTime: time.Now(), }, { Name: "katy", Sex: "female", Age: 15, ClassNo: "CLASS-2", CreatedTime: time.Now(), }, } ids, err := e.Model(&students). Options(&options.InsertManyOptions{}). Table("student_info"). Insert() if err != nil { log.Errorf(err.Error()) return } log.Infof("[Many] insert ids %+v", ids)- 单条查询
SELECT * FROM student_info LIMIT 1
var err error var student *Student err = e.Model(&student). Table("student_info"). Limit(1). Query() if err != nil { log.Errorf(err.Error()) return } log.Infof("single student %+v", student)- 多条查询
SELECT _id, name, age, sex FROM student_info ORDER BY created_time DESC LIMIT 10
var err error var students []*Student err = e.Model(&students). Table("student_info"). Select("_id", "name", "age", "sex"). Options(&options.FindOptions{}). Desc("created_time"). Limit(10). Query() if err != nil { log.Errorf(err.Error()) return } for _, student := range students { log.Infof("student %+v", student) }- 分页查询
SELECT _id, name, age, sex FROM student_info ORDER BY created_time DESC LIMIT 0,10
var students []*Student total, err := e.Model(&students). Table("student_info"). Select("_id", "name", "age", "sex"). Options(&options.FindOptions{}). Desc("created_time"). Page(1, 10). QueryEx() if err != nil { log.Errorf(err.Error()) return } log.Infof("student total %+v", total) for _, student := range students { log.Infof("student %+v", student) }- 条件查询
SELECT _id, name, age, sex FROM student_info WHERE class_no='CLASS-2' AND age >= 11 and age <=16 ORDER BY created_time DESC
var err error var students []*Student err = e.Model(&students). Table("student_info"). Select("_id", "name", "age", "sex"). Options(&options.FindOptions{}). Eq("class_no", "CLASS-2"). Gte("age", 11). Lte("age", 16). Desc("created_time"). Query() if err != nil { log.Errorf(err.Error()) return } for _, student := range students { log.Infof("student %+v", student) }- 自定义查询
SELECT _id, name, age, sex FROM student_info WHERE class_no='CLASS-2' AND age >= 11 and age <=16 ORDER BY created_time DESC
var err error var students []*Student err = e.Model(&students). Table("student_info"). Select("_id", "name", "age", "sex"). Options(&options.FindOptions{}). Filter(bson.M{ "class_no":"CLASS-2", "age": bson.M{"$gte":11}, "age": bson.M{"$lte":16}, }). Desc("created_time"). Query() if err != nil { log.Errorf(err.Error()) return } for _, student := range students { log.Infof("student %+v", student) }- 通过数据模型更新字段
UPDATE student_info SET name='kary', sex='female', age=39, balance='123.456', created_time=NOW() WHERE _id='6438f32fd71fc42e601558aa'
// 更新Id值6438f32fd71fc42e601558aa对应的数据记录 var student = &Student{ Id: "6438f32fd71fc42e601558aa", Name: "kary", Sex: "female", Age: 39, Balance: mgoc.NewDecimal("123.456"), CreatedTime: time.Now(), } _, err := e.Model(&student). Table("student_info"). Options(&options.UpdateOptions{}). Select("name", "sex", "age", "balance", "created_time"). Update() if err != nil { log.Errorf(err.Error()) return }- 通过Set方式更新字段
_, err := e.Model(). Table("student_info"). Options(&options.UpdateOptions{}). Id("6438f32fd71fc42e601558aa"). Set("name", "mason"). Set("sex", "male"). Set("balance", mgoc.NewDecimal("123.456")). Update() if err != nil { log.Errorf(err.Error()) return }- 结构嵌套更新
var student = &Student{ Id: "6438f32fd71fc42e601558aa", ClassNo: "CLASS-3", ExtraData: ExtraData { IdCard: "6553239109322", }, } // UPDATE student_info // SET class_no='CLASS-3', extra_data.id_card='6553239109322' // WHERE _id='6438f32fd71fc42e601558aa' _, err = e.Model(&student). Table("student_info"). Options(&options.UpdateOptions{}). Select("class_no", "extra_data.id_card"). Update() if err != nil { log.Errorf(err.Error()) return } //等同于下面的方式 _, err = e.Model(). Table("student_info"). Id("6438f32fd71fc42e601558aa"). Options(&options.UpdateOptions{}). Set("class_no", "CLASS-3"). Set("extra_data.id_card", "6553239109322"). Update() if err != nil { log.Errorf(err.Error()) return }var err error _, err = e.Model(). Table("student_info"). Id("6438f32fd71fc42e601558aa"). Set("name", "rose"). Set("sex", "female"). Set("age", 18). Set("created_time", time.Now()). Set("balance", mgoc.NewDecimal("520.1314")). Upsert() if err != nil { log.Errorf(err.Error()) return } //使用数据模型进行upsert操作(如果已id对应数据存在则更新balance) var student = &Student{ Id: "6438f32fd71fc42e601558aa", Name: "rose", Sex: "female", Age: 18, Balance: NewDecimal("123.456"), CreatedTime: time.Now(), } _, err = e.Model(&student). Table(TableNameStudentInfo). Select("balance"). Upsert() if err != nil { log.Errorf(err.Error()) return }rows, err := e.Model(). Table("student_info"). Options(&options.DeleteOptions{}). Id("6438f32fd71fc42e601558aa"). Delete() if err != nil { log.Errorf(err.Error()) return } log.Infof("rows %d deleted", rows)- ORM聚合查询
SELECT AVG(age) AS age, SUM(1) AS total, SUM(balance) as balance FROM student_info WHERE sex='female' GROUP BY name, age
/* db.getCollection("student_info").aggregate([ { "$match":{ "sex":"female" }, }, { "$group":{ "_id":{"name":"$name", "age":"$age"}, "age":{ "$avg":"$age"}, "balance":{ "$sum":"$balance"}, "total":{ "$sum":1} } } ]) ---------------------------------------------------------------------- { "_id": { "name": "katy3", "age": NumberInt("28") }, "age": 28, "balance": NumberDecimal("24149.3374"), "total": 8 } // 2 { "_id": { "name": "katy3", "age": NumberInt("27") }, "age": 27, "balance": NumberDecimal("234"), "total": 1 } // 3 { "_id": { "name": "rose", "age": NumberInt("18") }, "age": 18, "balance": NumberDecimal("520.1314"), "total": 1 } */ type AggID struct { Name string `bson:"name"` Female string `bson:"female"` } type StudentAgg struct { ID AggID `bson:"_id"` Age float64 `bson:"age"` Total int `bson:"total"` Balance mgoc.Decimal `bson:"balance"` } var agg []*StudentAgg err := e.Model(&agg). Table("student_info"). Avg("age"). Sum("total", 1). Sum("balance"). Eq("sex", "female"). GroupBy("name", "age"). Aggregate() if err != nil { log.Errorf(err.Error()) return } log.Infof("aggregate rows %d", len(agg)) for _, a := range agg { log.Infof("%+v", a) }- 自定义聚合查询
SELECT AVG(age) AS age, COUNT(1) AS total FROM student_info WHERE sex='female'
/* db.getCollection("student_info").aggregate([ { "$match":{ "sex":"female" }, }, { "$group":{ "_id":null, "age":{ "$avg":"$age"}, "total":{ "$sum":1} } }, { "$project":{ "_id":0, "age":1, "total":1 } } ] ) ---------- { "age": 18, "total": 14 } */ type StudentAgg struct { Age float64 `bson:"age"` Total int `bson:"total"` } var agg []*StudentAgg // create match stage match := bson.D{ { "$match", bson.D{ {"sex", "female"}, }, }, } // create group stage group := bson.D{ {"$group", bson.D{ {"_id", nil}, {"age", bson.D{{"$avg", "$age"}}}, {"total", bson.D{{"$sum", 1}}}, }}} // create projection stage project := bson.D{ {"$project", bson.D{ {"_id", 0}, {"age", 1}, {"total", 1}, }}} err := e.Model(&agg). Table("student_info"). Options(&options.AggregateOptions{}). Pipeline(match, group, project). Aggregate() if err != nil { log.Errorf(err.Error()) return } log.Infof("aggregate rows %d", len(agg)) for _, a := range agg { log.Infof("%+v", a) }- 查询某一点1000米范围内所有的餐馆
type Restaurant struct { Id string `json:"_id" bson:"_id,omitempty"` Location struct { Type string `json:"type" bson:"type"` Coordinates []float64 `json:"coordinates" bson:"coordinates"` } `json:"location" bson:"location"` Name string `json:"name" bson:"name"` Distance float64 `json:"distance" bson:"distance"` } const maxMeters = 1000 //meters var pos = mgoc.Coordinate{X: -73.93414657, Y: 40.82302903} //query restaurants near by distance 1000 meters var restaurants []*Restaurant err := e.Model(&restaurants). Table("restaurants"). GeoCenterSphere("location", pos, maxMeters). Query() if err != nil { log.Errorf(err.Error()) return } log.Infof("center sphere restaurants total [%d]", len(restaurants))- 查询某个社区范围内的所有餐馆
type Neighborhood struct { Id string `json:"_id" bson:"_id,omitempty"` Geometry mgoc.Geometry `json:"geometry" bson:"geometry"` Name string `json:"name" bson:"name"` } type Restaurant struct { Id string `json:"_id" bson:"_id,omitempty"` Location struct { Type string `json:"type" bson:"type"` Coordinates []float64 `json:"coordinates" bson:"coordinates"` } `json:"location" bson:"location"` Name string `json:"name" bson:"name"` Distance float64 `json:"distance" bson:"distance"` } var neighbor *Neighborhood var pos = Coordinate{X: -73.93414657, Y: 40.82302903} //查询 -73.93414657, 40.82302903 所在社区的社区范围信息 err = e.Model(&neighbor). Table("neighborhoods"). Filter(bson.M{ "geometry": bson.M{ mgoc.KeyGeoIntersects: bson.M{ mgoc.KeyGeoMetry: mgoc.NewGeoMetry(mgoc.GeoTypePoint, mgoc.FloatArray{pos.X, pos.Y}), }, }, }). Limit(1). Query() if err != nil { log.Errorf(err.Error()) return } log.Infof("neighborhood [%+v]", neighbor) //查询社区范围内的餐馆 var restaurants []*Restaurant err = e.Model(&restaurants). Table("restaurants"). Geometry("location", &neighbor.Geometry). Query() if err != nil { log.Errorf(err.Error()) return } log.Infof("neighborhood restaurants total [%d]", len(restaurants))- 查询某一点附近1000米内的所有餐馆数据并附带距离
type Restaurant struct { Id string `json:"_id" bson:"_id,omitempty"` Location struct { Type string `json:"type" bson:"type"` Coordinates []float64 `json:"coordinates" bson:"coordinates"` } `json:"location" bson:"location"` Name string `json:"name" bson:"name"` Distance float64 `json:"distance" bson:"distance"` } const maxMeters = 1000 //meters var pos = Coordinate{X: -73.93414657, Y: 40.82302903} var restaurants []*Restaurant err := e.Model(&restaurants). Table("restaurants"). Limit(10). Asc("distance"). GeoNearByPoint( "location", //存储经纬度的字段 pos, //当前位置数据 maxMeters, //最大距离数(米) "distance"). //距离数据输出字段名 Aggregate() if err != nil { log.Errorf(err.Error()) return } for _, restaurant := range restaurants { log.Debugf("geo near restaurant [%+v]", restaurant) } log.Infof("geo near restaurants total [%d]", len(restaurants))- Database方法
e, err := mgoc.NewEngine("mongodb://root:123456@127.0.0.1:27017/test?authSource=admin") if err != nil { log.Errorf(err.Error()) return } db := e.Database()- Use方法
e, err := mgoc.NewEngine("mongodb://root:123456@127.0.0.1:27017/test?authSource=admin") if err != nil { log.Errorf(err.Error()) return } db := e.Use("test2") e, err := mgoc.NewEngine("mongodb://root:123456@127.0.0.1:27017/test?authSource=admin") if err != nil { log.Errorf(err.Error()) return } col := e.Collection() _ = col查找一条记录
只更新一条记录
查找一条记录并更新
查找一条记录并替换
查找一条记录并删除
流水线方法,当使用该方法时会忽略ORM的其他聚合操作(GroupBy/Sum/Avg/Min/Max...)
分页查询,仅QueryEx执行有效. Page(1,10) == LIMIT 0, 10
等价于 {"field":{"$ne":value}}
等价于 {"field":{"$eq":value}}
等价于 {"field":{"$gt":value}}
等价于 {"field":{"$gte":value}}
等价于 {"field":{"$lt":value}}
等价于 {"field":{"$lte":value}}
等价于 {"field":{"$gt":value1, "$lt":value2}}
等价于 {"field":{"$gte":value1, "$lt":value2}}
等价于 {"field":{"$gte":value1, "$lte":value2}}
等价于 {"field":{"$gt":value1, "$lte":value2}}
聚合操作求和, 针对filed做聚合时values可不填,同时values可以是数字也可以是bson.M对象 指定字段时Sum("field") 等价于 {"field":{"$sum":"$field"}} 指定计数时Sum("field", 1) 等价于 {"field":{"$sum":1}}
聚合操作求平均值, 针对filed做聚合时values可不填,同时values也可以是bson.M对象 指定字段时Avg("field") 等价于 {"field":{"$avg":"$field"}}
聚合操作取最大值, 针对filed做聚合时values可不填,同时values也可以是bson.M对象 指定字段时Max("field") 等价于 {"field":{"$max":"$field"}}
聚合操作取最小值, 针对filed做聚合时values可不填,同时values也可以是bson.M对象 指定字段时Min("field") 等价于 {"field":{"$min":"$field"}}