mongoDB操作应用万能手册,熟练不等于精通

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 日常工作的各种查询语句总结,自动化安装部署,应用接入……

标题:mongoDB操作应用万能手册,熟练不等于精通;

引言:日常工作的各种查询语句总结,自动化安装部署,应用接入……

1、操作篇

客户端连接mongo服务,身份认证之后才能操作,身份认证之前必须切换到admin库,身份认证函数使用db.auth(user, password);

root@VM-218-88-centos:/# mongo
MongoDB shell version v4.0.4
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("9f2bb75f-783e-4e57-a5d2-8c5301005c61") }
MongoDB server version: 4.0.4
> use admin
switched to db admin
> db.auth("user","password")
1

1.1、数据库

1、新增(切到)目标数据库:use database

2、查看当前数据库:db

3、查看所有的数据库:show databases或者show dbs

4、删除数据库,先use database,切换到需要删除的数据库,然后执行db.dropDatabase()删除当前库;

> use vendors
switched to db vendors
> db
vendors
> db.dropDatabase()
{ "dropped" : "vendors", "ok" : 1 }
自定义的用户可能会报没有删除权限操作,需要添加一下root角色权限;

1.2、集合

1、查看当前库中的所有集合:show collections或者 show tables

2、创建集合(可指定配置参数):db.createCollection(colName, options),可选项参数如下;

  • capped:(可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数;
  • size:(可选)为固定集合指定一个最大值,即字节数。 如果 capped 为 true,也需要指定该字段;
  • max:(可选)指定固定集合中包含文档的最大数量;
当操作的集合不存在时,直接向其插入文档,也会自动的创建集合;

3、删除集合:db.colName.drop();

> use test
switched to db test
> db.createCollection("user")
{ "ok" : 1 }
> show tables
user
> db.user.drop()
true
> show tables

1.3、文档

1.3.1、增

单条插入

db.colName.insert(obj)

> db.user.insert({"name":"ikejcwang", "age":27})
WriteResult({ "nInserted" : 1 })

多条插入

db.colName.insertMany(arr, {writeConcern: 1, ordered: true})

  • arr:需要插入的条目数组,(不要求数组元素的字段一致,基于mongo的特性);
  • options:可选项扩展;
  • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求;
  • ordered:指定是否按数组的顺序写入,默认 true,按顺序写入;
> db.user.insertMany([{name:"i", age:23}, {name:"k", age:24}, {name:"e", age:25}], {ordered: false})
{
        "acknowledged" : true,
        "insertedIds" : [
                ObjectId("633d2a67b242067655f426b6"),
                ObjectId("633d2a67b242067655f426b7"),
                ObjectId("633d2a67b242067655f426b8")
        ]
}

同时,mongo支持JS脚本语句操作(任何场景下都行):

> for(let i = 0; i <= 5; i++){
... db.user.insert({name:"ike"+i, age:30+i})
... }
WriteResult({ "nInserted" : 1 })

1.3.2、删

db.colName.remove(<query>, {justOne: false, writeConcern: 1})

  • query:,删除的指定条件(全删,传空即可);
  • justOne:(可选),如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配的文档;
  • writeConcern:(可选),抛出异常级别
> db.user.remove({name:"ike0"})
WriteResult({ "nRemoved" : 1 })
>
> db.user.remove({})
WriteResult({ "nRemoved" : 9 })

1.3.3、改

db.colName.update(<query>, <update>, {upsert: false, multi: false, writeConcern: 1})

  • query:修改的查询条件(全修改传空即可);
  • update:更新覆盖的内容;
  • upsert:(可选)如果不存在update的记录,是否插入update,true为插入,默认是false,不插入;
  • multi:(可选)默认是false,只更新找到的第一条记录,设置参数为true,就把按条件查出来多条记录全部更新;
  • writeConcern:(可选),抛出异常的级别

更新操作必须使用修改器$set,query为空配合{multi: true},表示修改全部

> db.user.update({}, {"$set": {name:"ikejcwang"}}, {multi: true})
WriteResult({ "nMatched" : 6, "nUpserted" : 0, "nModified" : 5 })

1.3.4、查

查询后面继续调用pretty()函数,可以格式化结果集展示

标准

db.colName.find(<query>, projection)

  • query:可选:查询条件(查询条件多样性),
  • projection:展示和屏蔽的字段。eg:{name: 1}结果集仅展示name字段,{name: 0}结果集屏蔽name字段;

    _id会默认展示出来,除非手动屏蔽
> db.user.find({name:"ikejcwang"}, {name:1})
{ "_id" : ObjectId("633d2e90b242067655f426c0"), "name" : "ikejcwang" }
{ "_id" : ObjectId("633d2e90b242067655f426c1"), "name" : "ikejcwang" }
{ "_id" : ObjectId("633d2e90b242067655f426c2"), "name" : "ikejcwang" }
{ "_id" : ObjectId("633d2e90b242067655f426c3"), "name" : "ikejcwang" }
{ "_id" : ObjectId("633d2e90b242067655f426c4"), "name" : "ikejcwang" }

> db.user.find({name:"ikejcwang"}, {name:0, _id:0})
{ "age" : 31 }
{ "age" : 32 }
{ "age" : 33 }
{ "age" : 34 }
{ "age" : 35 }

统计

db.colName.find().size()

或者

db.colName.find().count();

去重

db.colName.distinct(field)

  • field:字符串,去重筛选的字段,
> db.user.distinct("name")
[ "ikejcwang" ]

排序

db.colName.find().sort(fieldOptions)

  • fieldOptions:支持多字段排序,1:生序,-1:降序。
> db.user.find().sort({name:1, age:-1})

模糊

,模糊如下:

正则表达式的匹配逻辑
# 查询集合中name字段,包含ik的条目
db.user.find({name:/ik/})

# 查询集合中name字段,以ik开头的条目
db.user.find({name:/^ik/})

# 查询集合中name字段,以ik结尾的条目
db.user.find({name:/ik$/})

大小于

,大于小于等于的条件需要用到比较器:$lt(小于),$lte(小于等于),$gt(大于),$gte(大于等于),$ne(不等于)。

# 查询age小于33的条目
> db.user.find({age:{$lt:33}})

# 查询age大于33的条目
> db.user.find({age:{$gt:33}})

# 查询age大于33,小于35的条目
> db.user.find({age:{$gt:33, $lt:35}})

存在判断

,需要用到操作符$exists,主要是操作字段内容是否为空

  • $exists:true,不为空,false,为空;
# 查询统计name不为空的条目个数
db.user.find(name:{$exists:true}).count()

# 类似于mysql的
select count(name) from tableName

or

,需要用到操作符$or,

# 单独or
db.tableName.find({"$or":[{"login_name":/王/},{"role_name":/王/}]},{"login_name":true,"role_name":true})

# and or,
db.tableName.find({"name":"ike", "$or":[{"login_name":/王/},{"role_name":/王/}]},{"login_name":true,"role_name":true})

分页

limit()skip()函数

  • limit:查询条目数;
  • skip:查询的起始位置;
# 查询聚集中前5条记录
>db.tableName.find().limit(5)
 
# 查询聚集中第10条以后的记录,就是从11条开始
>db.tableName.find().skip(10)
 
# 查询聚集中第10条记录以后的5条记录,正式分页处理
>db.tableName.find().skip(10).limit(5)

包含与否

,需要用到操作符$in$nin

  • $in:value为数组,表示查询引用字段的值包含在数组中的条目;
  • $nin:value为数组,表示查询引用字段的值不包含在数组中的条目;
# 查询name的值,在给定的数组中的条目
db.user.find({name:{$in:["ikejcwang", "hah"]}})

# 查询name的值,不在给定的数组中的条目
db.user.find({name:{$nin:["hah"]}})

1.3.5、数组查询

构造测试的数组文档字段,如下所示:tag字段为数组

> for(let i = 0; i <= 10; i++){
... let tags = [];
... for(let t = 0; t <= i; t++){
... tags.push("tag"+t);
... }
... db.user.insert({name:"ik"+i, age:"30"+i, tag:tags})
... }
WriteResult({ "nInserted" : 1 })
> 
> db.user.find()
{ "_id" : ObjectId("633d4434b242067655f426c5"), "name" : "ik0", "age" : "300", "tag" : [ "tag0" ] }
{ "_id" : ObjectId("633d4434b242067655f426c6"), "name" : "ik1", "age" : "301", "tag" : [ "tag0", "tag1" ] }
{ "_id" : ObjectId("633d4434b242067655f426c7"), "name" : "ik2", "age" : "302", "tag" : [ "tag0", "tag1", "tag2" ] }
{ "_id" : ObjectId("633d4434b242067655f426c8"), "name" : "ik3", "age" : "303", "tag" : [ "tag0", "tag1", "tag2", "tag3" ] }
{ "_id" : ObjectId("633d4434b242067655f426c9"), "name" : "ik4", "age" : "304", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4" ] }
{ "_id" : ObjectId("633d4434b242067655f426ca"), "name" : "ik5", "age" : "305", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4", "tag5" ] }
{ "_id" : ObjectId("633d4434b242067655f426cb"), "name" : "ik6", "age" : "306", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4", "tag5", "tag6" ] }
{ "_id" : ObjectId("633d4434b242067655f426cc"), "name" : "ik7", "age" : "307", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7" ] }
{ "_id" : ObjectId("633d4434b242067655f426cd"), "name" : "ik8", "age" : "308", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8" ] }
{ "_id" : ObjectId("633d4434b242067655f426ce"), "name" : "ik9", "age" : "309", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9" ] }
{ "_id" : ObjectId("633d4434b242067655f426cf"), "name" : "ik10", "age" : "3010", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9", "tag10" ] }

匹配

要对数组字段进行相等条件的查询,条件肯定也必须为数组,且顺序也必须一致;

:

# 指定集合中,tag字段 == ["tag0", "tag1"]的条目
> db.user.find({tag:["tag0", "tag1"]})
{ "_id" : ObjectId("633d4434b242067655f426c6"), "name" : "ik1", "age" : "301", "tag" : [ "tag0", "tag1" ] }

包含

如果只是要找到一个包含指定元素的数组,但不考虑目标字段数组中的顺序或其他元素,需要使用$all操作符

# 指定集合中,tag字段数组元素中,包含"tag0", "tag1"的条目数量
> db.user.find({tag:{$all:["tag0", "tag1"]}}).count()
10

元素查询

查询数组字段是否包含指定值的元素,使用普通的过滤器;

# 查询tag字段中,包含"tag9"元素的条目数量
> db.user.find({tag:"tag9"}).count()
2
同理,它也支持其他的条件过滤:比如大小于:

db.user.find({tag:{$gt: "10"}}).count()db.user.find({tag:{$gt: 10}}).count()

多条件元素查询

1、复合条件单个满足即可:

# 查询结果:目标字段数组中:单个元素满足大于15,或者单个元素满足小于 20,或者单个元素同时满足这两个条件的结果集:
db.user.find( { tagNum: { $gt: 15, $lt: 20 } } )

2、复合条件多个满足:

# 查询结果:目标字段数组中:至少包含一个元素能满足大于22,小于33的结果集
db.user.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

3、按目标字段数据元素的索引位置匹配查询

.的形式

# 查询tag字段,第9个元素为"tag9"的
> db.user.find({"tag.9": "tag9"}).count()
2

4、按目标字段数组的长度查询

# 查询tag字段数组长度为5的条目
> db.user.find({tag:{$size:5}})
{ "_id" : ObjectId("633d4434b242067655f426c9"), "name" : "ik4", "age" : "304", "tag" : [ "tag0", "tag1", "tag2", "tag3", "tag4" ] }

1.3.6、聚合查询

aggregate:聚合运算函数

  • $match:查询过滤条件;
  • $lookup:连表查询;
  • $project:展示/屏蔽字段,采用$符号来定义别名;
  • …………

字段别名

find只能展示和屏蔽字段,但是不能给字段定义别名,这里只能依靠聚合操作;

# 查询name包含"ik"的条目,并且将name字段取别名为userName展示,
> db.user.aggregate([{$match:{name:/ik/}}, {$project:{"userName":"$name", "_id":0}}])
{ "userName" : "ikejcwang" }
{ "userName" : "ikejcwang" }
{ "userName" : "ikejcwang" }
{ "userName" : "ikejcwang" }
{ "userName" : "ikejcwang" }
{ "userName" : "ikejcwang" }
{ "userName" : "ik0" }
{ "userName" : "ik1" }
{ "userName" : "ik2" }
{ "userName" : "ik3" }
{ "userName" : "ik4" }
{ "userName" : "ik5" }
{ "userName" : "ik6" }
{ "userName" : "ik7" }
{ "userName" : "ik8" }
{ "userName" : "ik9" }
{ "userName" : "ik10" }

联合查询

使用$lookup操作符,相当于连表join,$lookup参数介绍:

  • from:需要连接的集合名称;
  • as:对from的集合取别名;
  • localField:当前集合中对from的关联字段;
  • foreignField:from集合中关联当前集合的字段;
# 如下两张集合有关联关系,通过acs_policy的resource_member_ids来关联,
# 操作预期:acs_policy关联acs_resource_member查询,通过acs_policy的subjects字段做过滤查询,结果集屏蔽_id,将acs_policy的subjects取别名为role_ids,将关联acs_resource_member的id取别名为resource_member_id_list,最终对结果集格式化输出。

> db.acs_policy.aggregate([
    {"$match":{"subjects":{"$in":["d466d7a9-5152-47e7-ba5c-1c9bb8d0ad6c", "62a0ca2d-413a-4484-ac08-b911595b70d9"]}}},
    {"$lookup":{"from":"acs_resource_member", "as":"resourceMember", "localField":"resource_member_ids", "foreignField":"id"}},
    {"$project": {"resource_member_id_list": "$resourceMember.id", "role_ids": "$subjects", "_id":0}},
]).pretty()
{
        "resource_member_id_list" : [
                "146804b5-95bb-444a-8bd8-811d0364866c",
                "1c0c338f-b63f-48b5-9134-f48460d4ab10",
                "1f69910a-71d0-4d02-995c-215816758b6c",
                "c14b7e97-60cb-4a14-aa31-91ee542089b0",
                "c774b0c4-2774-4fcf-8760-5b49951c12e8",
                "dbc8d823-5a08-406f-8670-28ccb74ec959",
                "f6e03c36-0f64-4b77-a6dc-f6ae4af76abc"
        ],
        "role_ids" : [
                "62a0ca2d-413a-4484-ac08-b911595b70d9"
        ]
}
{
        "resource_member_id_list" : [
                "10d514b6-38a5-4f1a-8f61-a5874c1788ef",
                "3022c03e-bcac-4d98-8952-d118b581d803",
                "3d2e3770-0d1c-402b-a7f7-f7f5a665f69d",
                "96945cde-a5bb-4ba3-ae28-04f163bb296c",
                "9b0b1146-ae05-46da-9a42-07d13e1169c7",
                "ceb77748-39af-47e8-817d-282754058eed",
                "f8d9a9e5-2ede-4445-93d7-aba6698cd9cd"
        ],
        "role_ids" : [
                "d466d7a9-5152-47e7-ba5c-1c9bb8d0ad6c",
                "eeeee"
        ]
}

分组求和

对service集合,以"app.id","sys.id"分组,求和,统计每个"app.id","sys.id"下所有的数目,然后排序生序,并且最终对结果集进行格式化取别名处理,

$group操作符的_id为固定格式,下面包裹的是需要分组的字段;

count是自定义的统计展示字段,下面包含统计方式

> db.service.aggregate([
...     {
...         "$group":{
...           "_id":{
...             "appName":"$app._id",
...             "sysName":"$sys._id"
...           },
...           "count":{
...             "$sum":1
...           }
...         }
...     },
...     {
...         "$sort":{"count":-1}
...     },
...     {"$project":{"userCount":"$count", "sysName":"$_id.sysName", "appName":"$_id.appName", "_id":0}}
... ])
{ "userCount" : 137, "sysName" : "defaultsystem", "appName" : "amp" }
{ "userCount" : 60, "sysName" : "defaultsystem", "appName" : "acc" }
{ "userCount" : 53, "sysName" : "defaultsystem", "appName" : "rbac" }
{ "userCount" : 46, "sysName" : "defaultsystem", "appName" : "op" }
{ "userCount" : 25, "sysName" : "defaultsystem", "appName" : "vmp" }
{ "userCount" : 20, "sysName" : "defaultsystem", "appName" : "sc" }
{ "userCount" : 20, "sysName" : "defaultsystem", "appName" : "omp" }
{ "userCount" : 18, "sysName" : "defaultsystem", "appName" : "upf" }
{ "userCount" : 13, "sysName" : "defaultsystem", "appName" : "atm" }
{ "userCount" : 13, "sysName" : "defaultsystem", "appName" : "asm" }
{ "userCount" : 10, "sysName" : "defaultsystem", "appName" : "dt" }
{ "userCount" : 5, "sysName" : "defaultsystem", "appName" : "aud" }
{ "userCount" : 4, "sysName" : "defaultsystem", "appName" : "dbs" }
{ "userCount" : 3, "sysName" : "defaultsystem", "appName" : "ikejcwang" }
{ "userCount" : 2, "sysName" : "defaultsystem", "appName" : "network" }
{ "userCount" : 2, "sysName" : "defaultsystem", "appName" : "js_server_demo" }
{ "userCount" : 1, "sysName" : "defaultsystem", "appName" : "java_server_demo" }

分组求最值,平均值

以app.id分组,统计每个app._id下最大的status,并且排序降序,并且最终对结果集进行格式化取别名处理,

同理:$min(最小值),$avg(平均值),
> db.service.aggregate([
...     {
...         "$group":{
...           "_id":{
...             "appName":"$app._id"
...           },
...           "count":{
...             "$max":"$status"
...           }
...         }
...     },
...     {
...         "$sort":{"count":-1}
...     },
...     {"$project":{"_id":0, "appName":"$_id.appName", "maxStatus":"$count"}}
... ])
{ "appName" : "dt", "maxStatus" : 2 }
{ "appName" : "java_server_demo", "maxStatus" : 2 }
{ "appName" : "dbs", "maxStatus" : 2 }
{ "appName" : "vmp", "maxStatus" : 2 }
{ "appName" : "aud", "maxStatus" : 2 }
{ "appName" : "asm", "maxStatus" : 2 }
{ "appName" : "op", "maxStatus" : 2 }
{ "appName" : "omp", "maxStatus" : 2 }
{ "appName" : "network", "maxStatus" : 2 }
{ "appName" : "amp", "maxStatus" : 2 }
{ "appName" : "acc", "maxStatus" : 2 }
{ "appName" : "js_server_demo", "maxStatus" : 2 }
{ "appName" : "rbac", "maxStatus" : 2 }
{ "appName" : "sc", "maxStatus" : 2 }
{ "appName" : "upf", "maxStatus" : 2 }
{ "appName" : "ikejcwang", "maxStatus" : 2 }
{ "appName" : "atm", "maxStatus" : 2 }

1.3.7、其他

1、ObjectId查询

db.service.find({"svcObjectID":ObjectId("62c3e52e79a2a529fb51b80d")})

db.service.find({"svcObjectID":{"$in":[ObjectId("62c3e52e79a2a529fb51b80d"), ObjectId("62c3e52e79a2a52hehehehehehehe"),ObjectId("62c3e52e79a2a529hahahahaha")]})

2、字段内容为空查询(不是字段不存在),除了$exists,支持直接传null

db.service.find({"svcObjectID":null})

1.4、索引

查看索引

1、查看当前索引:db.colName.getIndexes()

2、查询当前索引的大小:db.colName.totalIndexSize()

> db.acs_action.getIndexes()
[
        {
                "v" : 2,
                "unique" : true,
                "key" : {
                        "appid" : 1,
                        "code" : 1
                },
                "name" : "appid_1_code_1"
        }
]
> db.acs_action.totalIndexSize()
110592

删除索引

1、删除所有索引:db.colName.dropIndexes()

2、删除指定的索引,指定name:db.colName.dropIndex("indexName")

创建索引

db.colName.createIndex(keys, options)

  • keys:需要创建的索引字段,1升序创建,-1降序创建,包含组合索引;

    单一索引:

    db.colName.createIndex({"id": 1})

    复合索引:

    db.colName.createIndex({"id": 1, "createTime": -1})

  • options:可选参数配置,非必填,可选参数列表如下:

    Parameter Type Description
    background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false
    unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
    name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
    sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
    expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
    v index version 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
    weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
    default_language string 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
    language_override string 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.
> db.user.createIndex({"name":1, "age": -1}, {unique: true})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

2、应用篇

2.1、mongo-driver

mongoDB官方提供了golang语言开发的驱动包:mongo-driver,但是用起来比较繁琐,操作上有代码叠加场景,这里简单的对其二次封装处理,使单表的增删改查,批量处理,分页……多表的聚合查询,用起来更简单。

2.2、封装思路

  1. 抽出一个操作数据库的接口,用来做抽象处理;
  2. mongo的每一个库的每一张集合,都会对应一个golang的结构体对象,来做唯一映射,即满足单表所有场景下的增删改查操作;
  3. 提供初始化函数和创建集合对象的函数,用于项目启动时的加载项;

2.3、实现方式

1、提供一个BaseCollection接口;

package collection

import "context"

//
//  BaseCollection
//  @Description: 定义操作的接口
//
type BaseCollection interface {

    //
    //  SelectPage
    //  @Description: 分页查询
    //  @param ctx
    //  @param filter
    //  @param sort
    //  @param skip
    //  @param limit
    //  @return int64
    //  @return []interface{}
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 16:21:08
    SelectPage(ctx context.Context, filter interface{}, sort interface{}, skip, limit int64) (int64, []interface{}, error)

    //
    //  SelectList
    //  @Description: 查询列表
    //  @param ctx
    //  @param filter
    //  @param sort
    //  @return []interface{}
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 16:21:51
    SelectList(ctx context.Context, filter interface{}, sort interface{}) ([]interface{}, error)

    //
    //  SelectOne
    //  @Description: 查询单条
    //  @param ctx
    //  @param filter
    //  @return interface{}
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 16:21:56
    SelectOne(ctx context.Context, filter interface{}) (interface{}, error)

    //
    //  SelectCount
    //  @Description: 查询统计
    //  @param ctx
    //  @param filter
    //  @return int64
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:36:45
    SelectCount(ctx context.Context, filter interface{}) (int64, error)

    //
    //  UpdateOne
    //  @Description: 更新单条
    //  @param ctx
    //  @param filter
    //  @param update
    //  @return int64
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:37:58
    UpdateOne(ctx context.Context, filter, update interface{}) (int64, error)

    //
    //  UpdateMany
    //  @Description: 更新多条
    //  @param ctx
    //  @param filter
    //  @param update
    //  @return int64
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:38:26
    UpdateMany(ctx context.Context, filter, update interface{}) (int64, error)

    //
    //  Delete
    //  @Description: 根据条件删除
    //  @param ctx
    //  @param filter
    //  @return int64
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:38:53
    Delete(ctx context.Context, filter interface{}) (int64, error)

    //
    //  InsetOne
    //  @Description: 插入单条
    //  @param ctx
    //  @param model
    //  @return interface{}
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:39:38
    InsetOne(ctx context.Context, model interface{}) (interface{}, error)

    //
    //  InsertMany
    //  @Description: 插入多条
    //  @param ctx
    //  @param models
    //  @return []interface{}
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:40:25
    InsertMany(ctx context.Context, models []interface{}) ([]interface{}, error)

    //
    //  Aggregate
    //  @Description: 聚合查询
    //  @param ctx
    //  @param pipeline
    //  @param result
    //  @return error
    //  @author: ikejcwang
    //  @create: 2022-10-04 14:46:54
    Aggregate(ctx context.Context, pipeline interface{}, result interface{}) error
  
    //
  //  CreateIndexes
  //  @Description: 创建索引,用于初始化时调用
  //  @param ctx
  //  @param indexes
  //  @return error
  //  @author: ikejcwang
  //  @create: 2022-10-06 13:23:20
    CreateIndexes(ctx context.Context, indexes []mongo.IndexModel) error

    //
  //  GetCollection
  //  @Description: 获取当前的*mongo.Collection对象
  //  @return *mongo.Collection
  //  @author: ikejcwang
  //  @create: 2022-10-04 17:04:45
    GetCollection() *mongo.Collection
}

2、创建BaseCollection的结构体,且实现上述接口;

package collection

import (
    "context"
    "fmt"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

//
//  BaseCollectionImpl
//  @Description: collection 的实现
//
type BaseCollectionImpl struct {
    DbName     string
    ColName    string
    DataBase   *mongo.Database
    Collection *mongo.Collection
}

func (b *BaseCollectionImpl) SelectPage(ctx context.Context, filter interface{}, sort interface{}, skip, limit int64) (int64, []interface{}, error) {
    var err error

    resultCount, err := b.Collection.CountDocuments(ctx, filter)
    if err != nil {
        return 0, nil, err
    }

    opts := options.Find().SetSort(sort).SetSkip(skip).SetLimit(limit)
    finder, err := b.Collection.Find(ctx, filter, opts)
    if err != nil {
        return resultCount, nil, err
    }

    result := make([]interface{}, 0)
    if err := finder.All(ctx, &result); err != nil {
        return resultCount, nil, err
    }
    return resultCount, result, nil
}

func (b *BaseCollectionImpl) SelectList(ctx context.Context, filter interface{}, sort interface{}) ([]interface{}, error) {
    var err error

    opts := options.Find().SetSort(sort)
    finder, err := b.Collection.Find(ctx, filter, opts)
    if err != nil {
        return nil, err
    }

    result := make([]interface{}, 0)
    if err := finder.All(ctx, &result); err != nil {
        return nil, err
    }
    return result, err
}

func (b *BaseCollectionImpl) SelectOne(ctx context.Context, filter interface{}) (interface{}, error) {
    result := new(interface{})
    err := b.Collection.FindOne(ctx, filter, options.FindOne()).Decode(result)
    if err != nil {
        return nil, err
    }
    return result, nil
}

func (b *BaseCollectionImpl) SelectCount(ctx context.Context, filter interface{}) (int64, error) {
    return b.Collection.CountDocuments(ctx, filter)
}

func (b *BaseCollectionImpl) UpdateOne(ctx context.Context, filter, update interface{}) (int64, error) {
    result, err := b.Collection.UpdateOne(ctx, filter, update, options.Update())
    if err != nil {
        return 0, err
    }
    if result.MatchedCount == 0 {
        return 0, fmt.Errorf("Update result: %s ", "document not found")
    }
    return result.MatchedCount, nil
}

func (b *BaseCollectionImpl) UpdateMany(ctx context.Context, filter, update interface{}) (int64, error) {
    result, err := b.Collection.UpdateMany(ctx, filter, update, options.Update())
    if err != nil {
        return 0, err
    }
    if result.MatchedCount == 0 {
        return 0, fmt.Errorf("Update result: %s ", "document not found")
    }
    return result.MatchedCount, nil
}

func (b *BaseCollectionImpl) Delete(ctx context.Context, filter interface{}) (int64, error) {
    result, err := b.Collection.DeleteMany(ctx, filter, options.Delete())
    if err != nil {
        return 0, err
    }
    if result.DeletedCount == 0 {
        return 0, fmt.Errorf("DeleteOne result: %s ", "document not found")
    }
    return result.DeletedCount, nil
}

func (b *BaseCollectionImpl) InsetOne(ctx context.Context, model interface{}) (interface{}, error) {
    result, err := b.Collection.InsertOne(ctx, model, options.InsertOne())
    if err != nil {
        return nil, err
    }
    return result.InsertedID, err
}

func (b *BaseCollectionImpl) InsertMany(ctx context.Context, models []interface{}) ([]interface{}, error) {
    result, err := b.Collection.InsertMany(ctx, models, options.InsertMany())
    if err != nil {
        return nil, err
    }
    return result.InsertedIDs, err
}

func (b *BaseCollectionImpl) Aggregate(ctx context.Context, pipeline interface{}, result interface{}) error {
    finder, err := b.Collection.Aggregate(ctx, pipeline, options.Aggregate())
    if err != nil {
        return err
    }
    if err := finder.All(ctx, &result); err != nil {
        return err
    }
    return nil
}

func (b *BaseCollectionImpl) CreateIndexes(ctx context.Context, indexes []mongo.IndexModel) error {
    _, err := b.Collection.Indexes().CreateMany(ctx, indexes, options.CreateIndexes())
    return err
}

func (b *BaseCollectionImpl) GetCollection() *mongo.Collection {
    return b.Collection
}

3、提供初始化函数,创建collection对象函数;

package collection

import (
    "context"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/mongo/readpref"
    "time"
)

var (
    MongoClient *mongo.Client
)

const (
    defaultTimeout = 50 * time.Second
    maxPoolSize    = 10
)

//
//  InitMongo
//  @Description: 初始化mongo
//  @param mongoUrl
//  @return error
//  @author: ikejcwang
//  @create: 2022-10-04 15:27:55
func InitMongo(mongoUrl string) error {
    var err error

    ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
    defer cancel()

    MongoClient, err = mongo.Connect(ctx, options.Client().ApplyURI(mongoUrl).SetMaxPoolSize(maxPoolSize))
    if err != nil {
        return err
    }

    if err := MongoClient.Ping(ctx, readpref.Primary()); err != nil {
        return err
    }
    return nil
}

//
//  CreateMongoCollection
//  @Description: 创建mongo集合的服务
//  @param dbName
//  @param colName
//  @return BaseCollection
//  @return error
//  @author: ikejcwang
//  @create: 2022-10-04 15:15:14
func CreateMongoCollection(dbName, colName string) BaseCollection {
    dataBase := MongoClient.Database(dbName)
    return &BaseCollectionImpl{
        DbName:     dbName,
        ColName:    colName,
        DataBase:   dataBase,
        Collection: dataBase.Collection(colName),
    }
}

2.4、场景篇

为了演示简单处理,其实mongoDB的配置应该用.conf文件的方式引入,且可以丰富选项;

1、假设目前有一个库,一张表,那就创建一个baseCollection对象,有多个就创建多个,放在程序初始化的入口那里;

2、一个baseCollection对象,包含着该集合的绝大多数数据操作(丰富的增删改查);

3、同时也提供初始化索引的API,因为mongoDB的数据库,集合无需主动创建,引用到了,就是创建了。故把索引结构初始化的逻辑也放到应用程序这一侧

2、如果有特殊的业务处理baseCollection里面封装的函数没有覆盖到,需要原始API,例如初始化时创建各种类型的索引,它也提供着原始的*mongo.Collection的属性,可以直接调用它来操作,类似于:appCollection.GetCollection()


var (
    appCollection  *mongo.Collection
    userCollection *mongo.Collection
    // ……多个
)

//  
//  Init
//  @Description: 初始化mongo
//  @param mongoUrl
//  @author: ikejcwang
//  @create: 2022-10-06 13:40:05
func Init(mongoUrl string) error {

    if err := collection.InitMongo(mongoUrl); err != nil {
        panic(err)
    }
    ctx := context.TODO()

    //  逐一初始化集合对象
    userCollection := collection.CreateMongoCollection("test", "user")
    appCollection := collection.CreateMongoCollection("test", "user")

    // 依次给集合初始化索引
    indexes := []mongo.IndexModel{
        {
            Keys:    bson.D{{"name", 1}, {"age", -1}},
            Options: options.Index().SetUnique(true),
        },
    }
    if err := userCollection.CreateIndexes(ctx, indexes); err != nil {
        fmt.Printf("init indexes error: %v", err.Error())
        return err
    }
    //  ………………多个
    
    return nil
}

2.5、测试

1、创建一个集合的结构体,属性映射到所有集合字段上,同时提供bson格式化转换的函数;

//
//  UserDto
//  @Description: app集合的字段映射
//
type UserDto struct {
    Name string `json:"name" bson:"name"`
    Age  int64  `json:"age" bson:"age"`
}

func (a *UserDto) BsonByte(item interface{}) {
    bytes, err := bson.Marshal(item)
    if err != nil {
        panic(err)
    }
    bson.Unmarshal(bytes, a)
}

2、测试,初始化Mongo客户端连接,创建bsonCollection对象,写查询条件,调用对应函数,格式化结果集,输出结果集;

func main() {

    if err := collection.InitMongo("mongodb://user:password@9.13.28.88:27017/?authSource=admin"); err != nil {
        panic(err)
    }
    ctx := context.TODO()

    //  初始化user的集合对象
    userCollection := collection.CreateMongoCollection("test", "user")

    // 初始化索引
    indexes := []mongo.IndexModel{
        {
            Keys:    bson.D{{"name", 1}, {"age", -1}},
            Options: options.Index().SetUnique(true),
        },
    }
    if err := userCollection.CreateIndexes(ctx, indexes); err != nil {
        fmt.Printf("init indexes error: %v", err.Error())
    }

    filter := bson.M{
        "name": bson.M{
            "$regex":   "ike",
            "$options": "i",
        },
    }
    list, err := userCollection.SelectList(ctx, filter, nil)
    if err != nil {
        panic(err)
    }

    userList := make([]*UserDto, 0)
    for _, item := range list {
        app := new(UserDto)
        app.BsonByte(item)
        userList = append(userList, app)
    }

    fmt.Printf("userList: length:%v\n", len(userList))
    bytes, _ := json.Marshal(userList)
    fmt.Printf("userList: data:%v\n", string(bytes))
}

3、执行:go run xxx.go

wangjinchao@IKEJCWANG-MB0 14_ike_goMongo % go run test/test.go
userList: length:6
userList: data:[{"name":"ikejcwang","age":35},{"name":"ikejcwang","age":34},{"name":"ikejcwang","age":33},{"name":"ikejcwang","age":32},{"name":"ikejcwang","age":31},{"name":"ikejcwang","age":0}]

2.6、应用

类似java web框架开发那种分层处理,这里封装的是orm层(dao层),直接对接数据库,前面应该还有controller,service……

3、自动化部署篇

未完待续……

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
2月前
|
存储 缓存 NoSQL
MongoDB 是什么?有哪些应用场景?
MongoDB 是一个由 MongoDB Inc. 开发的基于分布式文件存储的面向文档的数据库,自 2009 年推出以来,以其高性能、易部署、模式自由、强大的查询语言和出色的可扩展性受到广泛欢迎。它适用于互联网应用、日志分析、缓存、地理信息系统等多种场景。MongoDB 支持多种编程语言,并提供了丰富的社区支持,便于开发者快速上手。结合板栗看板等工具,MongoDB 可进一步提升数据存储、分析和同步的效率,支持个性化功能实现,助力团队协作和项目管理。
|
3月前
|
存储 监控 NoSQL
MongoDB以其独特的优势和广泛的应用场景
MongoDB以其独特的优势和广泛的应用场景
93 8
|
2月前
|
存储 NoSQL 物联网
这些案例展示了MongoDB在不同行业中的广泛应用
这些案例展示了MongoDB在不同行业中的广泛应用
148 4
|
2月前
|
存储 NoSQL 物联网
MongoDB在多个行业有广泛应用
MongoDB在多个行业有广泛应用
87 4
|
3月前
|
存储 监控 NoSQL
MongoDB的应用场景非常广泛
MongoDB的应用场景非常广泛
98 6
|
2月前
|
存储 监控 NoSQL
MongoDB在不同行业中的广泛应用
MongoDB在不同行业中的广泛应用
119 3
|
3月前
|
存储 NoSQL 物联网
MongoDB在哪些领域有应用?
MongoDB在哪些领域有应用?
74 3
|
2月前
|
JavaScript NoSQL 前端开发
使用 Node.js 和 MongoDB 构建实时聊天应用
【10月更文挑战第2天】使用 Node.js 和 MongoDB 构建实时聊天应用
|
4月前
|
人工智能 NoSQL atlas
MongoDB Atlas与大语言模型的梦幻联动:如何瞬间提升企业级AI应用的构建效率?
【8月更文挑战第8天】在大数据时代,企业需挖掘数据价值。MongoDB Atlas作为云端数据库服务,以灵活性著称,减轻运维负担并支持全球数据分布。大语言模型(LLMs)革新AI构建方式,擅长处理自然语言。本文通过对比展示如何整合Atlas与LLMs,构建高效企业级AI应用:Atlas确保数据高效存储管理,LLMs提供语言理解与生成能力,二者结合加速AI应用开发并激发创新潜能。
70 1
|
4月前
|
Java 前端开发 Spring
技术融合新潮流!Vaadin携手Spring Boot、React、Angular,引领Web开发变革,你准备好了吗?
【8月更文挑战第31天】本文探讨了Vaadin与Spring Boot、React及Angular等主流技术栈的最佳融合实践。Vaadin作为现代Java Web框架,与其他技术栈结合能更好地满足复杂应用需求。文中通过示例代码展示了如何在Spring Boot项目中集成Vaadin,以及如何在Vaadin项目中使用React和Angular组件,充分发挥各技术栈的优势,提升开发效率和用户体验。开发者可根据具体需求选择合适的技术组合。
86 0