Mongo DB初学

关于Mongo DB

MongoDB简介

MongoDB 是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

想初步了两种数据库的不同可以读一下关系型数据库 VS 非关系型数据库 - 知乎 (zhihu.com)

MongoDB特点

  • 面向集合存储,易存储对象类型的数据。
  • 模式自由
  • 支持动态查询
  • 可通过网络访问
  • 支持查询
  • 支持复制和故障恢复
  • 支持完全索引,包含内部对象
  • 文件存储格式为BSON(一种JSON的扩展)
  • 自动处理碎片,以支持云计算层次的扩展性
  • 使用高效的二进制数据存储,包括大型对象(如视频等)
  • 支持 Golang,RUBY,PYTHON,JAVA,C++,PHP,C# 等多种语言
  • MongoDB安装简单。

MongoDB 适用场景

MongoDB 的主要目标是在键/值存储方式(提供了高性能和高度伸缩性)和传统的RDBMS 系统(具有丰富的功能)之间架起一座桥梁,它集两者的优势于一身。根据官方网站的描述,Mongo 适用于以下场景。

  • 网站数据:Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
  • 缓存:由于性能很高,Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo 搭建的持久化缓存层可以避免下层的数据源过载。
  • 高伸缩性的场景:Mongo 非常适合由数十或数百台服务器组成的数据库,Mongo 的路线图中已经包含对MapReduce 引擎的内置支持。
  • 用于对象及JSON 数据的存储:Mongo 的BSON 数据格式非常适合文档化格式的存储及查询。

MongoDB 的使用也会有一些限制,例如,它不适合于以下几个地方。

  • 高度事务性的系统:例如,银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。
  • 传统的商业智能应用:针对特定问题的BI 数据库会产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。
  • 需要SQL 的问题。

MangoDB安装,启动,权限管理

在CentOS 7上安装MongoDB

MangoDB概念解析

mongodb中基本的概念是文档、集合、数据库,下面我们挨个介绍。

SQL术语/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
columnfield数据字段/域
indexindex索引
table joins表连接,MongoDB不支持
primary keyprimary key主键,MongoDB自动将_id字段设置为主键

数据库

一个mongodb中可以建立多个数据库。

MongoDB的默认数据库为"db",该数据库存储在data目录中。

MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

[hms@beijing-231-32.dx.edge.zw.corp.yodao.com ~]$ mongo -u hms -p --authenticationDatabase admin
MongoDB shell version: 3.2.22
Enter password: 
connecting to: test
> show dbs				# "show dbs" 命令可以显示所有数据的列表。
admin  0.000GB
local  0.000GB
> db				    # 执行 "db" 命令可以显示当前数据库对象或集合。
test
> use local				# 运行"use"命令,可以连接到一个指定的数据库。
switched to db local
> db
local
> 

数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。

  • 不能是空字符串("")
  • 不得含有' '(空格)、.、$/\和\0 (空宇符)。
  • 应全部小写。
  • 最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

文档

文档是一个键值(key-value)对(即BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

# 一个简单的文档例子
{"name":"han", "time":"2022/8/30 14:42"}

RDBMS 与 MongoDB 对应的术语:

RDBMSMongoDB
数据库数据库
表格集合
文档
字段
表联合嵌入文档
主键主键 (MongoDB 提供了 key 为 _id )

需要注意的是:

  1. 文档中的键/值对是有序的。
  2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  3. MongoDB区分类型和大小写。
  4. MongoDB的文档不能有重复的键。
  5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

  • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
  • .$有特别的意义,只有在特定环境下才能使用。
  • 以下划线"_"开头的键是保留的(不是严格要求的)。

集合

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。

集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

# 一个简单的集合例子
{"name":"han"}
{"name":"coke", "time":"2022/8/30 14:42"}
{"name":"soda", "time":"2022/8/30 14:42","num":7}
# 当第一个文档插入时,集合就会被创建。

合法的集合名

  • 集合名不能是空字符串""。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

元数据

数据库的信息是存储在集合中。它们使用了系统的命名空间:

dbname.system.*

在MongoDB数据库中名字空间 < dbname >.system.* 是包含多种系统信息的特殊集合,如下:

集合命名空间描述
dbname.system.namespaces列出所有名字空间。
dbname.system.indexes列出所有索引。
dbname.system.profile包含数据库概要(profile)信息。
dbname.system.users列出所有可访问数据库的用户。
dbname.local.sources包含复制对端(slave)的服务器信息和状态。

对于修改系统集合中的对象有如下限制。

{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)。

{{system.users}}是可修改的。 {{system.profile}}是可删除的。

MongoDB 数据类型

数据类型描述
String字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean布尔值。用于存储布尔值(真/假)。
Double双精度浮点值。用于存储浮点值。
Min/Max keys将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Arrays用于将数组或列表或多个值存储为一个键。
Timestamp时间戳。记录文档修改或添加的具体时间。
Object用于内嵌文档。
Null用于创建空值。
Symbol符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID对象 ID。用于创建文档的 ID。
Binary Data二进制数据。用于存储二进制数据。
Code代码类型。用于在文档中存储 JavaScript 代码。
Regular expression正则表达式类型。用于存储正则表达式。

Mongo DB命令行操作

创建数据库

# MongoDB 创建数据库的语法格式如下:
use DATABASE_NAME
# 如果数据库不存在,则创建数据库,否则切换到指定数据库
# 实例
> use han
switched to db han
> db 
han
> show dbs
admin  0.000GB
local  0.000GB
> 
# 可以看到,我们刚创建的数据库 han 并不在数据库的列表中, 要显示它,我们需要向 han 数据库插入一些数据
> use han
switched to db han
> db.han.insert({"name":"hanms"})
WriteResult({ "nInserted" : 1 })
> show dbs
admin  0.000GB
han    0.000GB
local  0.000GB
> 
# MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中

删除数据库

# MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
# 删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名
# 实例
> show dbs
admin  0.000GB
han    0.000GB
local  0.000GB
> use test
switched to db test
> show dbs
admin  0.000GB
han    0.000GB
local  0.000GB
> use han
switched to db han
> db.dropDatabase()
{ "dropped" : "han", "ok" : 1 }
> show dbs
admin  0.000GB
local  0.000GB
> 

创建集合

# MongoDB 创建集合的语法格式如下:
db.createCollection(name, options)
# 实例
> use han
switched to db han
> db.createCollection("test1")		 #在 han 数据库中创建 test1 集合
{ "ok" : 1 }
> show collections					#如果要查看已有集合,可以使用 show collections 或 show tables 命令
test1
> show tables
test1
# 创建固定集合 test2,整个集合空间大小 6142800 B, 文档最大个数为 10000 个
> db.createCollection("test2", { capped : true, autoIndexId : true, size :6142800, max : 10000 } )
{
        "note" : "the autoIndexId option is deprecated and will be removed in a future release",
        "ok" : 1
}
> show collections
test1
test2
#在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。详细见插入文档小节

删除集合

# MongoDB 删除集合的语法格式如下:
db.collection.drop()
# 实例
> use test
switched to db test
> show collections
test1
test2
> db.test2.drop()
true
> show collections
test1
> 

插入文档

# MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.COLLECTION_NAME.insert(document)
db.COLLECTION_NAME.save(document)
# save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() # 来代替
# insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据
# 实例
> db.han.insert({"name":"hanms"})
WriteResult({ "nInserted" : 1 })
> db.han.find()
{ "_id" : ObjectId("630dc285d0726aa32ec74d7b"), "name" : "hanms" }
# 也可以将数据定义为一个变量
> test=({"name":"smnah"});
{ "name" : "smnah" }
> db.col.insert(test)
WriteResult({ "nInserted" : 1 })
# 不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合
> show tables
col
han
#插入文档也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据

更新文档

# MongoDB 使用 update() 和 save() 方法来更新集合中的文档
# update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
   <query>,		# query : update的查询条件,类似sql update查询内where后面的
   <update>,	# update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
   {
     upsert: <boolean>,	# upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
     multi: <boolean>,	# multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
     writeConcern: <document>	# writeConcern :可选,抛出异常的级别
   }
)
# 实例
> use han
switched to db han
> db.test1.insert({"name":"hanms"})
WriteResult({ "nInserted" : 1 })
> db.test1.update({'name':'hanms'},{$set:{'name':'smnah'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test1.find()
{ "_id" : ObjectId("630dcb4ad0726aa32ec74d7f"), "name" : "smnah" }
# 如果有多个文档修改同一字段,则需要设置 multi 参数为 true
>db.test1.update({'name':'hanms'},{$set:{'title':'smnah'}},{multi:true})

# save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入。语法格式如下:
db.collection.save(
   <document>,	# document : 文档数据
   {
     writeConcern: <document> # writeConcern :可选,抛出异常的级别
   }
)
# 实例
> db.test1.save({ "_id" : ObjectId("630dcb4ad0726aa32ec74d7f"), "name" : "update" })
> db.test1.find()
{ "_id" : ObjectId("630dcb4ad0726aa32ec74d7f"), "name" : "update" }

删除文档

# MongoDB remove() 函数是用来移除集合中的数据,基本语法格式如下所示:
db.collection.remove(
   <query>,
   <justOne>
)
# 如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:
db.collection.remove(
   <query>,	# query :(可选)删除的文档的条件
   {
     justOne: <boolean>, # justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档
     writeConcern: <document> # writeConcern :(可选)抛出异常的级别
   }
)
# 实例
# 以下文档我们执行两次插入操作:
> db.test1.insert({"name":"hanms"})
# 使用 find() 函数查询数据:
> db.test1.find()
{ "_id" : ObjectId("630dcb4ad0726aa32ec74d7f"), "name" : "smnah" }
# 接下来移除 name 为 'smnah' 的文档:
> db.test1.remove({'name':'smnah'})
WriteResult({ "nRemoved" : 2 })           # 删除了两条数据
> db.test1.find()
……                                        # 没有数据
# 如果只想删除第一条找到的记录可以设置 justOne 为 1,如下所示:
>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)
# 如果想删除所有数据,可以使用以下方式(类似常规 SQL 的 truncate 命令):
> db.test1.remove({})
> db.test1.find()
>

查询文档

# MongoDB 查询数据的语法格式如下:
db.collection.find(query, projection)
# query :可选,使用查询操作符指定查询条件
# projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
# 如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:
db.col.find().pretty()
# 实例
> db.test2.insert({
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "name" : "Mongo DB",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "hms",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
})
WriteResult({ "nInserted" : 1 })
> db.test2.find().pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "name" : "Mongo DB",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "hms",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
> db.test2.find()
{ "_id" : ObjectId("56063f17ade2f21f36b03133"), "name" : "Mongo DB", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "hms", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
# 除了 find() 方法之外,还有一个 findOne() 方法,它只返回一个文档。

如果你熟悉常规的SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:

操作格式范例RDBMS中的类似语句
等于{<key>:<value>}db.col.find({"by":"hms"}).pretty()where by = 'hms'
小于{<key>:{$lt:<value>}}db.col.find({"likes":{$lt:50}}).pretty()where likes < 50
小于或等于{<key>:{$lte:<value>}}db.col.find({"likes":{$lte:50}}).pretty()where likes <= 50
大于{<key>:{$gt:<value>}}db.col.find({"likes":{$gt:50}}).pretty()where likes > 50
大于或等于{<key>:{$gte:<value>}}db.col.find({"likes":{$gte:50}}).pretty()where likes >= 50
不等于{<key>:{$ne:<value>}}db.col.find({"likes":{$ne:50}}).pretty()where likes != 50
MongoDB AND 条件

MongoDBfind() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQLAND 条件。

语法格式如下:

> db.test2.find({key1:value1, key2:value2}).pretty()

实例

以下实例通过 byname 键来查询 hmsMongo DB 的数据

> db.test2.find({"by":"hms", "name":"Mongo DB"}).pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "name" : "Mongo DB",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "hms",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}

以上实例中类似于 WHERE 语句:WHERE by=‘hms’ AND name='Mongo DB ’

MongoDB OR 条件

MongoDB OR 条件语句使用了关键字 $or,语法格式如下:

>db.test2.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

实例

以下实例中,演示了查询键 by 值为 hms 或键 name值为 Mongo DB 的文档。

>db.test2.find({$or:[{"by":"hms"},{"name": "Mongo DB"}]}).pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "name" : "Mongo DB",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "hms",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
>
AND 和 OR 联合使用

以下实例演示了 ANDOR 联合使用,类似常规 SQL 语句为: ‘where likes>50 AND (by = ‘hms’ OR name= ‘Mongo DB’)’

> db.test2.find({"likes": {$gt:50}, $or: [{"by": "hms"},{"name": "Mongo DB"}]}).pretty()
{
        "_id" : ObjectId("56063f17ade2f21f36b03133"),
        "name" : "Mongo DB",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "hms",
        "tags" : [
                "mongodb",
                "database",
                "NoSQL"
        ],
        "likes" : 100
}
条件操作符

MongoDB中条件操作符有:

  • (>) 大于 - $gt
  • (<) 小于 - $lt
  • (>=) 大于等于 - $gte
  • (<= ) 小于等于 - $lte

实例参照上例使用

$type操作符

在本章节中,我们将继续讨论MongoDB中条件操作符 $type

$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。

MongoDB 中可以使用的类型如下表所示:

类型数字备注
Double1
String2
Object3
Array4
Binary data5
Undefined6已废弃。
Object id7
Boolean8
Date9
Null10
Regular Expression11
JavaScript13
Symbol14
JavaScript (with scope)15
32-bit integer16
Timestamp17
64-bit integer18
Min key255Query with -1.
Max key127

$type 实例

如果想获取 "test2" 集合中 titleString 的数据,可以使用以下命令:

db.test2.find({"title" : {$type : 2}})
或
db.test2.find({"title" : {$type : 'string'}})

读取/跳过指定数量的数据

# 如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。
# limit()方法基本语法如下所示:
>db.COLLECTION_NAME.find().limit(NUMBER)
# 除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。
# skip() 方法脚本语法格式如下:
>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
# 实例
# col 集合中的数据如下:
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP", "description" : "PHP" "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java", "description" : "Java " "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB", "description" : "MongoDB", "likes" : 100 }
# limit
> db.col.find({},{"title":1,_id:0}).limit(2)
{ "title" : "PHP" }
{ "title" : "Java" }
>
# skip
>db.col.find({},{"title":1,_id:0}).limit(1).skip(1)
{ "title" : "Java" }
>

排序

# sort()方法基本语法如下所示:
>db.COLLECTION_NAME.find().sort({KEY:1})
# 实例
# col 集合中的数据如下:
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP", "description" : "PHP" "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java", "description" : "Java " "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB", "description" : "MongoDB", "likes" : 100 }
# 实例演示了 col 集合中的数据按字段 likes 的降序排列:
>db.col.find({},{"title":1,_id:0}).sort({"likes":-1})
{ "title" : "PHP" }
{ "title" : "Java" }
{ "title" : "MongoDB" }
>

索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

MongoDB使用 createIndex() 方法来创建索引。

# createIndex()方法基本语法格式如下所示:
>db.collection.createIndex(keys, options)
# 实例
# 语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。
>db.col.createIndex({"title":1})
>
# createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。
>db.col.createIndex({"title":1,"description":-1})
>

createIndex() 接收可选参数,可选参数列表如下:

ParameterTypeDescription
backgroundBoolean建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false
uniqueBoolean建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
namestring索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDupsBoolean**3.0+版本已废弃。**在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
sparseBoolean对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSecondsinteger指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
vindex version索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weightsdocument索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_languagestring对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_overridestring对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.

实例

在后台创建索引:

db.values.createIndex({open: 1, close: 1}, {background: true})

通过在创建索引时加 background:true 的选项,让创建工作在后台执行

聚合

MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。

有点类似 SQL 语句中的 count(*)

MongoDB中聚合的方法使用aggregate()。

# aggregate() 方法的基本语法格式如下所示:
> db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
# 实例
# 集合中的数据如下:
{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by_user: 'soda',
   url: 'http://http://59-111-179-160.ydip.netease.com/soda-ksm/#/',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
},
{
   _id: ObjectId(7df78ad8902d)
   title: 'NoSQL Overview', 
   description: 'No sql database is very fast',
   by_user: 'soda',
   url: 'http://http://59-111-179-160.ydip.netease.com/soda-ksm/#/',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
},
{
   _id: ObjectId(7df78ad8902e)
   title: 'Neo4j Overview', 
   description: 'Neo4j is no sql database',
   by_user: 'Neo4j',
   url: 'http://www.neo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
},
# 现在我们通过以上集合计算每个作者所写的文章数,使用aggregate()计算结果如下:
> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{
   "result" : [
      {
         "_id" : "soda",
         "num_tutorial" : 2
      },
      {
         "_id" : "Neo4j",
         "num_tutorial" : 1
      }
   ],
   "ok" : 1
}
>
# 以上实例类似sql语句:
select by_user, count(*) from mycol group by by_user

下表展示了一些聚合的表达式:

表达式描述实例
$sum计算总和。db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { s u m : " sum : " sum:"likes"}}}])
$avg计算平均值db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { a v g : " avg : " avg:"likes"}}}])
$min获取集合中所有文档对应值得最小值。db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { m i n : " min : " min:"likes"}}}])
$max获取集合中所有文档对应值得最大值。db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", num_tutorial : { m a x : " max : " max:"likes"}}}])
$push将值加入一个数组中,不会判断是否有重复的值。db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : { p u s h : " push: " push:"url"}}}])
$addToSet将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入。db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", url : { a d d T o S e t : " addToSet : " addToSet:"url"}}}])
$first根据资源文档的排序获取第一个文档数据。db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", first_url : { f i r s t : " first : " first:"url"}}}])
$last根据资源文档的排序获取最后一个文档数据db.mycol.aggregate([{KaTeX parse error: Expected '}', got 'EOF' at end of input: …roup : {_id : "by_user", last_url : { l a s t : " last : " last:"url"}}}])

管道

管道在UnixLinux中一般用于将当前命令的输出结果作为下一个命令的参数。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

聚合框架中常用的几个操作:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。

管道操作符实例

1、$project实例

db.article.aggregate(
    { $project : {
        title : 1 ,
        author : 1 ,
    }}
 );

这样的话结果中就只还有_id,tilteauthor三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:

db.article.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        author : 1
    }});

2.$match实例

db.articles.aggregate( [
                        { $match : { score : { $gt : 70, $lte : 90 } } },
                        { $group: { _id: null, count: { $sum: 1 } } }
                       ] );

$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

3.$skip实例

db.article.aggregate(
    { $skip : 5 });

经过$skip管道操作符处理后,前五个文档被"过滤"掉。

复制(副本集)

MongoDB复制是将数据同步在多个服务器的过程。

复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。

复制还允许您从硬件故障和服务中断中恢复数据。

复制原理

mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。

mongodb各个节点常见的搭配方式为:一主一从、一主多从。

主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

MongoDB复制结构图如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWEllyZP-1662112981871)(C:\Users\hanms\Desktop\mongo复制.png)]

副本集特征:

  • N 个节点的集群
  • 任何节点可作为主节点
  • 所有写入操作都在主节点上
  • 自动故障转移
  • 自动恢复

实例

待补充,自己尝试给自己mongo玩崩了

分片

Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。

MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。

为什么使用分片

  • 复制所有的写入操作到主节点
  • 延迟的敏感数据会在主节点查询
  • 单个副本集限制在12个节点
  • 当请求量巨大时会出现内存不足。
  • 本地磁盘不足
  • 垂直扩展价格昂贵

Mongo DB C++操作

环境配置

​ 想在c++中操作你的数据库,需要安装驱动,或者说库mongocxx,个人感觉很麻烦,所以可以运行此脚本,或者一步一步自己在命令行执行,版本根据需要自行更改,我的起始目录是/home/hms,中间有些下载项,网上说未必需要,一开始没下载,就出错,所以我直接全下载,未必真对,但是好用

#!/bin/bash
eval "sudo yum install pkg-config openssl-devel cyrus-sasl-devel"
eval "wget https://github.com/mongodb/mongo-c-driver/releases/download/1.8.1/mongo-c-driver-1.8.1.tar.gz"
eval "tar xzf mongo-c-driver-1.8.1.tar.gz"
eval "cd mongo-c-driver-1.8.1"
eval "./configure"
eval "make"
eval "sudo make install"

eval "“curl -OL https://github.com/mongodb/mongo-cxx-driver/archive/r3.1.3.tar.gz”"
eval "tar -xzf r3.1.3.tar.gz"
eval "cd mongo-cxx-driver-r3.1.3/build"
eval "PKG_CONFIG_PATH=/usr/local/lib/pkgconfig cmake -DBSONCXX_POLY_USE_MNMLSTC=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local .."


eval "wget https://github.com/mongodb/libbson/releases/download/1.8.1/libbson-1.8.1.tar.gz"
eval "tar -zxvf libbson-1.8.1.tar.gz"
eval "cd libbson-1.8.1/"
eval "./configure"
eval "make"
eval "sudo make install"
eval "cd .."

eval "wget http://mirrors.kernel.org/gnu/autoconf/autoconf-2.69.tar.gz"
eval "tar -zxvf autoconf-2.69.tar.gz"
eval "cd autoconf-2.69/"
eval "./configure"
eval "make"
eval "sudo make install"
eval "cd .."

eval "wget http://ftp.gnu.org/gnu/automake/automake-1.15.1.tar.gz"
eval "tar -zxvf autoconf-2.69.tar.gz"
eval "cd autoconf-2.69/"
eval "./configure"
eval "make"
eval "sudo make install"

eval "sudo yum install m4"
eval "sudo yum install libtool"
eval "cd .."

eval "sudo make EP_mnmlstc_core"
eval "make && sudo make install"

eval "export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig"

验证程序,将以下源文件的文件名保存test.cpp 在任何目录下

#include <iostream>

#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/json.hpp>

#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>

int main(int, char**) {
    mongocxx::instance inst{};
    mongocxx::client conn{mongocxx::uri{}};

    bsoncxx::builder::stream::document document{};

    auto collection = conn["testdb"]["testcollection"];
    document << "hello" << "world";

    collection.insert_one(document.view());
    auto cursor = collection.find({});

    for (auto&& doc : cursor) {
        std::cout << bsoncxx::to_json(doc) << std::endl;
    }
}

// 编译
// g++ --std=c++11 test.cpp -o test $(pkg-config --cflags --libs libmongocxx)

// 运行后,可能会类似报这样的错
// ./test: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./test)
// ./test: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /usr/local/lib/libmongocxx.so._noabi)
// ./test: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /usr/local/lib/libbsoncxx.so._noabi)
// ./test: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /usr/local/lib/libsnappy.so.0)
// ./test: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /usr/local/lib/libsnappy.so.0)

// 命令行:strings /usr/lib64/libstdc++.so.6|grep GLIBCXX
// 查看是否缺少该版本
// 如果缺少例如6.0.21版本,/usr/local/lib64/目录下可能有比这个版本高的版本,用那个版本就行,如果没用高于该版本的就得更新gcc,6.0.21是gcc5.3
// 我使用的gcc8.3为6.0.25
// cp /usr/local/lib64/libstdc++.so.6.0.21 /usr/lib64/  
// cd /usr/lib64/
// rm -f libstdc++.so.6
// ln -s libstdc++.so.6.0.21 libstdc++.so.6
// [hms@beijing-231-32.dx.edge.zw.corp.yodao.com ~]$ ll libstdc*
// lrwxrwxrwx. 1 root root       19 4月   7 23:43 libstdc++.so.6 -> libstdc++.so.6.0.21
// -rwxr-xr-x. 1 root root   995840 11月 19 22:20 libstdc++.so.6.0.19
// -rwxr-xr-x. 1 root root 11093843 4月   7 23:39 libstdc++.so.6.0.21

// 假如头文件报错 可以去/usr/local/include 看一眼路径
// 正常应该头文件路径/usr/local/include/mongocxx/v_noabi/mongocxx是这个,我把里面的mongocxx挪出来就好了,未必正确,但是能解决问题
// bsoncxx同理

// 输出如下时,代表成功
// { "_id" : { "$oid" : "5a0c27a6e138232bc80c1852" }, "hello" : "world" }
// 可以去你的mongo中db.testcollection.find()看一下,能看的到

编译

​ 为什么什么都没开始,先谈编译,你可能会发现在安装 mongocxx 驱动的过程中会安装一个 libmongocxx.pc 文件,用于配合 pkg-config 使用。编译程序时请使用以下命令:

g++ --std=c++11 <input>.cpp $(pkg-config --cflags --libs libmongocxx)

​ 如果不安装需要怎么做呢?

​ 需要手动的在命令行或者你的 IDE 中设置头文件和库文件路径。假设 libmongocmongocxx 安装在 /usr/local,那么应该使用下面这样的命令去编译你的代码:

c++ --std=c++11 <input>.cpp
  -I/usr/local/include/mongocxx/v_noabi -I/usr/local/include/libmongoc-1.0 \
  -I/usr/local/include/bsoncxx/v_noabi -I/usr/local/include/libbson-1.0 \
  -L/usr/local/lib -lmongocxx -lbsoncxx

建立链接

当成功完成环境配置时,并且保证你的Mongo DB实例运行在本地27017端口,就可以开始对mongocxx的学习了

在建立任何连接之前,你需要创建且仅创建一个 mongocxx::instance 实例。这个实例必须在你整个程序周期中都存在

使用 mongocxx::client 类去连接一个正在运行的 mongoDB 实例。

你必须使用一个包含MongoDB URImongocxx:uri 实例指定待连接的主机,然后将其传递给 mongocxx::client 的构造函数。

mongocxx::uri 的默认构造函数会连接到运行在 localhost 27017 端口的服务:

mongocxx::instance instance{}; 
mongocxx::client client{mongocxx::uri{}};

等同于:

mongocxx::instance instance{}; 
mongocxx::uri uri("mongodb://localhost:27017");
mongocxx::client client(uri);

访问数据库

已经与本地Mongo DB建立链接了,那么下一步一定是访问数据库,当我们有一个链接到Mongo DB服务的 mongocxx::client 实例,就可以使用database()方法或者 [ ] 操作符操作获得一个 mongocxx::database 实例

如果访问的数据库不存在,MongoDB 会在你第一次存储数据时创建这个数据库

database()方法实例

mongocxx::database db = client["mydb"];

[ ] 操作符实例,看环境配置中的验证程序就可以

访问集合

一旦有了一个 mongocxx::database 实例,就可以使用 collection()方法或者 [ ] 操作获得一个 mongocxx::collection 实例。

如果访问的集合不存在,MongoDB 会在你第一次存储数据时创建这个集合。

例如,使用之前段落中创建的 db 实例,下面代码会访问 mydb 中的 test 集合:

mongocxx::collection coll = db["test"];

[ ] 操作符实例,看环境配置中的验证程序就可以

插入单个文档

对于在集合中插入单个文档,使用 mongocxx::collection 实例的 insert_one() 方法:

bsoncxx::stdx::optional<mongocxx::result::insert_one> result =
 restaurants.insert_one(doc);

如果没有在文档中手动指定 _id 字段,MongoDB 会自动的在插入的文档中添加一个 id 字段。你可以通过返回的 mongocxx::result::insert_one 实例的 inserted_id() 方法获得这个返回的值。

插入多个文档

对于在集合中插入多个文档,请使用 mongocxx::collection 实例的 insert_many() 方法,该方法可以插入一个文档列表。

接下来的代码会插入多个下面形式的文档:

{ "i" : value }

创建一个文档列表并在循环中添加文档:

std::vector<bsoncxx::document::value> documents;
for(int i = 0; i < 100; i++) {
    documents.push_back(
      bsoncxx::builder::stream::document{} << "i" << i << finalize);
}

为了将文档列表插入到集合中,应该将文档列表传递给 insert_many() 方法。

collection.insert_many(documents);

如果不给每一个文档指定 _id 字段,MongoDB 会自动的给插入的文档添加 _id 字段。

可以通过返回的 mongocxx::result::insert_many 实例的 inserted_ids() 方法获得这些值。

集合查询

为了查询集合,请使用集合的 find()find_one() 方法。

find() 方法将返回一个 mongocxx::cursor 实例,对应的 find_one 将返回一个 std::optional< bsoncxx::document::value > 实例。

即可以通过调用一个传递了空文档的方法来查询集合中的所有文档,也可以传递一个过滤器来查询符合过滤条件的文档。

在集合中查询单个文档

为了获得集合中的一个文档,使用不带任何参数的 find_one() 方法。

bsoncxx::stdx::optional<bsoncxx::document::value> maybe_result =
  collection.find_one({});
if(maybe_result) {
  // Do something with *maybe_result;
}
查询集合中的所有文档
mongocxx::cursor cursor = collection.find({});
for(auto doc : cursor) {
  std::cout << bsoncxx::to_json(doc) << "\n";
}
指定一个查询过滤器
获取匹配过滤器的单个文档

为了获取字段 i71 的第一个文档,传递文档 {"i":71} 来指定等式条件:

bsoncxx::stdx::optional<bsoncxx::document::value> maybe_result =
  collection.find_one(document{} << "i" << 71 << finalize);
if(maybe_result) {
  std::cout << bsoncxx::to_json(*maybe_result) << "\n";
}

这里例子将会输出一个文档:

{ "_id" : { "$oid" : "5755e19b38c96f1fb25667a8" },  "i" : 71 }

_id 字段是被 MongoDB 自动添加上的,你的值将会和上面显示的不同。MongoDB 保留字段名称以一个下划线(_)和美元符号($)开始,用于内部使用。

获得匹配过滤器的所有文档

下面例子将会返回并打印满足 50<"i"<=100 条件的所有文档:

mongocxx::cursor cursor = collection.find(
  document{} << "i" << open_document <<
    "$gt" << 50 <<
    "$lte" << 100
  << close_document << finalize);
for(auto doc : cursor) {
  std::cout << bsoncxx::to_json(doc) << "\n";
}

更新文档

为了更新集合中的文档,你可以使用集合的 update_one()update_many() 方法。

更新方法返回一个 std::optional< mongocxx::result::update > 实例,其保存了更新操作修改文档数量的信息。

更新单个文档

更新单个文档,使用 update_one() 方法。

下面代码将会更新符合筛选器 {"i":10} 的第一个文档并将其 i 的值设为 110

collection.update_one(document{} << "i" << 10 << finalize,
                      document{} << "$set" << open_document <<
                        "i" << 110 << close_document << finalize);
更新多个文档

为了更新符合过滤器的所有文档,使用 update_many() 方法。

下面例子将会将值小于 100i 增加 100

bsoncxx::stdx::optional<mongocxx::result::update> result =
 collection.update_many(
  document{} << "i" << open_document <<
    "$lt" << 100 << close_document << finalize,
  document{} << "$inc" << open_document <<
    "i" << 100 << close_document << finalize);

if(result) {
  std::cout << result->modified_count() << "\n";
}

删除文档

为了从集合中删除文档,可以使用集合的 delete_one()delete_many() 方法。

删除方法将会返回一个 std::optional< mongocxx::result::delete > 方法,其保存了删除文档的数量。

删除单个文档

为了删除最多一个符合过滤器的文档,请使用 delete_one() 方法。

例如,删除一个符合过滤器 {"i":110} 的文档:

collection.delete_one(document{} << "i" << 110 << finalize);
删除匹配过滤器的所有文档

为了删除匹配过滤器的所有文档,使用 delete_many() 方法。

下面例子将删除所有 i 值大于 100 的文档:

bsoncxx::stdx::optional<mongocxx::result::delete_result> result =
 collection.delete_many(
  document{} << "i" << open_document <<
    "$gte" << 100 << close_document << finalize);

if(result) {
  std::cout << result->deleted_count() << "\n";
}

创建索引

为了给一个或多个字段创建索引,传递一个索引规则文档(index specification document)给一个 mongocxx::collection 实例的 create_index() 方法。一个索引键规则文档(index key specification document)包含索引的字段以及每个字段的索引类型:

{ "index1": "<type>", "index2": <type> }
  • 对于升序索引类型,指定 <type> 为 1.
  • 对于降序索引类型,指定 <type> 为 -1.

下面的示例代码在 i 字段上创建了一个升序索引:

auto index_specification = document{} << "i" << 1 << finalize;
collection.create_index(std::move(index_specification));
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值