MongoDB之CRUD基本操作与聚合框架的使用
MongoDB概述
MongoDB 是一款灵活、可扩展、高性能的 NoSQL 数据库,主要用于处理非结构化数据和大规模数据处理。它采用的是面向文档的数据模型,数据以 BSON(Binary JSON) 格式存储,支持动态查询和索引,可在分布式环境下运行。
特点和优势:
1.面向文档的数据模型:
MongoDB 存储的数据是以 BSON 格式表示的文档,与传统关系型数据库的表格模型不同,这样可以更好地处理非结构化数据和半结构化数据。
2.高性能和可扩展性:
MongoDB 支持水平扩展,可以通过添加更多的节点来增加系统的处理能力,同时还具有高性能的查询和索引能力,适用于大规模数据处理和高并发访问场景。
3.强大的查询和索引功能:
MongoDB 支持动态查询和索引,可以使用多种查询方式进行数据检索,同时还可以创建多种类型的索引来优化查询性能。
4.灵活的数据模型和数据一致性:
MongoDB 支持动态数据模型,可以方便地扩展和修改数据结构,同时还具有多种数据一致性选项,包括强一致性和最终一致性等。
5.开源和社区支持:
MongoDB 是一款开源软件,拥有庞大的社区支持和活跃的开发者社区,可以提供丰富的功能和优化。
MongoDB官方文档:https://docs.mongodb.com
MongoDB中文社区:http://www.mongoing.com
相关术语
在mongodb中是通过数据库、集合、文档的方式来管理数据,与关系型数据库类似。
SQL 术语/概念书 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接(MongoDB不支持) | |
primary key | primary key | 主键,MongoDB自动在每个集合的文档中添加_id的主键 |
数据库
一个mongodb中可以建立多个数据库
Help查看命令提示
db.help();
切换/创建数据库(如果数据库不存在,则创建数据库,否则切换到指定数据库)
use test
查询所有数据库
show dbs;
删除当前使用数据库
db.dropDatabase();
查看当前使用的数据库
db.getName();
显示当前db状态
db.stats();
当前db版本
db.version();
查看当前db的链接机器地址
db.getMongo〇;
文档
文档是一组键值(key-value)对(即BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
RDBMS与MongoDB对应的术语
RDBMS | MongoDB |
---|---|
数据库 | 数据库 |
表格 | 集合 |
行 | 文档 |
列 | 字段 |
表联合 | 嵌入文档 |
主键 | 主键 (MongoDB 提供了 key 为 _id ) |
需要注意的是: |
文档中的键/值对是有序的
文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)
MongoDB区分类型和大小写
MongoDB的文档不能有重复的键
文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符
文档键命名规范:
键不能含有\0 (空字符)。这个字符用来表示键的结尾
.和$有特别的意义,只有在特定环境下才能使用
以下划线"_"开头的键是保留的(不是严格要求的)
集合
集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
创建一个集合(table)
db.createCollection( "collName");
得到指定名称的集合(table )
db.getCollection("user");
数据类型
数据类型 |描述
–|–|–
String | 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的
Integer |整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位
Boolean |布尔值。用于存储布尔值(真/假)
Double |双精度浮点值。用于存储浮点值。
Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array |用于将数组或列表或多个值存储为一个键
Timestamp | 时间戳。记录文档修改或添加的具体时间
Object |用于内嵌文档
Null |用于创建空值
Symbol |符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言
Date |日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息
Object ID |对象 ID。用于创建文档的 ID
Binary Data |二进制数据。用于存储二进制数据
Code |代码类型。用于在文档中存储 JavaScript 代码
Regular expression | 正则表达式类型。用于存储正则表达式
连接命令格式
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
mongodb:// 固定前缀
username:账号,可不填
password:密码,可不填
host:主机名或ip地址,只有host主机名为必填项
port:端口,可不填,默认27017
/database:连接某一个数据库
?options:连接参数,key/value对
mongodb://localhost 连接本地数据库27017端口
mongodb://root:123456@localhost 使用用户名root密码为123456连接本地数据库27017端口
mongodb://localhost:27017,localhost:27018,localhost:27019,连接三台主从服务器,端口为27017、27018、27019
MongoDB基本操作
数据库操作
登录客户端
root@d8110bf377a7:/# mongo
MongoDB shell version v5.0.5
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("7f7b7e78-ffa1-4f8d-8c08-78800215cce0") }
MongoDB server version: 5.0.5
查询
show databases 查询全部数据库
show dbs 查询全部数据库
db 显示当前数据库
创建数据库
1.use DATABASE_NAME
use demodb :有demodb数据库则切换过去,否则创建,新创建的数据库不显示,需要至少包括一个集合。
2.插入数据即刻创建数据库
删除数据库
使用 db.dropDatabase() 来删除数据库
数据库相应文件也会被删除,磁盘空间将被释放
先切换数据库:use demodb
再执行删除:db.dropDatabase()
use demo
db.dropDatabase()
集合(表)操作
集合相当于关系数据库中的表,一个数据库可以创建多个集合,一个集合是将相同类型的文档管理起来。
集合列表
show collections
创建集合
1.db.createCollection(name, options)
name: 新创建的集合名称
options: 创建参数
db.createCollection(user) : 创建user集合
2.插入数据即可创建集合
更新集合名称
db.集合名.renameCollection("新的集合名称")
删除集合
db.<集合>.drop() 删除一个集合
集合中的全部文档都会被删除
集合相关的索引也会被删除
db.collection_name.drop()
db.user.drop() : 删除user集合
文档操作
mongodb中文档的格式是json格式,使用 insert() 或 save() 方法向集合中插入文档
进入数据库,创建集合
> use demoDb
switched to db demoDb
> db.createCollection('user')
{ "ok" : 1 }
>
insert插入
insert单条数据
db.<集合>.insertOne(<JSON对象>)
insert多条数据
db.<集合>.insertMany([<JSON 1>, <JSON 2>, …<JSON n>])
示例
db.COLLECTION_NAME.insert(document)
db.demoDb.insert({"name":"lisi","age":12})
{
"_id" : ObjectId("5fc7a1a39b84bb20d9911830"),
"name" : "lisi",
"age" : 12.0
}
每个文档默认以_id作为主键,主键默认类型为ObjectId(对象类型),mongodb会自动生成主键值。
Objectld是、id”的默认类型。Objectld使用12字节的存储空间,每个字节二位十六进制数字, 是一个24位的字符串
update更新
语法格式:
db.collection.update(
<query>,
<update>,
<options>
)
query:查询条件,相当于sql语句的where
update:更新文档内容
options:选项
1、整体更新
将符合条件的第一个文档更新
db.user.update({"name" : "lisi"},{"name" : "wangwu", "age" : 22.0})
2、局部更新
使用
$set
修改器指定要更新的key,key不存在则创建,存在则更新。
db.user.update({"name" : "lisi"},{$set:{"age":22}})
3.批量更新
将符合条件的所有文档更新
db.user.update({"name" : "wangwu"},{$set:{"name" : "lisi", "age" : 33.0}},{multi:true})
multi:false表示更新第一个匹配的文档,true表示更新所有匹配的文档。
updateOne表示无论条件匹配多少条记录,始终只更新第一条
updateMany表示条件匹配多少条就更新多少条
updateOne/updateMany方法要求更新条件部分必须具有以下之一,否则将报错:
$set/$unset
$push/$pushAll/$pop
$pull/$pullAll
$addToSet
db.user.updateOne({"age":21},{$set:{"name":"name111"}})
更新数组
$push: 增加一个对象到数组底部
$pushAll: 增加多个对象到数组底部
$pop: 从数组底部删除一个对象
$pull: 如果匹配指定的值,从数组中删除相应的对象
$pullAll: 如果匹配任意的值,从数据中删除相应的对象
$addToSet: 如果不存在则增加一个值到数组
Remove删除
db.student.remove(<query>)
query:删除条件,相当于sql语句中的where
删除全部
db.user.remove({})
按条件删除
db.user.remove({"name":"wangwu"})
remove命令需要配合查询条件使用;
匹配查询条件的的文档会被删除;
指定一个空文档条件会删除所有文档;
find查询
# query:查询条件,可不填
# projection:投影查询key,可不填
db.collection.find(query, projection)
查询全部
db.user.find()
以格式化的方式来显示所有文档
db.集合名.find().pretty()
查询符合条件的记录
db.user.find({"age":33})
投影查询
设置需要显示字段,一旦设置投影默认字段都不显示,需要手动设置字段为 1,如果是_id默认是显示,如果不显示可以把 _id 设置成 0
db.集合名称.find( {查询条件} , {_id : 0, 字段A : 1, 字段B : 1} )
# 设置查询条件 设置投影条件
db.user.find({name:"mongo"},{_id:0,name:1,age:1})
只显示name和age两个key,_id主键不显示。
db.user.find({"name":"lisi"},{name:1,age:1,_id:0})
排序
1升序,-1降序
db.user.find().sort({age:-1})
统计个数
db.user.find().count()
db.user.count({})
消除重复
# 去重字段 查询条件
db.user.distinct('name',{age:{$gt:20}})
查询子文档
搜索子文档,使用“field.sub_field”的形式查询子文档
{
"_id": {
"$oid": "61330facbf0bf2331163f17a"
},
"name": "测试",
"age": 21,
"address": {"name":"address1"}
}
db.user.find( { "address.name" : "address1"})
对数组查询
对数组中的元素进行搜索
{
"_id": {
"$oid": "61330facbf0bf2331163f17a"
},
"address": ["address1", "address2"]
}
db.user.find( { "address" : "address1"})
db.user.find( { $or: [{"address" : "address1"},{"address" : "address2"}]})
搜索数组中的对象
{
"_id": {
"$oid": "61330facbf0bf2331163f17a"
},
"users":[{"name":"name1","age":22},{"name":"name2","age":23}]
}
db.user.find({"users.name":"name2"})
db.user.find({"users.name":"name2","users.age":23})
// 索子对象的多个字段时,如果使用 $elemMatch,它表示必须是同一个子对象满足多个条件。
db.user.find({"users":{$elemMatch:{"name":"name1","age":22}}})
比较运算符
$lt: 存在并小于
$lte: 存在并小于等于
$gt: 存在并大于
$gte: 存在并大于等于
$ne: 不存在或存在但不等于
db.user.find({age:{$gt:20}})
逻辑运算符
$and: 匹配全部条件
$or: 匹配两个或多个条件中的一个
单条件查询
db.user.find({"age":21});
多条件and查询
db.user.find({"name":"测试","age":21});
db.user.find({$and : [{"name":"测试"},{"age":21}]});
多条件 or 查询
db.user.find({$or : [{"name":"测试"},{"age":25}]});
范围运算符
$in: 存在并在指定数组中
$nin: 不存在或不在指定数组中
db.user.find(
{
name:{$nin:['java','vue']}
}
)
正则表达式
db.user.find(
{
name:/^java*/
}
)
db.user.find(
{
name:{$regex:"^java"}
}
)
自定义查询
db.user.find(
{
$where: function(){
// true 表示 符合条件,false 表示不符合条件
// 1. 返回值 必须是 true 或者 false
// 2. 在函数中的关键 this 表示当前记录
if(this.name == "java"){
return true
} else {
return this.age > 20 && this.age < 30
}
}
}
)
limit和skip
limit 显示记录数
db.user.find().limit(2)
skip跳过记录
db.user.find().skip(1)
2者混合使用,不管先后先 skip 再 limit
db.user.find().limit(2).skip(1)
db.user.find().skip(1).limit(2)
查询条件对照表
SQL | MQL |
---|---|
a = 1 | {a: 1} |
a <> 1 | {a: {$ne: 1}} |
a > 1 | {a: {$gt: 1}} |
a >= 1 | {a: {$gte: 1}} |
a < 1 | {a: {$lt: 1}} |
a <= 1 | {a: {$lte: 1}} |
查询逻辑对照表
SQL | MQL |
---|---|
a = 1 AND b = 1 | {a: 1, b: 1} 或 {$and: [{a: 1}, {b: 1}]} |
a = 1 OR b = 1 | {$or: [{a: 1}, {b: 1}]} |
a IS NULL | {a: {$exists: false}} |
a IN (1, 2, 3) | {a: {$in: [1, 2, 3]}} |
索引操作
在MongoDB中,可以使用索引来加速数据的查询和排序操作。
语法格式:
db.collection.createIndex(keys, options)
插入10W数据
插入10万条数据到数据库中
use demo
for(i=0;i<100000;i++){db.user.insert({name:'test'+i,age:i})}
查询速度对比
db.user.find({name:'test1000'})
# 查询操作的详细信息
db.user.find({name:'test1000'}).explain('executionStats')
创建索引
# 1:指定按升序创建索引
# -1:指定按降序来创建索引
db.user.createIndex({"name":1})
创建唯一索引
默认情况下mongdb的索引域的值是可以相同的,创建唯一索引之后,数据库会在插入数据的时候检查创建索引域的值是否存在,如果存在则不会插入该条数据
可以根据唯一索引指定的字段的值,进行数据去重
db.user.createIndex({"name":1}, {"unique":true})
建立复合索引
设置使用多个字段创建索引。将名称name与年龄age等2个字段建立复合索引
db.user.createIndex({"name":1,"age":1})
查看集合索引
db.user.getIndexes()
删除索引
删除集合指定索引
db.user.dropIndex('索引名称')
删除集合所有索引
db.user.dropIndexes()
查看集合索引大小
db.user.totalIndexSize()
注意点
根据需要选择是否需要建立唯一索引
索引字段是升序还是降序在单个索引的情况下不影响查询效率,但是带复合索引的条件下会有影响
数据量巨大并且数据库的读出操作非常频繁的时候才需要创建索引,如果写入操作非常频繁,创建索引会影响写入速度
MongoDB聚合框架
MongoDB中聚合(aggregate)主要用于处理数据(如统计平均值,求和等),并返回计算后的数据结果。而find主要用于查询数据
MongoDB聚合框架(Aggregation Framework)是一个计算框架,它可以:
作用在一个或几个集合上
对集合中的数据进行的一系列运算
将这些数据转化为期望的形式
聚合框架相当于SQL查询中的:GROUP BY、 LEFT JOIN、 AS等
管道(Pipeline)和步骤(Stage)
整个聚合运算过程称为管道(Pipeline),它是由多个步骤(Stage)组成的,每个管道接受一系列文档(原始数据),每个步骤对这些文档进行一系列运算,结果文档输出给下一个步骤
聚合运算写法
db.集合名称.aggregate([
{管道名 : {表达式}},
{管道名 : {表达式}},
{管道名 : {表达式}},
])
pipeline = [$stage1, $stage2, ...$stageN];
db.<COLLECTION>.aggregate(
pipeline,
{ options }
);
管道名
管道名 | 作用 | SQL等价运算符 |
---|---|---|
$project | 和投影作用一样,可以让某些字段不显示。修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。 | AS |
$match | 匹配出符合条件的数据,和find操作一致,用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。 | WHERE |
$limit | 用来限制MongoDB聚合管道返回的文档数。 | LIMIT |
$skip | 在聚合管道中跳过指定数量的文档,并返回余下的文档。 | SKIP |
$unwind | 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。 | |
$group | 按照某个字段进行分组,可用于统计结果。 | GROUP BY |
$sort | 将输入文档排序后输出。 | ORDER BY |
$geoNear | 输出接近某一地理位置的有序文档 | |
$lookup | 左外连接 | LEFT OUTER JOIN |
$graphLookup | 图搜索 | |
$facet $bucket | 分面搜索 |
表达式
表达式 | 描述 | 实例 |
---|---|---|
$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”}}}]) |
group 管道
按照某个字段进行分组,依次对每个组进行统计计算。 _id表示按照某个字段进行分组,但是内部的字段名称前面必须添加$字符,如果_id设置为 null å表示把所有数据作为一组看待。
$sum/$avg
$push/$addToSet
$first/$last/$max/$min
db.user.aggregate([
{
$group: {
// 设置分组字段
// _id:"$gender",
_id:null,
// 编写统计表达式
// $sum 统计总数表达式
// "总数":{$sum:3},
// $avg 表示平均值
"平均年龄":{$avg:"$age"},
// "最大年龄":{$max:"$age"},
// "最小年龄":{$min:"$age"}
// "姓名":{$push:"$name"},
// "xxx":{$push:"$$ROOT"}
}
}
]).pretty()
match 管道
$eq/$gt/$gte/$lt/$lte
$and/$or/$not/$in
$geoWithin/$intersect
db.user.aggregate([
{
$match: {
age:{$gt:15}
}
},
{
$group:{
_id:"$name",
"平均年龄":{$avg:"$age"},
"最大年龄":{$max:"$age"}
}
}
])
project 管道
和投影一样,如果不设置就表示隐藏,如果设置为1表示表示显示,_id 默认是显示的,如果想让_id不显示可以设置为0
$map/$reduce/$filter
$range
$multiply/$divide/$substract/$add
$year/$month/$dayOfMonth/$hour/$minute/$second
db.user.aggregate([
{
$match:{
age:{$gt:15}
}
},
{
$group:{
_id:"$name",
"平均年龄":{$avg:"$age"},
"最大年龄":{$max:"$age"}
}
},
{
$project:{
"_id":0,
"平均年龄":1
}
}
])
sort 管道
1表示升序,-1表示降序
db.user.aggregate([
{
$match:{
age:{$gt:15}
}
},
{
$sort:{
age:1
}
}
])
limit管道 和 skip管道
limit管道和skip管道有先后次序因为是管道的作用
db.user.aggregate([
{
$match:{
age:{$gte:20}
}
},
{
$limit:2
},
{
$skip:1
}
])
unwind 管道
按照某个字段进行数据拆分成多条记录,默认情况会过滤 列表为空,列表为 null 字段不存在的数据,如果想要不过滤数据使用preserveNullAndEmptyArrays:true
db.user.aggregate([
{
$unwind:'$age'
}
])
// 不过滤数据
db.user.aggregate([
{
$unwind: {
// 设置拆分字段
path:'$age',
// 设置不过滤数据
preserveNullAndEmptyArrays:true
}
}
])
混合使用
db.user.aggregate([
{$match: {age: 21}},
{$skip: 5},
{$limit: 10},
{$project: {
'名称': '$username',
}}
]);
{ _id: ObjectId("613329f36294f81cb1c96092"), '名称': 'test' }
复制集的作用
MongoDB 复制集的主要意义在于实现服务高可用
它的现实依赖于两个方面的功能:
数据写入时将数据迅速复制到另一个独立节点上
在接受写入的节点发生故障时自动选举出一个新的替代节点
在实现高可用的同时,复制集实现了其他几个附加作用:
数据分发:将数据从一个区域复制到另一个区域,减少另一个区域的读延迟
读写分离:不同类型的压力分别在不同的节点上执行
异地容灾:在数据中心故障时候快速切换到异地
复制集结构
一个典型的复制集由3个以上具有投票权的节点组成,包括:
一个主节点(PRIMARY):接受写入操作和选举时投票
两个(或多个)从节点(SECONDARY):复制主节点上的新数据和选举时投票
不推荐使用 Arbiter(投票节点)
数据是如何复制的
当一个修改操作,无论是插入、更新或删除,到达主节点时,它对数据的操作将被记录下来(经过一些必要的转换),这些记录称为 oplog。
从节点通过在主节点上打开一个 tailable 游标不断获取新进入主节点的 oplog,并在自己的数据上回放,以此保持跟主节点的数据一致。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137004.html