MongoDB学习之路——基础

一、相关概念

MongoDB中没有表和记录的概念,也不支持外键、事务、数据类型约定等,MongoDB中的表是集合(Collection),记录是文档(Document),MongoDB中一个数据库包含多个集合,一个集合包含多个文档。

二、启动Mongo服务

MongoDB的服务配置请自行百度。

  • 使用命令
    按下Win+R,输入cmd,在命令行中直接启动:1. 启动mongoDB:net start mongoDB 2. 停止mongoDB:net stop mongoDB
  • 在搜索框中输入service,找到MongoDB服务,手动开启
  • 进入MongoDB按照目录的bin文件夹下,输入mongo启动服务

三、数据库相关操作

命令 功能
show dbs 显示所有数据库(或show databases)
use admin 使用数据库admin,不存在时会自动创建数据库
db 当前所处的数据库
db.dropDatabase() 销毁数据库

四、集合的相关操作

语法:db.createCollection(name,options)
name:创建的集合名称
options:是一个作为初始化的文档(可选),包含的参数如下:

  • capped:类型为 Boolean,如果为 true 则创建一个固定大小的集合,当其条目达到最大时可以自动覆盖以前的条目。在设置其为 true 时也要指定参数大小;
  • autoIndexId:类型为 Boolean,默认为 false,如果设置为 true,则会在 _id 字段上自动创建索引;
  • size:如果 capped 为 true 则需要指定,指定参数的最大值,单位为 byte;
  • max:指定最大的文档数。
db.createCollection("users"):创建集合users
db.createCollection("users", { capped : 1, autoIndexId : 1, size : 6142800, max : 10000 } ) #带参数
show collections:显示数据库中所有的集合
db.users.drop():删除该数据库中的集合users

五、文档的相关操作

1.插

插入有insert跟save,insert 不能插入一条已经存在的记录,如果已经有了一条记录(以主键为准),insert 操作会报错,而使用 save 指令则会更新原记录。若集合不存在会自动创建集合。

> use users
switched to db users
> db.users.insert({"name":"aaa","age":18,"sex":"male"}) # insert插入一条数据
WriteResult({ "nInserted" : 1 })
> db.users.insert([{"name":"bbb","age":19,"sex":"male"},{"name":"ccc","age":25,"sex":"female"}]) #插入多条数据时要将数据用[]括起来
BulkWriteResult({
       "writeErrors" : [ ],
       "writeConcernErrors" : [ ],
       "nInserted" : 2,
       "nUpserted" : 0,
       "nMatched" : 0,
       "nModified" : 0,
       "nRemoved" : 0,
       "upserted" : [ ]
})
> doc1=({"user_id":1,"name":"ddd","age":20})
{ "user_id" : 1, "name" : "ddd", "age" : 20 }
> doc2=({"user_id":2,"name":"eee","age":30})
{ "user_id" : 2, "name" : "eee", "age" : 30 }
> db.users.insert([doc1,doc2]) # 直接插入多个文档
BulkWriteResult({
       "writeErrors" : [ ],
       "writeConcernErrors" : [ ],
       "nInserted" : 2,
       "nUpserted" : 0,
       "nMatched" : 0,
       "nModified" : 0,
       "nRemoved" : 0,
       "upserted" : [ ]
})

2.查

(1)AND

语法:db.users.find({ key1: value1, key2: value2 }).pretty() #{}内部是查找的条件,是AND的关系
等价于SQL语句:select * from users where key1=value1 and key2=value2;

db.users.find() # 查询所有数据
db.users.find().pretty() # 使查询输出的结果更美观

(2)OR

语法:db.users.find({$or:[{key1: value1}, {key2: value2}]}).pretty()#[]内部是OR的关系
等价于SQL语句:select * from users where key1=value1 or key2=value2;

> db.users.find({$or:[{"name":"aaa"},{"name":"bbb"}]})# 查询name为aaa或者name为bbb的文档
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }

(3)AND+OR

等价于SQL语句:select * from users where age>18 and (name="bbb" or sex="female");

> db.users.find({# 第一重花括号是AND的关系
... "age":{$gt:18}, # age大于18
... $or:[ # []内部是OR的关系
... {"name":"bbb"},
... {"sex":"female"}
... ]
... })
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }

(4)条件查询

语法:$condition:[key]
可选的condition值如下:

  • ne:不等于 not equal。
  • gt:大于 greater than
  • lt:小于 less than
  • gte:大于或等于 greater than equal
  • lte:小于或等于 less than equal
> db.users.find({"age":{$gt:20}})
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }

语法:$type:[key]
可选的 key 值如下:

  • 1: 双精度型(Double)
  • 2: 字符串(String)
  • 3: 对象(Object)
  • 4: 数组(Array)
  • 5: 二进制数据(Binary data)
  • 7: 对象 ID(Object id)
  • 8: 布尔类型(Boolean)
  • 9: 数据(Date)
  • 10: 空(Null)
  • 11: 正则表达式(Regular Expression)
  • 13: JS 代码(Javascript)
  • 14: 符号(Symbol)
  • 15: 有作用域的 JS 代码(JavaScript with scope)
  • 16: 32 位整型数(32-bit integer)
  • 17: 时间戳(Timestamp)
  • 18: 64 位整型数(64-bit integer)
  • -1: 最小值(Min key)
  • 127: 最大值(Max key)
> db.users.find({"age":{$type:1}}) # 查询所有age的type为double类型的文档
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }
{ "_id" : ObjectId("5d01dd8a5464632726bf70bb"), "user_id" : 1, "name" : "ddd", "age" : 20 }
> db.users.find({"age":{$type:"double"}}) # 与上面的功能相同
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }
{ "_id" : ObjectId("5d01dd8a5464632726bf70bb"), "user_id" : 1, "name" : "ddd", "age" : 20 }

(5)正则式查询

正则语法可以参考:https://www.runoob.com/regexp/regexp-syntax.html

> db.users.find({"name":/^a/}) # 查询name以a开头的文档
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
> db.users.find({"name":/b$/}) # 查询name以b结尾的文档
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }

(6)limit()与skip()

> db.users.find() # 所有文档
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }
{ "_id" : ObjectId("5d01dd8a5464632726bf70bb"), "user_id" : 1, "name" : "ddd", "age" : 20 }
> db.users.find().limit(2) # 查询两个文档
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
> db.users.find().limit(2).skip(1) # 跳过一个文档,即从第二个文档开始查询两个文档
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }

(7)sort()

语法:db.COLLECTION_NAME.find().sort({KEY:1|-1})
升序用 1 表示,降序用 -1 表示。

> db.users.find().sort({"age":-1}) # 按照age降序排列
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }
{ "_id" : ObjectId("5d01dd8a5464632726bf70bb"), "user_id" : 1, "name" : "ddd", "age" : 20 }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }

3.更新

语法:db.COLLECTION_NAME.update(SELECTION_CRITERIA,UPDATED_DATA,MULTI)
一共有三个参数:
第一个大括号内容标示查找条件,第二个大括号内容则表示更新后的数据
默认的 update 函数只对一个文档更新,如果想作用所有文档,则需要加入 multi:true

> db.users.update({"user_id":2},{$set:{"age":35,"sex":"male"}},{multi:true}) # 将user_id为2的文档的age改为32,sex改为male
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find({"user_id":2})
{ "_id" : ObjectId("5d01dd8a5464632726bf70bc"), "user_id" : 2, "name" : "eee", "age" : 35, "sex" : "male" }
>

4.替换已经存在的文档

语法:db.COLLECTION_NAME.save({_id:ObjectId(),NEW_DATA})

> db.users.save({"_id":ObjectId("5d01dd8a5464632726bf70bc"),"name":"kkk","city":"hubei"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.find({"name":"kkk"})
{ "_id" : ObjectId("5d01dd8a5464632726bf70bc"), "name" : "kkk", "city" : "hubei" }

5.删除文档

语法:db.COLLECTION_NAME.remove(DELECTION_CRITERIA)

> db.users.remove({"name":"kkk"})
WriteResult({ "nRemoved" : 1 })

六、索引

语法:db.COLLECTION_NAME.createIndex({KEY:1|-1},{OPTIONS})
1 代表升序,-1 代表降序。
ensureIndex() 的可选参数:

参数 类型 描述
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false。
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups Boolean 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
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.users.createIndex({"age":1})
db.users.createIndex({"user_id":1,"name":1},{background:1}) # 在后台创建索引

七、聚合(Aggregate)

语法:db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
聚合的表达式(表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档):

表达式 描述 实例
$sum 计算总和。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])`
$avg 计算平均值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max 获取集合中所有文档对应值得最大值 。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])`
$push 在结果文档中插入值到一个数组中。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])
> db.users.find()
{ "_id" : ObjectId("5d01d55e5464632726bf70b8"), "name" : "aaa", "age" : 18, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70b9"), "name" : "bbb", "age" : 19, "sex" : "male" }
{ "_id" : ObjectId("5d01d6365464632726bf70ba"), "name" : "ccc", "age" : 25, "sex" : "female" }
{ "_id" : ObjectId("5d01dd8a5464632726bf70bb"), "name" : "ddd", "age" : 20, "sex" : "male" }
> db.users.aggregate([{$group:{"_id":"$sex","total":{$sum:1}}}]) #通过sex字段对文档进行分组,{$sum:1}表示出现了一条记录就加1
{ "_id" : "female", "total" : 1 }
{ "_id" : "male", "total" : 3 }
> db.users.aggregate([{$group:{"_id":"$sex","total":{$sum:"$age"}}}])#对sex相同的文档的age进行累加
{ "_id" : "female", "total" : 25 }
{ "_id" : "male", "total" : 57 }
> db.users.aggregate([{$group:{"_id":null,"total":{$sum:"$age"}}}])#_id为null时是统计所有文档的age之和
{ "_id" : null, "total" : 82 }

八、管道

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。聚合框架中常用的几个操作有:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。类似于SQL中的投影操作,默认输出_id,若为0表示不输出,为1表示输出
  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
    --$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。_id字段是必填的;但是,可以指定_id值为null来为整个输入文档计算累计值。
    语法:{ $group: { _id: , : { : }, ... } }
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。

下面等价于SQL语句:select age,count(*) as total from users where sex='male' group by age

db.users.aggregate([
  {
    $match:{"sex":"male"} #$match放在$group前面类似于where语句,将匹配match的文档传送到下一个管道
  },
  {
    $group: {# 分组后只有_id与total两个属性了
        "_id": "$age",
        "total": {
            $sum: 1
        }
    }
 }
])

下面等价于SQL语句:select sex, count(*) as total from users group by sex having total>1

db.users.aggregate([
  {
    $group: {# 将分组结果传送到下一个管道
        "_id": "$sex",
        "total": {
            $sum: 1
        }
    }
 },
  {
    $match:{total:{$gt:1}} # match放在group后面类似于having条件
  }
])

下面等价于SQL语句:select sum(age) as total from users group by sex having total>10;

db.users.aggregate([
  {
    $group: {
        "_id": "$sex", #按照sex分组
        "total": {# 计算每组中age的和,赋值给total
            $sum: "$age"
        }
    }
 },
  {
  $match:{"total":{$gt:10}}#匹配total大于10的文档
  } ,
  {
      $project:{"_id":0,"total":1}#只投影total,不投影_id
  }
])
参考文档
  1. https://www.shiyanlou.com/courses/12
  2. https://www.runoob.com/mongodb/mongodb-aggregate.html
  3. https://www.cnblogs.com/zhoujie/p/mongo1.html
记录自己的MongoDB学习之路,错误的地方欢迎指正~~
上一篇:Day2 - 基于ECS快速搭建Docker环境


下一篇:linux系统移植流程