MongoDB的连接是通过url进行添加的,
MongoDB创建数据库,后端通过url和数据库名完成连接
数据的表在MongoDB中叫集台
在go中,可以初始化连接单表
但是连接有一个坑
{
"URL": "mongodb://today:today123@10.206.28.25:27017,10.206.28.26:27017,10.206.28.196:27017/today?replicaSet=replSet",
"Database": "today"
}
{
"URL": "mongodb://root:sf123456@10.206.53.156:27017,10.206.53.157:27017,10.206.53.158:27017/today?authSource=admin",
"Database": "today"
}
第一个连接是普通用户连接,第二个用户是管理员连接,在go启动连接后,err是nil,也就是没有问题,但是入库就出现了连接超时的情况,,原因就是使用的是管理员账号,需要加配置authSource=admin
//链接mongoDB相关设置,链接地址
var clientOptions = options.Client().ApplyURI(mongoConfig.URL)
Client, err := mongo.Connect(context.TODO(), clientOptions)
fmt.Println("mongodb连接mongoConfig.URL:", mongoConfig.URL)
fmt.Println("mongodb连接:", err)
//按钮信息集合
ActionColl = Client.Database(mongoConfig.Database).Collection("action_info")
注意问题
当使用 result := util.UserBizId.FindOne(context.TODO(), filter)
result.Err()是用来判断是否异常,包括查找不到文档,但是
result, err := util.UserBizId.Find(context.TODO(), filter, nil)
if err != nil {
return userBizeids, err
}
这种情况如果查不到数据并不会报err,所以拿到的数据应该二次判断。这里的问题可能在于结构问题
defer result.Close(context.TODO())
for result.Next(context.TODO()) {
var userBizeid model.UserBizid
err = result.Decode(&userBizeid)
//err = tabs.Decode(&item.PrimaryId)
if err != nil {
return userBizeids, err
}
userBizeids = append(userBizeids, userBizeid)
}
这种结构可能导致无法预知是否又下一个文档数据。
不懂就看菜鸟教程(查了半天索引命中没有找到,这里就看到了)
https://www.runoob.com/mongodb/mongodb-advanced-indexing.html
通过检查索引的生效,可以看出,索引是唯一的
db.getCollection("user_bizid").find({"updateTime":{"$gte": 1, "$lte": 2}}).explain();
db.getCollection("user_bizid").createIndex({"updateTime":1,"zSetType":1});
关于find的查找问题
查找所有
db.CustomSendFindMany(context.Background(), bson.M{})
正常情况下,下面的代码是可以解析的,但是Decode是必须针对确定的结构才能很好的解析
func BackgroundPageGroupConfigFindMany(ctx context.Context, filter bson.M) ([]BackgroundPageGroupConfig, error) {
var backgrounds []BackgroundPageGroupConfig
result, err := util.BackgroundPageGroupConfig.Find(ctx, filter)
//result, err := util.BackgroundPageGroupConfig.Find(ctx, filter, nil)
if err != nil {
return backgrounds, err
}
defer result.Close(ctx)
for result.Next(ctx) {
var background BackgroundPageGroupConfig
err = result.Decode(&background)
//err = tabs.Decode(&item.PrimaryId)
if err != nil {
return backgrounds, err
}
backgrounds = append(backgrounds, background)
}
return backgrounds, err
}
修复,采用result.Current.String()做json处理
for result.Next(ctx) {
var background BackgroundPageGroupConfig
var get = make(map[string]interface{})
result.Decode(&get)
marshal, err := json.Marshal(get)
if err!=nil {
logger.Info("BackgroundPageGroupConfigFindMany:json Marshal解析异常",err)
continue
}
err = json.Unmarshal(marshal, &background)
if err!=nil {
logger.Info("BackgroundPageGroupConfigFindMany:json Unmarshal解析异常",err)
continue
}
backgrounds = append(backgrounds, background)
}
关于mongodb的id
如果id是自定义一个string,这个时候可以通过string类型查找,但是如果是primitive.ObjectID,这个时候需要考虑转换
// string 转 ObjectID
obj_id, e := primitive.ObjectIDFromHex(xxxStr)
if e != nil {
//处理错误信息
}
// ObjectID 转 string
xxxStr := obj_id.Hex()
添加后返回id
返回类型是interface,但是最后成功后是object类型
func ProcessCategoryAdd(ctx context.Context, processCategory *model.ProcessCategory)(interface{},error) {
result, err := util.ProcessCategory.InsertOne(ctx, processCategory)
if err != nil {
return nil,err
}
if result.InsertedID != nil {
logger.Info("Insert successfully. ID: %v", result.InsertedID)
}
return result.InsertedID ,nil
}
processCategoryId, err := db.ProcessCategoryAdd(ctx, &processCategory)
objectID := processCategoryId.(primitive.ObjectID)
err = db.ProcessCategoryIdUpdate(ctx, in, userId, objectID.Hex())
模糊查找
flowKeyM, err := db.BackgroundPageGroupFindMany(context.Background(), bson.M{"flowKey": bson.M{"$regex": "104"}})
索引的底层研究
https://blog.csdn.net/duzm200542901104/article/details/81910509
唯一索引,稀疏索引
https://www.jb51.net/article/154610.htm
db.getCollection("test").createIndex( { "phone": 1 }, { sparse: true,unique: true } )
db.getCollection("theme_group_id").createIndex( { "customId": 1 }, { sparse: true,unique: true } )
db.getCollection("theme_group_id").createIndex( { "appId": 1 ,"runId":1}, { sparse: true,unique: true } )
索引是数组
注意不能出现两个索引都是数组的情况,只能是有一个
使用函数实现一些数据库操作
//分组实现数据的筛选,然后搬迁数据
db.theme_group_record.aggregate([{
KaTeX parse error: Expected '}', got 'EOF' at end of input: … _id: {runId: "runId", appId: “$appId”},
}
}]).forEach(
function(d){
db.getCollection(“gn”).insert({‘runId’:d._id.runId,‘appId’:d._id.appId,‘customId’:d._id.runId});
});
批量处理
当文档新增字段的时候,会发现没有像mysql数据库那样会给旧数据一个默认值,这个时候就需要做批量更新的处理
db.getCollection('sfecp_button_record').find({"recordType": "consultation"}).forEach(
function(item){
db.getCollection('sfecp_button_record').update({"_id":item._id},{$set:{"appId":"ecp"}})
}
)
排序字段不用加索引
以下命令查看的结果"indexName": “runId_1”,只有runId索引生效,所以排序字段不在索引的范围,
db.getCollection("theme_group_record").createIndex({"runId":1});
db.getCollection("theme_group_record").createIndex({"runId":1,"updateTime":1});
db.getCollection('theme_group_record').find({"runId":1234567}).sort({updateTime:-1}).explain()
添加索引
查看索引
db.user.getIndexes()
添加索引,一个或者联合索引
db.user.ensureIndex({“name”:1,“age”:1})
添加二级的索引
db.business_data_his.createIndex({“data.version”:1})
修改脚本
db.event_group.update({‘groupType’:‘taskCcMe’},{$set:{‘groupName.zh-CN’:‘任务(我关注)’,‘groupName.zh-HK’:‘任务(我關注)’,‘groupName.en-US’:‘Task(I
care)’}});
查数组对象某个字段的
icons.id
icons是一个数组,数组的对象里有个字段是id
{
"collection":"business_data_user",
"filter":{"icons.id":"5ec3d820e9403ba9bc0074da"}
}
模糊查询
采用正则表达式,如果是mongodb中的使用方法是:
db.article.find({“title”: {$regex: /a/, $options: “im”}})
但是在golang中,bson.M{“title”: bson.M{“
r
e
g
e
x
"
:
"
/
a
/
"
,
"
regex": "/a/", "
regex":"/a/","options”: “im”}} 失效
找到了primitive.Regex和bson.Regex使用无效
灵机一动
如果查不到数据,结果是怎样的?
这个时候err!=nil,内容是no document in result
update := db.BusinessDataPageUpdate(pageDO)
更新数据失败和成功
对于数据的更新,出现失败的情况。这里的pageDo是一个map,更新是根据id对map中的字段进行了更新。id值正确,但是更新失败了,为什么呢?
这是因为保存的时候采用了map保存,也就是说结构是不确定的,这也是mongodb跟mysql的巨大差别之一,所以同一个集合中可以保存任意形式的结构。但是问题来了,如果这个时候进行修改呢?这个时候map是一个确定的结构体,所以如果被修改的数据不是这种结构,将会导致更新失败,所以决定采用删除的方式。
但是,,,,,在page里的某个字段下,有一个包含数组的,添加一个新的对象到数组中,这个时候,却可以更新,这是因为对于page的数据来说,跟新前后是结构体是一样的,用java来说就是具有相同的类型,也就是类相同。
使用分布式锁进行对数据唯一操作
实际的结果出现了被操作了两次,为啥呢,因为作为标识的更新时间没有修改,所以,,,等于一直可以被修改。
go
//链接mongoDB相关设置,链接地址
var clientOptions = options.Client().ApplyURI(mongoConfig.URL)
Client, _ = mongo.Connect(context.TODO(), clientOptions)
//读取的dataBase和集合名称
TestCon = Client.Database(mongoConfig.Database).Collection("test")
存储数据的时候,是直接存储一个data
func Add(coll *mongo.Collection, data interface{}) error {
result, err := coll.InsertOne(context.TODO(), data)
if err != nil {
return err
}
if result.InsertedID != nil {
logger.Info("Insert successfully. ID: %v", result.InsertedID)
//data.Id = result.InsertedID.(primitive.ObjectID)
}
return nil
}
数据获取的时候,通过filter数据库中某个字段的值来进行获取data
func GetOne(coll *mongo.Collection, filter bson.M, opts ...*options.FindOneOptions) ([]byte, error) {
one := coll.FindOne(context.TODO(), filter, opts...)
var data []byte
err := one.Decode(&data)
//封装数据
return data, err
}
//注意如果这里要进行判断是否有数据,并不是用判断nil,而是判断结果的字段Err,
//但是这里用Decode完全没有毛病,查看底层代码会发现会对Err进行判断
result := util.UserBizId.FindOne(context.TODO(), bson.M{"appId":"abc"})
println(result == nil)//永远都是false
println(result.Err())
package db
import (
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"gotoday/model"
"gotoday/util"
)
//添加数据
func UserBizIdAdd(data *model.UserBizid) error {
result, err := util.UserBizId.InsertOne(context.TODO(), data)
if err != nil {
return err
}
if result.InsertedID != nil {
logger.Info("Insert successfully. ID: %v", result.InsertedID)
}
return nil
}
func UserBizIdDel(filter bson.M) error {
_, err := util.UserBizId.DeleteMany(context.TODO(), filter)
if err != nil {
return err
}
return nil
}
//查找数据
func UserBizIdFindOne(filter bson.M) model.UserBizid {
var userBizeid model.UserBizid
result := util.UserBizId.FindOne(context.TODO(), filter)
if result == nil {
return userBizeid
}
result.Decode(&userBizeid)
//封装数据
return userBizeid
}
//更新数据
func UserBizIdUpdate(data model.UserBizid) error {
filter := bson.M{"_id": data.Id}
update_content := bson.D{
{"$set", bson.D{
{"userId", data.UserId},
{"bizId", data.BizId},
{"zSetType", data.ZSetType},
{"updateTime", data.UpdateTime},
}},
}
_, err := util.UserBizId.UpdateOne(context.TODO(), filter, update_content)
if err != nil {
return err
}
return nil
}
//更新数据
func UserBizIdFindAndUpdate(data model.UserBizid) model.UserBizid {
var userBizeid model.UserBizid
filter := bson.M{"userId": data.UserId, "bizId": data.BizId, "zSetType": data.ZSetType}
update_content := bson.D{
{"$set", bson.D{
{"updateTime", data.UpdateTime},
}},
}
result := util.UserBizId.FindOneAndUpdate(context.TODO(), filter, update_content)
if result == nil {
return userBizeid
}
result.Decode(&userBizeid)
//封装数据
return userBizeid
}
func GetUserBizidList(filter bson.M) ([]model.UserBizid, error) {
var userBizids []model.UserBizid
option := new(options.FindOptions)
sortMap := make(map[string]interface{})
sortMap["updateTime"] = 1
option.Sort = sortMap
results, err := util.UserBizId.Find(context.TODO(), filter, option)
if err != nil {
return userBizids, err
}
defer results.Close(context.TODO())
for results.Next(context.TODO()) {
var userBizid model.UserBizid
err = results.Decode(&userBizid)
if err != nil {
return userBizids, err
}
userBizids = append(userBizids, userBizid)
}
return userBizids, nil
}
func AppendList(list *[]model.UserBizid, filter bson.M, bizMap *map[string]string) {
results, err := GetUserBizidList(filter)
if err != nil {
return
}
for _, v := range results {
if value, ok := (*bizMap)[v.BizId]; ok {
logger.Info("去重", value)
} else {
*list = append(*list, v)
(*bizMap)[v.BizId] = v.BizId
}
}
}
查找的filter
filter3 := bson.M{"userId": strconv.FormatInt(userId, 10), "companyId": strconv.FormatInt(companyId, 10), "zSetType": constant.TM_END_BASE, "updateTime": bson.M{"$gte": start, "$lte": end}}
设计一个动态查询的接口
先看入参
{
"collection":"template_relation",//集合名
"filter":{"status":{"completed":true}}//查表的filter,这里实现第二级的判断条件
}
gin框架代码
func SelectFromCollection(ctx *gin.Context) {
//验签
flag, msg := check(ctx)
if !flag {
util.Response(ctx, 400, msg, "")
return
}
var param param.FromCollectionParam
err := ctx.BindJSON(¶m)
if err != nil {
logger.Error("请求参数格式不正确:", err)
util.Response(ctx, 500, "请求参数格式不正确", nil)
return
}
collName := param.Collection
var coll *mongo.Collection
coll = util.Client.Database(config.MongoConfiguration.Database).Collection(collName)
filter := param.Filter
results, err := coll.Find(context.TODO(), filter)
if err != nil {
util.Response(ctx, 400, "查库失败", "")
return
}
defer results.Close(context.TODO())
var datas []interface{}
for results.Next(context.TODO()) {
var dataMap = make(map[string]interface{})
err = results.Decode(&dataMap)
if err != nil {
logger.Info("数据解析异常")
} else {
datas = append(datas, dataMap)
}
}
util.Response(ctx, 200, "ok", datas)
}
func check(ctx *gin.Context) (flag bool, msg string) {
appKey := ctx.Request.Header.Get("appKey")
timestamp := ctx.Request.Header.Get("timestamp")
nonce := ctx.Request.Header.Get("nonce")
checksum := ctx.Request.Header.Get("checksum")
if "gotoday" != appKey {
return false, "appKey不正确"
}
//验签的过期
hash := sha1.New()
hash.Write([]byte("cc" + nonce + timestamp))
result := fmt.Sprintf("%x", hash.Sum(nil))
if result != checksum {
return false, "验证不通过"
}
return true, "ok"
}