Nosql
## 第一章 :MongoDB基础
### 1. 关系型数据库与非关系型数据库
1)关系型数据库
关系型数据库建立在关系型数据模型的基础上,是借助于集合代数等数学概念和方法来处理数据的数据库。Oracle、MySQL等都是。
在关系型数据库中,实体以及实体间的练习均由单一的结构类型来表示单一的数据结构----关系(表文件)。关系数据库的表采用二维表格来存储数据,是一种按行与列排列的具有相关信息的逻辑组,它类似于Excel工作表。一个数据库可以包含任意多个数据表。
在用户看来,一个关系模型的逻辑结构是一张二维表,由行和列组成。这个二维表就叫关系,通俗地说,一个关系对应一张表。
2)非关系型数据库
NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。
在信息化时代背景下,互联网数据增长迅猛,数据不仅仅是传统的结构化数据,还包含了大量的非结构化和半结构化数据,关系型数据库无法存储此类数据。
大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
### 2. NoSQL数据库分类
<https://baike.baidu.com/item/NoSQL/8828247?fromtitle=%E9%9D%9E%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93>
键值数据库 :如 Redis
列存储数据库 :如 HBase
文档型数据库 :如MongoDB
图形数据库:如Neo4j
### 3. MongoDB简介
1)简介
**分布式**文件存储的数据库,由C++编写。**文档型**非关系型数据库。
MongoDB 将数据存储为一个文档,**数据结构由键值(key=>value)对**组成。MongoDB 文档类似于 JSON 对象的Bson格式数据。
```
{
“name” : “张三" ,
“age” : 18 ,
“groups” : ["足球队" , "IT队"] ,
“birth” : new Date(2000,2,13)
}
```
支持的数据结构非常松散,是类似json的bson格式,存储比较复杂的数据类型。
2)集合和文档
集合 ==》表(table);文档的集合,集合中的文档是各式各样的。
文档:key+value存储的数据 ==》记录,元组;就是键值对的一个有序集。
### 4. MongoDB的安装
**-- Windows系统下的安装(解压缩配置)**
【控制台下,以**管理员**身份操作】
安装 :解压缩 ==> 安装服务 ==> 开始使用
-- 下载zip包
-- 解压(不要出现中文路径、空格路径)
-- 解压后在文件中创建 "data" 与 "log" 两个目录
-- 以”**管理员**“身份打开cmd,**定位到MongoDB的解压缩bin目录**,然后执行如下命令
```
[安装服务--在MongoDB的解压缩bin目录下,如果配置了环境变量就任意]
d:\green_software\mongodb\bin> mongod --install --dbpath D:\green_software\mongodb\data --logpath D:\green_software\mongodb\log\mongodb.log
是否成功:打开服务 (启动 services.msc) , 查找是否由MongoDB的服务
[启动、停止、重启服务]
net start|stop mongodb
[移除服务]
先停止服务再操作
mongod --remove
[进入MongoDB的客户端]
mongo
```
配置环境变量
path变量增加 **<mongodb解压缩路径>\bin**
使用:启动服务 ==> 客户端
卸载:停止服务 ==> 移出服务 ==> 删除文件夹
**-- Linux系统下安装(CentOS7.8)**
-- 关闭防火墙
[root@CentosA ~]# systemctl stop firewalld
[root@CentosA ~]# systemctl disable firewalld
-- 选择好软件安装位置,将压缩包(.tar.gz ==>.tgz)传输到该位置
/usr/local
[root@CentosA ~]# cd /usr/local
-- 定位到 /usr/local , 解压
[root@CentosA local]# tar -zxvf xxx.tgz
-- 将压缩包重命名
[root@CentosA local]# mv xxx /usr/local/mongodb
-- 创建 data/ 与 log/ 目录
[root@CentosA local]# mkdir -p /usr/local/mongodb/data /usr/local/mongodb/log
-- 启动服务
[root@CentosA local]# cd /usr/local/mongodb/bin
[root@CentosA bin]# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --logappend --port=27017 --fork
-- 进入到MongoDB客户端
[root@CentosA bin]# ./mongo
```
systemctl stop firewalld
systemctl disable firewalld
cd /usr/local/
ls
tar -zxvf mongodb-linux-x86_64-rhel70-4.4.5.tgz
mv mongodb-linux-x86_64-rhel70-4.4.5 /usr/local/mongodb
ls
mkdir -p /usr/local/mongodb/data /usr/local/mongodb/log
cd mongodb/
ls
cd bin
ls
./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --logappend --port=27017 --fork
./mongo
```
-- 停止服务
```
方式一:在MongoDB的客户端下
> use admin
switched to db admin
> db.shutdownServer();
server should be down...
方式二 :在Shell命令下
[root@CentosA bin]# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --shutdown
方式三 : 杀死进程
[root@CentosA bin]# kill -9 进程号
```
-- 查看端口号占用情况
[root@CentosA bin]# netstat -tunlp | grep 27017
##
## 0423
## 第二章 MongoDB的基本操作
### 1. 基本操作
1)获取当前所访问的服务器对象
db.getMongo()
2)查看当前服务器下的数据库
show dbs | show databases
3)打开数据库
use 库名
> tip : MongoDB中不需要创建数据库,也不需要创建集合。
>
> 当忘数据库中插入了文档或者创建集合,会检查数据库是否存在,不存在自动创建数据库;
>
> 也会检查集合是否存在,不存在自动创建集合。
```
1. 库名全部小写,不允许使用"_"以外的其他字符,基本上就是ASCII范围的字母和数字
2. 不允许数字开头
3. 最多64个字符
4. 库名将以文件夹的形式存在
5. 系统中已存在了 admin , config , local 不要使用这些名称
```
4)查看当前数据库下集合
show collections
5)查看当前打开的数据库
db.getName()
6)删除数据库
【注】先打开要删除的数据
db.dropDatabase()
7)创建集合
db.createCollection("集合名")
```
1. 集合名全部小写,不允许使用"_","."以外的其他字符
2. 不允许数字开头
3. 最多64个字符
4. 禁止使用 system 开头
```
8)删除集合
db.集合名.drop()
### 2. MongoDB支持的数据类型
```
-- null
-- 布尔值 true与false
-- 数值
> x = {
... "k1":3, double
... "k2":3.14, double
... "k3":NumberInt(3), 32-int
... "k4":NumberLong(3) 64-int
... }
{ "k1" : 3, "k2" : 3.14, "k3" : NumberInt(3), "k4" : NumberLong(3) }
-- 字符串
-- 日期
使用new Date()创建的将是ISODate类型的日期对象,同样可以使用ISODate()
> data = {
... "d1":new Date(),
... "d2":ISODate(),
... "d3":new Date(2020,3,14),
... "d4":new Date("2020-03-14")
... }
{
"d1" : ISODate("2021-04-23T01:05:40.849Z"),
"d2" : ISODate("2021-04-23T01:05:40.850Z"),
"d3" : ISODate("2020-04-13T16:00:00Z"),
"d4" : ISODate("2020-03-14T00:00:00Z")
}
> d2 = {"dd":new Date("2020-04-12T10:20:20Z")}
{ "dd" : ISODate("2020-04-12T10:20:20Z") }
-- 数组
> m = {
... "k1":["hello",123,true,34]
... }
{
"k1" : [
"hello",
123,
true,
34,
5
]
}
-- 内嵌文档
> n = {
... "name":"rose",
... "addr":{"prov":"HeBei","city":"Shijiazhuang"}
... }
{
"name" : "rose",
"addr" : {
"prov" : "HeBei",
"city" : "Shijiazhuang"
}
}
-- 正则表达式
> t={
... "pro":/hello/i
... }
{ "pro" : /hello/i }
-- ObjectID 类型
构成 :时间戳+机器+PID+计数器==>24位十六进制数字组成的字符串
> k = {
... id: ObjectId()
... }
{ "id" : ObjectId("60763d98d5046809b857e4dc") }
-- 二进制数据
-- 代码
> u = {
... "pro" : function(){
... return "hello";
... }
... }
{ "pro" : function(){ return "hello"; } }
```
### 3. 插入文档
**语法:db.集合名.insert(文档数据)**
**集合存在,直接插入数据;集合不存在,创建。**
【说明】文档数据是BSON格式,key必须使用双引号引起来。但是在MongoDB下操作时,key可以不使用双引号。
-- 每次插入一条记录
```mysql
> db.getName()
test12
> show collections
> db.c1.insert({
... "name":"rose",
... "age":18
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
> db.c1.insert({
... "name":"joe",
... "age":20,
... "gender":"M"
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
> db.c1.insert({
... "name":"mike",
... "age":22,
... "gender":null
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
> db.c1.insert({
... "_id":4,
... "name":"X1"
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
{ "_id" : 4, "name" : "X1" }
```
-- 批量插入数据
```mysql
> datas = [
... {"name":"zs" , "age":12} ,
... {"name":"lisi", "email":"ls@qq.com" , "gender":"F"}
... ]
[
{
"name" : "zs",
"age" : 12
},
{
"name" : "lisi",
"email" : "ls@qq.com",
"gender" : "F"
}
]
> db.c1.insert(datas)
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
```
-- 支持js语法
(不是批量插入,而是insert执行了多次)
```mysql
> for(i = 1 ;i <=10 ;i++) {
... if(i%2==0) {
... db.c1.insert({"name":"isoft"+i , "gender":"F"})
... } else {
... db.c1.insert({"name":"isoft"+i , "gender":"M"})
... }
... }
WriteResult({ "nInserted" : 1 })
```
### 4. 更新文档
#### 1)update()
语法 :db.集合名.update(条件,更新后数据 [,{upsert:boolean , multi:boolean }])
```
条件描述 {}
更新后数据描述方式为 :
{“修改器" : { ... } , .... }
【注】不使用修改器,是进行替换
upsert : 按照条件没有找到匹配文档则执行插入(true-插入,false-不插入 默认)
multi:匹配成功所有数据都修改(true-是,false-否 默认)
```
| 修改器名称 | 作用 |
| ---------- | ------------------------------------------------------------ |
| $set | 修改key值,如果key在文档中不存在,则插入该属性以及值到条件匹配文档 |
| $inc | 在原值基础上递增减 |
| $rename | 重命名key |
| $unset | 根据key删除value,形式 {"$unset":{”key" : true}} |
```mysql
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
{ "_id" : 4, "name" : "X1" }
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zs", "age" : 12 }
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646140"), "name" : "isoft1", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646141"), "name" : "isoft2", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646142"), "name" : "isoft3", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646143"), "name" : "isoft4", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646144"), "name" : "isoft5", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646145"), "name" : "isoft6", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646146"), "name" : "isoft7", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646147"), "name" : "isoft8", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646148"), "name" : "isoft9", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646149"), "name" : "isoft10", "gender" : "F" }
# 演示不使用插入器
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
> db.c1.update(
... {"name":"joe"},
... {"email":"joe@qq.com"}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "email" : "joe@qq.com" }
# 插入器使用
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zs", "age" : 12 }
# 将 zs 的name修改为 zhangsan,email修改为zs@qq.com
> db.c1.update(
... {"name":"zs"},
... {"$set":{"name":"zhangsan","email":"zs@qq.com"}}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
# 将 rose的年龄 -1
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":-1}}
... )
# 将mike的gender属性移除,age+1,name属性修改为username
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
> db.c1.update(
... {"name":"mike"},
... {
... "$unset":{"gender":true},
... "$inc":{"age":1},
... "$rename":{"name":"username"}
... }
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
# 选项 upsert的使用
> db.c1.update(
... {"name":"Y1"},
... {"$set":{"name":"rose","age":8,"email":"rr@qq.com"}}
... )
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.c1.update(
... {"name":"Y1"},
... {"$set":{"name":"rose","age":8,"email":"rr@qq.com"}},
{"upsert":true}
... )
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("608269aa6d779b5456ab6259")
})
# 选项 multi的使用
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":10}}
... )
> db.c1.update( {"name":"rose"}, {"$inc":{"age":10}} )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":10}} ,
{"multi":true}
... )
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
```
#### 2)save()
**语法 :db.集合名.save({文档数据})**
通过传入的文档来**替换**已有文档,也可能执行**插入**
如果没有传入**_id**属性,或者**_id**属性的值不存在 ==> 插入,否则就是按照**_id**做条件来替换已有文档。
```mysql
{ "_id" : 4, "name" : "X1" }
> db.c1.save({
... "name":"X1",
... "gender":"M"
... }
... )
WriteResult({ "nInserted" : 1 })
> db.c1.save(
... {
... "_id": 6,
... "name":"X1",
... "age":6
... }
... )
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 6 })
> db.c1.save(
... {"_id":4 , "name":"XX1" , "gender":"F"}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
```
### 5. 删除文档
**语法:db.集合名.remove(条件 [, {justOne:boolean} ] )**
justOne是否删除一条,true-是;false-否 默认
条件描述 : {} json数据格式 , 如果条件描述中出现多个属性,是与and关系
**remove({}) ==> 删除集合所有文档**
```mysql
> db.c1.remove({"name":"X1"})
WriteResult({ "nRemoved" : 2 })
> db.c1.remove(
... {"name":"rose"},
... {"justOne":true}
... )
WriteResult({ "nRemoved" : 1 })
> db.c1.remove({"_id":4})
WriteResult({ "nRemoved" : 1 })
> db.c1.remove({"_id": ObjectId("608221c66baab5abac64613b")})
WriteResult({ "nRemoved" : 1 })
> for(i=1;i<=10;i++){
db.c2.insert({"name" : "N" + i})
}
> db.c2.remove({})
WriteResult({ "nRemoved" : 10 })
```
### 6. 查询文档--find()
**语法:db.集合名.find(条件 [, 要显示的key])**
> tip : 可以最后加一个pretty() 格式化展示数据
#### 1)基本查询
```
条件描述 : "="
{} 或者 不写 查询所有文档
{"age":26} age=26
{"age":26,"gender":"M"} age=26 and gender='M'
{"age":12,"age":23} age=23(age=23覆盖条件age=12)
查询的属性
不写 所有key
{"age":1} 只显示key为age
{"age":1 , "gender":1} 显示key为age和gender
{“age":0} 除了key为age的其他
【注】_id 属性默认显示,如果不希望出现使用 "_id:0"
```
```mysql
> db.c1.find(
... {"name":"lisi"}
... )
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
>
> db.c1.find(
... {"gender":"F","name":"isoft"}
... )
>
> db.c1.find(
... {"age":12}
... )
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
>
> db.c1.find(
... {"age":12,"age":23}
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
> db.c1.find(
... {"age":23,"age":12}
... )
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
> db.c1.find(
... {},
... {"name":1}
... )
> db.c1.find(
... {},
... {"name":0}
... )
> db.c1.find(
... {},
... {"_id":0,"name":1,"email":1}
... )
```
#### 2)条件运算符的使用
```
描述 :{"key" : {"运算符" : value}}
```
| 运算符 | 作用 |
| ------ | :-------------------------------------------- |
| $gt | > |
| $gte | >= |
| $lt | < |
| $lte | <= |
| $ne | != |
| $in | in alue值是数组, {"age" : {"$in" : [10,20]} |
| $nin | not in |
```mysql
# 数据准备
> for(i=1;i<=10;i++){
... db.c1.update({"name":"isoft"+i},{"$set":{"age":10+i}})
... }
> db.c1.find( {"age":{"$gt":20}})
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
> db.c1.find(
... {"age":{"$lte":15}}
... )
> db.c1.find(
... {"gender":{"$in":['f']}}
... )
> db.c1.find( {"gender":{"$in":['f','F','0']}} )
> db.c1.find(
... {"age":{"$nin":[20]}}
... )
> db.c1.find(
{"age":{"$ne":20}}
)
```
#### 3)逻辑与、或
$and , $or
```
{“$and|$or" : [{} , {} ,...]
```
```mysql
# 查询age<12或者 age>20
> db.c1.find(
... {"$or":[
... {"age":{"$lt":12}},
... {"age":{"$gt":20}}
... ]}
... )
# 查询20<age<12
> db.c1.find(
... {"$and":[
... {"age":{"$gt":12}},
... {"age":{"$lt":20}}
... ]}
... )
#查询姓名是rose的文档 或者 年龄在18以上的男性文档
> db.c1.find(
... {"$or" : [
... {"name":"rose"},
... {"age":{"$gt":18} , "gender":"M"}
... ]}
... )
> db.c1.find(
{"$or" : [
{"name":"rose"},
{"$and":[
{"age":{"$gt":18}} ,
{"gender":"M"}
] }
] }
)
```
#### 4)$not、 $type
https://www.runoob.com/mongodb/mongodb-operators-type.html
double -- 1 , string--2 , object -- 3 , array -- 4 , null -- 10 , 32位int--16 , 64位int--18
NumberInt() , NumberLong()
【注意】**值是Array类型比较特殊,使用$not结合$type否定类型时,Array不能被检索到**
```mysql
# 数据准备
> db.c1.insert({"name":10,"age":11})
> db.c1.insert({"name":NumberInt(20),"age":20})
> db.c1.insert({"name":["r","o"],"age":20})
> db.c1.insert({"name":{"first":"rose","last":"L"}})
# 查询name值是 double类型
> db.c1.find(
... {"name":{"$type":1}}
... )
# 查询name的value值类型是数值类型
> db.c1.find(
... {"$or":[
... {"name":{"$type":1}},
... {"name":{"$type":16}},
... {"name":{"$type":18}}
... ]}
... )
> db.c1.find(
... {"name": {"$type":{"$in":[1,16,18]}}}
... )
Error: error: {
"ok" : 0,
"errmsg" : "type must be represented as a number or a string",
"code" : 14,
"codeName" : "TypeMismatch"
}
# 查询name的value值类型是string类型
> db.c1.find(
... {"name":{"$type":2}}
... )
# 查询name的value值类型不是string类型 ???? name值是array的
> db.c1.find(
... {"name":{"$not":{"$type":2}}}
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
{ "_id" : ObjectId("6084c12a95c3b0b8ed942f79"), "name" : 10, "age" : 11 }
{ "_id" : ObjectId("6084c1e495c3b0b8ed942f7a"), "name" : 20, "age" : 20 }
{ "_id" : ObjectId("6084c9b895c3b0b8ed942f7d"), "name" : { "first" : "rose", "last" : "L" } }
# 正确为
> db.c1.find(
... {"$or":[
... {"name":{"$not":{"$type":2}}} ,
... {"name": {"$type":4}}
... ]}
... )
```
#### 5)null与not null
【注】条件表述为 null ==》**该key不存在或者key的值是null的两种情况的文档**
```
value 值是null 的判定 :
{"key":null}
{"key":{"$in":[null]}}
value 值不是null 的判定 :
{"key":{"$nin":[null]}}
```
**可以结合 $in , $type , $exists , $not 一起使用**
```mysql
> db.c1.insert({"name":null,"age":0})
WriteResult({ "nInserted" : 1 })
# 查询 name 是 null ==> 结果是 name为null或者不存在name属性
> db.c1.find( {"name":null} )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
{ "_id" : ObjectId("6084c61495c3b0b8ed942f7c"), "name" : null, "age" : 0 }
# 查询name属性存在,且值是null的文档
> db.c1.find(
... {"name":{"$in":[null] , "$exists":true} }
... )
{ "_id" : ObjectId("6084c61495c3b0b8ed942f7c"), "name" : null, "age" : 0 }
# 查询 name 属性不是null ==> name不是null或者不存在name属性
> db.c1.find(
... {"name":{"$not":{"$type":10}}}
... )
# 查询 name 有确定值
> db.c1.find(
... {"$and" : [
... {"name":{"$not":{"$type":10}}} ,
... {"name":{"$exists":true}}
... ]}
... )
# 或者
> db.c1.find(
... {"name":{"$nin":[null]}}
... )
```
#### 6)正则表达式的使用
```
{"key": /正则表达式/修饰符 }
或者
{"key":
{"$regex":"正则表达式" [,{"$options":"修饰符"}]}
}
-- 含有字符 /字符/
-- 以字符开头 /^字符/
-- 以字符结尾 /字符$/
-- 常用的修饰符就是 i => 不区分大小写
```
```mysql
> db.c1.insert({"name":"Im"})
# name的值是i
> db.c1.find(
... {"name":"i"}
... )
# name的值含有i
> db.c1.find(
... {"name":/i/}
... )
# 使用修饰符
> db.c1.find(
... {"name":/i/i}
... )
# 使用正则表达式另外一种写法
> db.c1.find(
... {"name":{"$regex":"i"}}
... )
> db.c1.find( {"name":{"$regex":"i","$options":"i"}} )
# 以字符i开头
> db.c1.find(
... {"name":/^I/}
... )
{ "_id" : ObjectId("608508a0b2e7bc32dfacb336"), "name" : "Im" }
> db.c1.find(
... {"name":/I/i}
... )
# 以字符i结尾
> db.c1.find(
... {"name":/i$/}
... )
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
# 查询name属性是长度2--10的英文value值
> db.c1.find(
... {"name":/^[a-zA-Z]{2,10}$/}
... )
```
#### 7)查询数组值
##### -- 条件描述
```
$all : { "key“ : {"$all" : ["v1","v2" ,...]} } key in [v1 and v2]
key.index : index从0数起,用来限定数组的第几个值的条件描述
$size :限定数组元素值的个数 {"key":{"$size":int}}
```
```mysql
> data = [
... {"name":"A" , "fruit":["apple","banana","lemon"]} ,
... {"name":"B" , "fruit":["apple","orance","peach"]} ,
... {"name":"C" , "fruit":["cherry","banana","apple"]} ,
... {"name":"D" , "fruit":["cherry"]}
... ]
> db.c2.insert(data)
# 查找 fruit值含有 apple和banana的文档
-- 使用 $in 是 or
> db.c2.find(
... {"fruit":{"$in":["apple","banana"]}}
... )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb337"), "name" : "A", "fruit" : [ "apple", "banana", "lemon" ] }
{ "_id" : ObjectId("60850b58b2e7bc32dfacb338"), "name" : "B", "fruit" : [ "apple", "orance", "peach" ] }
{ "_id" : ObjectId("60850b58b2e7bc32dfacb339"), "name" : "C", "fruit" : [ "cherry", "banana", "apple" ] }
-- 正确写法
> db.c2.find(
... {"fruit":{"$all":["apple","banana"]}}
... )
# 查询 fruit的第三个元素值是 apple
> db.c2.find( {"fruit.2":"apple"} )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb339"), "name" : "C", "fruit" : [ "cherry", "banana", "apple" ] }
> db.c2.find(
... {"fruit.2":/apple/i}
... )
# 按照元素个数匹配
> db.c2.find(
... {"fruit":{"$size":1}}
... )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb33a"), "name" : "D", "fruit" : [ "cherry" ] }
> db.c2.find(
... {"fruit":{"$size":{"$gt":2}}}
... )
Error: error: {
"ok" : 0,
"errmsg" : "$size needs a number",
"code" : 2,
"codeName" : "BadValue"
}
```
##### -- 切片 $slice,返回限定个数
```mysql
> db.c2.find(
... {},
... {"fruit":{"$slice":1}} # 第一个元素
... )
> db.c2.find( {}, {"fruit":{"$slice":-1}} ) # 最后一个元素
> db.c2.find( {}, {"fruit":{"$slice":[1,3]}} ) # 索引位置从1开始,向后2个
```
##### -- 数组元素范围匹配 $elemMatch
> 如果查询条件只是一半范围,没有必要使用 $elemMath
```
数组元素值含有满足条件的情况的文档
{"key" : {"$elemMatch":{条件1, 条件2}}}
```
```mysql
> arr = [
... {"_id":1 , "x":5} ,
... {"_id":2 , "x":15} ,
... {"_id":3 , "x":25} ,
... {"_id":4 , "x":[5,25]} ,
... {"_id":5 , "x":[5,15,20,25,30]} ,
... {"_id":6 , "x":[8,16,24,36]}
... ]
>db.c3.insert(arr)
# 以下值限定不仅匹配普通value值,也匹配数组
> db.c3.find( {"x" : {"$gte":15}} )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 去掉数组类型值得匹配
> db.c3.find(
... {"$and":[
... {"x":{"$gte":15}},
... {"x":{"$not":{"$type":4}}}
... ]}
... )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
# 值在某个范围
> db.c3.find(
... {"$and":[
... {"x":{"$gt":10}},
... {"x":{"$lt":30}}
... ]}
... )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 , 含有值 >=8
> db.c3.find(
{"x":{"$elemMatch":{"$gte":8}}}
)
-- 等价于
> db.c3.find(
{"$and" :[
{"x":{"$gte":8}} ,
{"x" : {"$type":4}}
]}
)
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 ,含有值 >=8 并且 <40
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":40}}} )
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":20}}} )
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
```
#### 8)查询内嵌文档值
```
key.property
```
```mysql
> users = [
... {"name":{"first":"rose","last":"A"} , "age" : 18 },
... {"name":{"first":"joe","last":"B"} , "age" : 28 },
... {"name":{"first":"mike","last":"C"} , "age" : 23 }
... ]
> db.c4.insert(users)
# 使用 key.属性 进行查询
> db.c4.find(
... {"name.last":/a/i}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
>
> db.c4.find(
... {"name.first":/o/i}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
{ "_id" : ObjectId("60851611b2e7bc32dfacb33c"), "name" : { "first" : "joe", "last" : "B" }, "age" : 28 }
>
> db.c4.find(
... {"name":{"first":"rose","last":"A"}}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
# $elemMatch的使用
> books={
... "title":"A",
... "content":"helloworld is java",
... "comments":[
... {"author":"joe" , "score":3 , "comment":"good"} ,
... {"author":"rose" , "score":6 , "comment":"terrible"} ,
... ]
... }
> db.c5.insert(books)
# 查询 同一条评论作者是joe,并且分值在5分以上 ==》没有满足条件的文档
> db.c5.find(
... {"comments": {"$elemMatch":{"author":"joe" , "score":{"$gte":5}}}}
... )
# 查询 同一条评论作者是rose,并且分值在5分以上
> db.c5.find(
... {"comments": {"$elemMatch":{"author":"rose" , "score":{"$gte":5}}}}
... )
```
#### 9)$where
$where 可以执行任意的javascript函数,来实现查询条件描述。
!!!当使用$where时,要将集合中每个文档从Bson转换为js对象,然后在进行$where的js函数处理,性能非常低,**尽量不使用**。
```
{"$where": js函数 }
{"$where" : "js条件表达式" }
【注】在描述$where的条件时,js代码可以使用"this"指代当前文档 ; 函数返回boolean值
```
```mysql
> db.c3.find()
{ "_id" : 1, "x" : 5 }
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 ,含有值 >=8 并且 <40
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":40}}} )
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
> js = function() {
... arr = this.x ;
... for(n in arr) {
... return arr[n]>=8 && arr[n]<40
... }
... }
> db.c3.find(
... {"$where":js}
... )
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数据准备
> data = [
... {"name":"joe" , age : "12" , brotherAge : 20 },
... {"name":"rose" , age : "20" , brotherAge : 12 }
... ]
> db.c4.insert(data)
# 查询集合中当前文档记录的是弟弟或者妹妹信息的数据
> db.c4.find(
... {"$where":"this.age<this.brotherAge"}
... )
{ "_id" : ObjectId("60860de108e9f867708fcb4c"), "name" : "joe", "age" : "12", "brotherAge" : 20 }
```
#### 10)分页--用在find()后面
**skip(跳过的文档书) 与 limit(返回的文档数)**
```mysql
> db.c1.find({},{"_id":0}).skip(3)
> db.c1.find({},{"_id":0}).limit(3)
> db.c1.find({},{"_id":0}).skip(3).limit(10)
```
#### 11)排序--用在find()后面
**sort({"key" : 1|-1 , .... })** 1=>升序,-1=>降序
> tip : 分页和排序同时使用 :
>
> find().sort().skip().limit()
```mysql
> db.c1.find({},{"_id":0}).sort({"age":-1})
> db.c1.find({},{"_id":0}).sort({"age":-1,"name":1})
> db.c1.find({},{"_id":0}).sort({"name":1}).skip(5).limit(5)
```
### 7. 查询一条文档—findOne()
```mysql
> db.c1.findOne()
{
"_id" : ObjectId("608222976baab5abac64613d"),
"age" : 23,
"username" : "mike"
}
> db.c1.findOne(
... {"name":/o/i , "age":{"$gt":18}}
... )
{
"_id" : ObjectId("608225516baab5abac646148"),
"name" : "isoft9",
"gender" : "M",
"age" : 19
}
```
### 8. count()
**语法 : db.集合名.count(条件)**
作用:返回满足条件的文档数量
```mysql
> db.c1.count()
20
> db.c1.count(
... {"name":/o/i}
... )
12
> db.c1.count(
... {"$and":[
... {"age":{"$gte":10}},
... {"age":{"$lte":20}}
... ]}
... )
15
```
### 9. distinct()
**语法 :db.集合名.distinct("key" [,条件])**
作用 :返回满足条件的文档指定key的不同值有哪些,返回值类型是数组
```mysql
> db.c1.distinct("gender")
[ "F", "M" ]
> db.c1.distinct("age")
[ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23 ]
> db.c1.distinct("age" , {"name":/o/i})
[ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]
```
## 0425—find查询(笔记见上方)
## 0426—查询、游标、索引
### 1. find查询(笔记见上方)
### 2. findOne()(笔记见上方)
### 3. count()(笔记见上方)
### 4. distinct()(笔记见上方)
### 5. 管道(自学)
https://www.cnblogs.com/yunlongaimeng/p/11460291.html
https://www.cnblogs.com/qlqwjy/p/8652555.html
### 6. 游标
#### 1)游标
不是查询结果,而是查询的返回接口或者记录指针。
通过游标可以逐行读取数据。
声明游标时不执行查询,通过游标提取数据时才执行查询
#### 2)游标相关操作
-- 声明游标
**var 游标 = 查询**
-- 判断游标是否到达了末尾
**游标.hasNext()** 返回一个boolean
-- 取出游标所指向的下一个文档
**游标.next()**
-- 循环遍历游标
**while(游标.hasNext()) {**
...
游标.next()
...
**}**
或者
**游标.forEach(回调函数)**
-- 将游标所对应的查询结果文档转为一个数组
**数组 = 游标.toArray()**
```mysql
> var cur = db.c2.find()
>
> print(cur.hasNext())
true
>
> print(cur.next())
[object BSON]
>
> printjson(cur.next())
{
"_id" : ObjectId("60850b58b2e7bc32dfacb338"),
"name" : "B",
"fruit" : [
"apple",
"orance",
"peach"
]
}
> while(cur.hasNext()){
... printjson(cur.next())
... }
{
"_id" : ObjectId("60850b58b2e7bc32dfacb339"),
"name" : "C",
"fruit" : [
"cherry",
"banana",
"apple"
]
}
{
"_id" : ObjectId("60850b58b2e7bc32dfacb33a"),
"name" : "D",
"fruit" : [
"cherry"
]
}
> var cur2 = db.c1.find({"gender":"F"})
> cur2.forEach(function(doc){
... printjson(doc)
... print(doc.name+"--"+doc.gender)
... })
> print(cur2.hasNext())
false
> var cur3 = db.c1.find({"age":{"$gte":20}})
> age20 = cur3.toArray()
[
{
"_id" : ObjectId("608222976baab5abac64613d"),
"age" : 23,
"username" : "mike"
},
{
"_id" : ObjectId("608225516baab5abac646149"),
"name" : "isoft10",
"gender" : "F",
"age" : 20
},
{
"_id" : ObjectId("6084c1e495c3b0b8ed942f7a"),
"name" : 20,
"age" : 20
},
{
"_id" : ObjectId("6084c37995c3b0b8ed942f7b"),
"name" : [
"r",
"o"
],
"age" : 20
}
]
> print(age20)
[object BSON],[object BSON],[object BSON],[object BSON]
> printjson(age20)
....
```
## 第三章 MongoDB的索引
### 1. 简介
**索引通常能够极大的提高查询的效率**,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文档并选取那些符合查询条件的文档。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
### 2. 优缺点
-- 优点
极大的提高查询的效率
-- 缺点
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。
【很少对集合进行读取操作,建议不使用索引】
### 3. 限制
- 集合中索引不能超过64个
- 索引名的长度不能超过128个字符
- 一个复合索引最多可以有31个字段
### 4. 查询性能分析
**语法 :db.集合名.find(..).explain(["executionStats"])**
显示结果 :关注 winningPlan ==>stage|inputState ==>stage的值
COLLSCAN 全集和扫描
IXSCAN 索引扫描
IDHACK _id为条件应用自动为该key创建的索引进行扫描
### 5. 索引的类型
-- 索引类型
单键索引、复合索引、多键索引、哈希索引、全文索引、地理空间索引
-- 索引属性
唯一索引、局部索引、稀疏索引、TTL索引
### 6. 创建索引
**语法形式 : db.createIndex({"key":1|-1 , ...} [, 额外选项] )**
额外选项 :
| background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为**false**。 |
| :---------------------------------- | ------------- | ------------------------------------------------------------ |
| unique(唯一索引) | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为**false**. |
| name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。<br />默认索引名是key_1\|key_-1 |
| partialFilterExpression(局部索引) | 条件 | 针对key满足条件的文档创建索引 |
| sparse(稀疏索引) | Boolean | 稀疏索引(或者称间隙索引)就是只包含有索引字段的文档的条目,跳过索引键不存在的文档 |
| expireAfterSeconds(TTL索引) | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
| v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
### 7. 删除索引
db.集合名.dropIndex(索引名)
db.集合名.dropIndexes()
### 8 . 查看索引
db.集合.getIndexes()
> tip : MongoDB中为集合默认为_id属性创建一个单键索引
```mysql
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test12.c1"
}
]
```
### 9. 索引类型
#### 1)单键索引
单键索引(Single Field Indexes)顾名思义就是单个字段作为索引列,mongoDB的所有collection默认都有一个单键索引_id。
```mysql
> db.c1.createIndex(
... {"name":1},
... {"name":"name_index1"}
... )
> db.c1.createIndex({"age":1})
> db.c1.dropIndex("age_1")
> db.c1.dropIndex("name_index1")
```
#### 2)复合索引
复合索引(Compound Indexes)指一个索引包**含多个key**,用法和单键索引基本一致。使用复合索引时要注意字段的顺序。mongoDB中一个复合索引最多可以包含32个字段。
```mysql
> db.c1.find(
... {"$and":[
... {"name":/o/i},
... {"age":{"$gt":15}}
... ]}
... ).explain()
{
"queryPlanner" : {
"....
"winningPlan" : {
"stage" : "COLLSCAN",
....
},
....
}
>
> db.c1.createIndex(
... {"name":1 , "age":1}
... )
> db.c1.getIndexes()
[
....
{
"v" : 2,
"key" : {
"name" : 1,
"age" : 1
},
"name" : "name_1_age_1",
"ns" : "test12.c1"
}
]
> db.c1.find( {"$and":[ {"name":/o/i}, {"age":{"$gt":15}} ]} ).explain()
{
"queryPlanner" : {
....
"winningPlan" : {
....
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1,
"age" : 1
},
"indexName" : "name_1_age_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"name" : [
"name"
],
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
.....
}
```
#### 3)多键索引
多键索引(mutiKey Indexes)是建在数组上的索引 。
```mysql
# 准备数据
> db.classes.insert([
... ... {
... ... "classname":"class1",
... ... "students":[{name:'jack',age:20},
... ... {name:'tom',age:22},
... ... {name:'lilei',age:25}]
... ... },
... ... {
... ... "classname":"class2",
... ... "students":[{name:'lucy',age:20},
... ... {name:'jim',age:23},
... ... {name:'jarry',age:26}]
... ... }
... ... ])
> db.classes.createIndex({"students.age":1})
> db.classes.find({"students.age":20}).explain()
```
#### 4)哈希索引
哈希索引(hashed Indexes)就是将field的值进行hash计算后作为索引,其强大之处在于实现0(1)查找,当然用哈希索引最主要的功能也就是实现**定值查找**(key=value),对于经常需要排序或查询范围查询的集合不要使用哈希索引。
**语法 : {"key":"hashed"}**
```mysql
> db.c1.createIndex({"gender":"hashed"})
> db.c1.find({"gender":"F"}).explain()
```
### 10. 索引属性
#### 1)唯一索引
唯一索引(unique indexes)用于为collection添加唯一约束,即**强制要求collection中的索引key没有重复值**。
语法 :**{unique:true}**
【注】创建该索引前,创建索引的key不能有重复的value!
```mysql
> db.c1.createIndex(
... {"name":-1},
... {"unique":true}
... )
{
"ok" : 0,
"errmsg" : "E11000 duplicate key error collection: test12.c1 index: name_-1 dup key: { name: \"zhangsan\" }",
"code" : 11000,
"codeName" : "DuplicateKey",
"keyPattern" : {
"name" : -1
},
"keyValue" : {
"name" : "zhangsan"
}
}
# 移出name的重复值
> db.c1.createIndex( {"name":-1}, {"unique":true} )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}
{
"v" : 2,
"unique" : true,
"key" : {
"name" : -1
},
"name" : "name_-1",
"ns" : "test12.c1"
}
> db.c1.insert({"name":"zhangsan"})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test12.c1 index: name_-1 dup key: { name: \"zhangsan\" }"
}
})
```
#### 2)局部索引
只对collection的一部分添加索引。创建索引的时候,根据过滤条件判断是否对document添加索引,对于没有添加索引的文档查找时采用的全表扫描,对添加了索引的文档查找时使用索引。
语法 : {"partialFilterExpression" : {条件}}
```mysql
> db.c1.createIndex({"age":1},{"partialFilterExpression":{"age":{"$gt":15}}})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"age" : 1
},
"name" : "age_1",
"ns" : "test12.c1",
"partialFilterExpression" : {
"age" : {
"$gt" : 15
}
}
}
]
> db.c1.find({"age":{"$lt":15}}).explain()
> db.c1.find({"age":{"$gt":18}}).explain()
```
#### 3)稀疏索引
稀疏索引(sparse indexes)在有索引字段的document上添加索引,如在address字段上添加稀疏索引时,只有document有address字段时才会添加索引。而普通索引则是为所有的document添加索引,使用普通索引时如果document没有索引字段的话,设置索引字段的值为null。
语法 :**{"sparse": true}**
```mysql
# 数据准备
> db.scores.insert([
... { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" },
... { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 },
... { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }])
> db.scores.createIndex( { score: 1 } , { sparse: true } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.scores.find( { score: { $lt: 90 } } )
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
# 由于文档newbie并不包含score键,因此该文档不会出现在稀疏索引之中,也就不会被查询返回
> db.scores.find( { score: { $lt: 90 } } ).explain()
{
"queryPlanner" : {
。。。
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"score" : 1
},
"indexName" : "score_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"score" : [ ]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"score" : [
"[-inf.0, 90.0)"
]
}
}
},
。。。。
}
```
#### 4)TTL索引
TTL索引(time-to-live index 具有生命周期的索引)是一种特殊的单键索引,用于**设置document的过期时间**,mongoDB会在document过期后将其删除,TTL非常容易实现类似缓存过期策略的功能。
语法 : **{"expireAfterSeconds" : 秒值}**
【注】TTL索引只能设置**在date类型字段(或者包含date类型的数组)**上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;
```mysql
-- 数据准备 --
> db.logs.insert([
... {_id:1,createtime:new Date(),msg:"log1"},
... {_id:2,createtime:new Date(),msg:"log2"},
... {_id:3,createtime:new Date(),msg:"log3"},
... {_id:4,createtime:new Date(),msg:"log4"}
... ])
> db.logs.createIndex(
... {"createtime":1},
... {"expireAfterSeconds":20}
... )
{
... "v" : 2,
... "key" : {
... "createtime" : 1
... },
... "name" : "createtime_1",
... "ns" : "test12.logs",
... "expireAfterSeconds" : 20
... }
# 等待一会儿去执行查看
> db.logs.find()
```
【注】TTL索引只能设置在date类型字段(或者包含date类型的数组)上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;
### 11. 全文索引
【在mongodb中,每个数据**集合只能创建一个全文索引**】
全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
这个过程类似于通过字典中的检索字表查字的过程。
**语法:db.集合名.ensureIndex({字段名 : "text“});**
```mysql
-- 数据准备
> posts=[
{
"post_text": "enjoy the mongodb articles on Runoob",
"tags": [
"mongodb",
"runoob"
]
},
{
"post_text": "enjoy the python articles on Mrs Gao",
"tags": [
"python",
"gao"
]
},
]
> db.posts.insert(posts)
-- 创建全文索引
> db.posts.ensureIndex({"post_text":"text"})
-- 执行查询 , 使用索引
> db.posts.find({"$text":{"$search":"runoob"}})
```
**使用全文索引:使用全文索引跟使用其他索引不一样,我们不再需要key的名字,而是直接使用$text,$search进行查询。**
### 12. 地理空间索引
随着移动设备的应用的爆发式增长,有一种查询变得越来越流行:找到离当前位置最近的N个场所。MongoDB为坐标平面查询提供了专门的索引,称作地理空间索引。地理空间索引分2dsphere索引和2d索引。
## 0427
## 第四章 MongoDB GridFS
### 1. GridFS简介
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。
GridFS 可以更好的存储大于16M的文件(MongoDB的BSON格式的数据(文档)存储有尺寸限制,最大为16M)。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。
### 2. 使用场景
-- 如果您的文件系统在一个目录中存储的文件数量有限(太多将会影响文件的打开速度),你可以使用GridFS存储尽可能多的文件。
-- 当你想访问大型文件的部分信息,却不想加载整个文件到内存时,您可以使用GridFS存储文件,并读取文件部分信息,而不需要加载整个文件到内存。
-- 你想让你的文件和元数据自动同步并部署在多个系统和设施,你可以使用GridFS实现分布式文件存储。
### 3. 存储相关集合
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。
以下是简单的 **fs.files** 集合文档:
```
{
"_id": <ObjectId>, // 文档id,唯一标识
"filename": <string>,
"chunkSize": <num>,
"uploadDate": <ISODate>,
"md5": <string>,
"length": <num>,
"metadata":<dataObject>
}
```
以下是简单的 **fs.chunks** 集合文档:
```
{
"_id": <ObjectId>,
"files_id": <ObjectId>, // 对应fs.files文档的 _id
"n": <num>, // 序号,标识文档的第几个chunk
"data": <binary> // 文件二进制数据,不要显示该属性值
}
```
### 4. GridFS操作
使用MongoDB安装目录"bin/" 的mongofiles 工具
**命令格式 :**
**mongofiles \<options> \<command> \<filename or _id>**
```
command :
list , search , put , get , delete
put_id , get_id , delete_id
options :
-d 数据库名
-l 文件位置及名称
```
```mysql
# 上传一个20K+的文件
D:\green_software\mongodb\bin>mongofiles -d gridfs -l "d:\OO\Pictures\skb.png" put m1.png
2021-04-27T08:53:12.180+0800 connected to: mongodb://localhost/
2021-04-27T08:53:12.350+0800 added gridFile: m1.png
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.000GB
local 0.000GB
test12 0.001GB
> use gridfs
switched to db gridfs
> show collections
fs.chunks
fs.files
> db.fs.files.find().pretty()
{
"_id" : ObjectId("60876078661647544d94250a"),
"length" : NumberLong(24775),
"chunkSize" : 261120,
"uploadDate" : ISODate("2021-04-27T00:53:12.348Z"),
"filename" : "m1.png",
"metadata" : {
}
}
> db.fs.chunks.find({} , {"data":0})
{ "_id" : ObjectId("60876078661647544d94250b"), "files_id" : ObjectId("60876078661647544d94250a"), "n" : 0 }
# 上传一个 2M+ 的文件
D:\green_software\mongodb\bin>mongofiles -d gridfs -l d:\OO\Pictures\mongofile.jpg put m2.jpg
2021-04-27T08:59:39.923+0800 connected to: mongodb://localhost/
2021-04-27T08:59:40.008+0800 added gridFile: m2.jpg
D:\green_software\mongodb\bin>mongo
> use gridfs
switched to db gridfs
> show collections
fs.chunks
fs.files
> var fid=db.fs.files.findOne({"filename":"m2.jpg"})._id
> fid
ObjectId("60876ab999b849dc1310485c")
> db.fs.chunks.find({"files_id":fid},{"data":0})
{ "_id" : ObjectId("608761fb6f324480d836e155"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 0 }
{ "_id" : ObjectId("608761fb6f324480d836e156"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 1 }
{ "_id" : ObjectId("608761fb6f324480d836e157"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 2 }
{ "_id" : ObjectId("608761fb6f324480d836e158"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 3 }
{ "_id" : ObjectId("608761fb6f324480d836e159"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 4 }
{ "_id" : ObjectId("608761fb6f324480d836e15a"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 5 }
{ "_id" : ObjectId("608761fb6f324480d836e15b"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 6 }
{ "_id" : ObjectId("608761fb6f324480d836e15c"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 7 }
{ "_id" : ObjectId("608761fb6f324480d836e15d"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 8 }
{ "_id" : ObjectId("608761fb6f324480d836e15e"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 9 }
{ "_id" : ObjectId("608761fb6f324480d836e15f"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 10 }
# 测试其他命令
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:08:01.102+0800 connected to: mongodb://localhost/
m1.png 24775
m2.jpg 2852907
D:\green_software\mongodb\bin>mongofiles -d gridfs search m1.jpg
2021-04-27T09:08:40.609+0800 connected to: mongodb://localhost/
D:\green_software\mongodb\bin>mongofiles -d gridfs search m1.png
2021-04-27T09:08:45.972+0800 connected to: mongodb://localhost/
m1.png 24775
D:\green_software\mongodb\bin>mongofiles -d gridfs get m1.png
2021-04-27T09:09:21.635+0800 connected to: mongodb://localhost/
2021-04-27T09:09:21.649+0800 finished writing to m1.png
D:\green_software\mongodb\bin>mongofiles -d gridfs -l d:\mongofile.jpg get m2.jpg
2021-04-27T09:10:41.794+0800 connected to: mongodb://localhost/
2021-04-27T09:10:41.832+0800 finished writing to d:\mongofile.jpg
D:\green_software\mongodb\bin>mongofiles -d grifs delete m1.png
2021-04-27T09:11:25.403+0800 connected to: mongodb://localhost/
2021-04-27T09:11:25.415+0800 successfully deleted all instances of 'm1.png' from GridFS
D:\green_software\mongodb\bin>mongofiles -d grifs delete m2.jpg
2021-04-27T09:11:35.724+0800 connected to: mongodb://localhost/
2021-04-27T09:11:35.737+0800 successfully deleted all instances of 'm2.jpg' from GridFS
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:11:46.170+0800 connected to: mongodb://localhost/
m1.png 24775
m2.jpg 2852907
D:\green_software\mongodb\bin>net stop mongodb
MongoDB 服务正在停止.
MongoDB 服务已成功停止。
D:\green_software\mongodb\bin>net start mongodb
MongoDB 服务正在启动 ..
MongoDB 服务已经启动成功。
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:14:40.243+0800 connected to: mongodb://localhost/
```
## 第五章 数据库的备份与还原
### 1. 备份数据库 mongodump
**命令 :mongodump -h host:port - u username -p password -d dbName -o directory**
```
-h 服务器地址【不写默认本机】
:port 端口号(不写默认27017)
-u 账号
-p 密码
-d 需要备份的数据库实例,不写导出全局
-o 备份的数据存放位置,例如:c:\data\dump【目录已存在】
```
```mysql
# 备份本机服务器下所有数据库
D:\green_software\mongodb\bin>mongodump -o d:\mongodump
# 备份数据库 test12
D:\green_software\mongodb\bin>mongodump -d test12 -o d:\mongodump
```
### 2. 还原数据库 mongorestore
**命令 :mongorestore - h host:port - u username -p password -d dbName --drop directory**
```mysql
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
test12 0.001GB
> use test12
switched to db test12
> db.dropDatabase()
{ "dropped" : "test12", "ok" : 1 }
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
> exit
D:\green_software\mongodb\bin>mongorestore -d test d:\mongodump\test12
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
test 0.000GB
> use test
switched to db test
> show collections
c1
c2
c3
c4
c5
classes
logs
posts
scores
user
> db.coll.insert({"name":123})
WriteResult({ "nInserted" : 1 })
> exit
bye
D:\green_software\mongodb\bin>mongorestore -d test --drop d:\mongodump\test12
```
## 0429
### 1. Linux下MongoDB的配置(笔记见上方)
### 2. MongoDB的复制系统(见MongoDB复制笔记)
## 第六章 :MongoDB的复制系统
## 0428
## 第七章:其他语言访问MongoDB
### 1. 使用Java语言访问
> tip : 使用原生态方式访问 MongoDB
>
> SpringBoot中访问MongoDB,spring-boot-starter-data-mongodb
#### 1)添加jar包 ==> Maven 依赖项
```XML
<properties>
<mongo.driver.version>3.12.7</mongo.driver.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo.driver.version}</version>
</dependency>
</dependencies>
```
```java
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
```
#### 2)连接服务器
```java
/* mongodb://user:password@host:port */
String connStr = "mongodb://host:27017";
MongoClientURI uri = new MongoClientURI(connStr) ;
MongoClient mongoClient = new MongoClient(uri) ;
```
#### 3)选择数据库
```java
MongoDatabase db = mongoClient.getDatabase("dbname") ;
```
#### 4)选择集合
```java
MongoCollection<Document> collection = db.getCollection("collname")
```
#### 5)执行CRUD
-- insertOne() , insertMany()
```java
public void insert(MongoCollection<Document> collection) {
Document doc1 = new Document() ;
doc1.append("name" , "rose")
.append("pwd" , "123456")
.append("mobile" , "13812344321")
.append("gender" , "F") ;
collection.insertOne(doc1);
System.out.println(doc1);
/////////////////
List<Document> list = new ArrayList<>() ;
list.add(
new Document().append("name" , "java").append("pwd" , "111").append("gender" , "M")
) ;
list.add(
new Document().append("name" , "python").append("pwd" , "222").append("gender" , "F")
) ;
collection.insertMany(list);
System.out.println("insert many!");
}
```
-- find()
Bson
Filters 构造条件 , 返回 Bson对象
FindIterable 集合调用find()的返回值
MongoCursor FindIterable对象调用iterable() 获取
Document
```java
public void select(MongoCollection<Document> collection) {
// 查询所有文档
// FindIterable findIterable = collection.find() ;
// 条件过滤
// Bson bson = Filters.eq("name" , "rose") ;
Bson bson = Filters.regex("name" , "o" , "i") ;
FindIterable findIterable = collection.find(bson) ;
MongoCursor cursor = findIterable.iterator() ;
while(cursor.hasNext()) {
Document doc = (Document) cursor.next();
System.out.println(doc);
System.out.println(doc.getObjectId("_id") + " , " + doc.getString("name"));
}
}
```
```java
public void login(MongoCollection<Document> collection) {
String name = "java" ;
String pwd = "111" ;
Bson bson1 = Filters.eq("name" , name) ;
Bson bson2 = Filters.eq("pwd" , pwd) ;
Bson filterBson = Filters.and(bson1 , bson2) ;
FindIterable findIterable = collection.find(filterBson) ;
MongoCursor cursor = findIterable.iterator() ;
Document doc = null ;
if(cursor.hasNext()) {
doc = (Document) cursor.next() ;
}
System.out.println(doc);
}
```
-- updateOne() , updateMany()
UpdateResult
```java
public void update(MongoCollection<Document> collection) {
Bson filterBson = Filters.regex("name" , "o" , "i") ;;
Document doc = new Document("$set" ,
new Document().append("age" , 28).append("pwd" , 321)
);
UpdateResult updateResult = collection.updateMany(filterBson , doc) ;//collection.updateOne(filterBson , doc) ;
System.out.println(updateResult.getModifiedCount());
}
```
-- deleteOne() , deleteMany()
DeleteResult
```java
public void delete(MongoCollection<Document> collection) {
Bson filterBson = Filters.eq("_id" , new ObjectId("6088b901e819882d57589802")) ;
DeleteResult deleteResult = collection.deleteOne(filterBson) ;
System.out.println(deleteResult);
}
```
### 2. 使用Python语言访问MongoDB
https://www.runoob.com/python3/python-mongodb.html
#### 1) 相关库 pymongo
```
$ python -m pip install pymongo
```
#### 2)连接服务器
```python
import pymongo
conn = pymongo.MongoClient("localhost|127.0.0.1|host" , 27017 )
```
#### 3)选择数据库
```python
db = conn['dbname']
# db = conn.dbname
```
#### 4)选择集合
```python
coll = db['collname']
# coll = db.collname
```
```python
import pymongo
from bson import ObjectId
def conn() :
conn = pymongo.MongoClient("127.0.0.1" , 27017)
db = conn['test']
coll = db['python']
# print(coll)
return coll
```
#### 5)执行CRUD
-- insert_one() , insert_many()
```python
def insert(coll) :
user = {
"name" : "zhangsan2" ,
"pwd" : "123" ,
"gender" : "F"
}
result = coll.insert_one(user)
print(result.inserted_id)
print(user)
#############
u1 = {
"name" : "lisi" ,
"pwd" : "111" ,
"gender" : "M"
}
u2 = {
"name": "wangwu",
"pwd": "222",
"gender": "F"
}
results = coll.insert_many([u1 , u2])
print(results.inserted_ids)
print(u1)
print(u2)
```
-- find_one() , find()
```python
def findAll(coll) :
results2 = coll.find()
data = []
for doc in results2:
data.append({
"_id" : str(doc['_id']) ,
"name" : doc['name'] ,
"pwd" : doc['pwd'] ,
"gender" : doc['gender'],
# "email" : doc['email']
})
print(data)
print("-------------")
```
```python
def find(coll) :
row = coll.find_one()
print(row)
print(type(row))
print("-----------------")
row = coll.find_one({"name" : "zhangsan" , "pwd" : "123321"}) ;
print(row)
print("-------------")
results = coll.find({
"name" : {"$regex" : "a" , "$options" : "i"} ,
"gender" : "F"
})
print(results)
print(type(results))
for doc in results :
print(doc)
print("-------------")
num = coll.count_documents({})
# num = coll.count_documents({"name": "zhangsan"})
print(num)
results2 = coll.find().sort("name" , -1)
for doc in results2 :
print(doc)
print("-------------")
results3 = coll.find().skip(1).limit(2)
for doc in results3:
print(doc)
```
-- update_one() , update_many()
```python
def update(coll) :
query = {"name" : {"$regex" : "zhangsan"}}
new_value = {"$set" : {
"email" : "zzss@qq.com" ,
"pwd" : "686868"
}}
# result = coll.update_one(query , new_value)
# print(result.modified_count)
results = coll.update_many(query , new_value)
print(results.modified_count)
```
-- delete_one() , delete_many()
```python
def delete(coll) :
# query = {"name" : {"$regex" : "zhangsan"}}
query = {"_id" : ObjectId('6088ff8d5ed9981a6f6f3d45')}
result = coll.delete_one(query)
# result = coll.delete_many(query)
print(result.deleted_count)
```
NoSQL数据库技术:
出勤 20%,作业20%,笔试60%
课程目标:
掌握MongoDB的安装与配置--Windows,Linux(CentOS)
掌握MongoDB中基本操作命令
掌握MongoDB中文档相关操作——CRUD
了解MongoDB的复制系统,副本集的配置
掌握Python、Java访问MongoDB
理解索引的意义,学会几种常见索引的创建
## 0422
## 第一章 :MongoDB基础
### 1. 关系型数据库与非关系型数据库
1)关系型数据库
关系型数据库建立在关系型数据模型的基础上,是借助于集合代数等数学概念和方法来处理数据的数据库。Oracle、MySQL等都是。
在关系型数据库中,实体以及实体间的练习均由单一的结构类型来表示单一的数据结构----关系(表文件)。关系数据库的表采用二维表格来存储数据,是一种按行与列排列的具有相关信息的逻辑组,它类似于Excel工作表。一个数据库可以包含任意多个数据表。
在用户看来,一个关系模型的逻辑结构是一张二维表,由行和列组成。这个二维表就叫关系,通俗地说,一个关系对应一张表。
2)非关系型数据库
NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。
在信息化时代背景下,互联网数据增长迅猛,数据不仅仅是传统的结构化数据,还包含了大量的非结构化和半结构化数据,关系型数据库无法存储此类数据。
大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
### 2. NoSQL数据库分类
<https://baike.baidu.com/item/NoSQL/8828247?fromtitle=%E9%9D%9E%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93>
键值数据库 :如 Redis
列存储数据库 :如 HBase
文档型数据库 :如MongoDB
图形数据库:如Neo4j
### 3. MongoDB简介
1)简介
**分布式**文件存储的数据库,由C++编写。**文档型**非关系型数据库。
MongoDB 将数据存储为一个文档,**数据结构由键值(key=>value)对**组成。MongoDB 文档类似于 JSON 对象的Bson格式数据。
```
{
“name” : “张三" ,
“age” : 18 ,
“groups” : ["足球队" , "IT队"] ,
“birth” : new Date(2000,2,13)
}
```
支持的数据结构非常松散,是类似json的bson格式,存储比较复杂的数据类型。
2)集合和文档
集合 ==》表(table);文档的集合,集合中的文档是各式各样的。
文档:key+value存储的数据 ==》记录,元组;就是键值对的一个有序集。
### 4. MongoDB的安装
**-- Windows系统下的安装(解压缩配置)**
【控制台下,以**管理员**身份操作】
安装 :解压缩 ==> 安装服务 ==> 开始使用
-- 下载zip包
-- 解压(不要出现中文路径、空格路径)
-- 解压后在文件中创建 "data" 与 "log" 两个目录
-- 以”**管理员**“身份打开cmd,**定位到MongoDB的解压缩bin目录**,然后执行如下命令
```
[安装服务--在MongoDB的解压缩bin目录下,如果配置了环境变量就任意]
d:\green_software\mongodb\bin> mongod --install --dbpath D:\green_software\mongodb\data --logpath D:\green_software\mongodb\log\mongodb.log
是否成功:打开服务 (启动 services.msc) , 查找是否由MongoDB的服务
[启动、停止、重启服务]
net start|stop mongodb
[移除服务]
先停止服务再操作
mongod --remove
[进入MongoDB的客户端]
mongo
```
配置环境变量
path变量增加 **<mongodb解压缩路径>\bin**
使用:启动服务 ==> 客户端
卸载:停止服务 ==> 移出服务 ==> 删除文件夹
**-- Linux系统下安装(CentOS7.8)**
-- 关闭防火墙
[root@CentosA ~]# systemctl stop firewalld
[root@CentosA ~]# systemctl disable firewalld
-- 选择好软件安装位置,将压缩包(.tar.gz ==>.tgz)传输到该位置
/usr/local
[root@CentosA ~]# cd /usr/local
-- 定位到 /usr/local , 解压
[root@CentosA local]# tar -zxvf xxx.tgz
-- 将压缩包重命名
[root@CentosA local]# mv xxx /usr/local/mongodb
-- 创建 data/ 与 log/ 目录
[root@CentosA local]# mkdir -p /usr/local/mongodb/data /usr/local/mongodb/log
-- 启动服务
[root@CentosA local]# cd /usr/local/mongodb/bin
[root@CentosA bin]# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --logappend --port=27017 --fork
-- 进入到MongoDB客户端
[root@CentosA bin]# ./mongo
```
systemctl stop firewalld
systemctl disable firewalld
cd /usr/local/
ls
tar -zxvf mongodb-linux-x86_64-rhel70-4.4.5.tgz
mv mongodb-linux-x86_64-rhel70-4.4.5 /usr/local/mongodb
ls
mkdir -p /usr/local/mongodb/data /usr/local/mongodb/log
cd mongodb/
ls
cd bin
ls
./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --logappend --port=27017 --fork
./mongo
```
-- 停止服务
```
方式一:在MongoDB的客户端下
> use admin
switched to db admin
> db.shutdownServer();
server should be down...
方式二 :在Shell命令下
[root@CentosA bin]# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --shutdown
方式三 : 杀死进程
[root@CentosA bin]# kill -9 进程号
```
-- 查看端口号占用情况
[root@CentosA bin]# netstat -tunlp | grep 27017
##
## 0423
## 第二章 MongoDB的基本操作
### 1. 基本操作
1)获取当前所访问的服务器对象
db.getMongo()
2)查看当前服务器下的数据库
show dbs | show databases
3)打开数据库
use 库名
> tip : MongoDB中不需要创建数据库,也不需要创建集合。
>
> 当忘数据库中插入了文档或者创建集合,会检查数据库是否存在,不存在自动创建数据库;
>
> 也会检查集合是否存在,不存在自动创建集合。
```
1. 库名全部小写,不允许使用"_"以外的其他字符,基本上就是ASCII范围的字母和数字
2. 不允许数字开头
3. 最多64个字符
4. 库名将以文件夹的形式存在
5. 系统中已存在了 admin , config , local 不要使用这些名称
```
4)查看当前数据库下集合
show collections
5)查看当前打开的数据库
db.getName()
6)删除数据库
【注】先打开要删除的数据
db.dropDatabase()
7)创建集合
db.createCollection("集合名")
```
1. 集合名全部小写,不允许使用"_","."以外的其他字符
2. 不允许数字开头
3. 最多64个字符
4. 禁止使用 system 开头
```
8)删除集合
db.集合名.drop()
### 2. MongoDB支持的数据类型
```
-- null
-- 布尔值 true与false
-- 数值
> x = {
... "k1":3, double
... "k2":3.14, double
... "k3":NumberInt(3), 32-int
... "k4":NumberLong(3) 64-int
... }
{ "k1" : 3, "k2" : 3.14, "k3" : NumberInt(3), "k4" : NumberLong(3) }
-- 字符串
-- 日期
使用new Date()创建的将是ISODate类型的日期对象,同样可以使用ISODate()
> data = {
... "d1":new Date(),
... "d2":ISODate(),
... "d3":new Date(2020,3,14),
... "d4":new Date("2020-03-14")
... }
{
"d1" : ISODate("2021-04-23T01:05:40.849Z"),
"d2" : ISODate("2021-04-23T01:05:40.850Z"),
"d3" : ISODate("2020-04-13T16:00:00Z"),
"d4" : ISODate("2020-03-14T00:00:00Z")
}
> d2 = {"dd":new Date("2020-04-12T10:20:20Z")}
{ "dd" : ISODate("2020-04-12T10:20:20Z") }
-- 数组
> m = {
... "k1":["hello",123,true,34]
... }
{
"k1" : [
"hello",
123,
true,
34,
5
]
}
-- 内嵌文档
> n = {
... "name":"rose",
... "addr":{"prov":"HeBei","city":"Shijiazhuang"}
... }
{
"name" : "rose",
"addr" : {
"prov" : "HeBei",
"city" : "Shijiazhuang"
}
}
-- 正则表达式
> t={
... "pro":/hello/i
... }
{ "pro" : /hello/i }
-- ObjectID 类型
构成 :时间戳+机器+PID+计数器==>24位十六进制数字组成的字符串
> k = {
... id: ObjectId()
... }
{ "id" : ObjectId("60763d98d5046809b857e4dc") }
-- 二进制数据
-- 代码
> u = {
... "pro" : function(){
... return "hello";
... }
... }
{ "pro" : function(){ return "hello"; } }
```
### 3. 插入文档
**语法:db.集合名.insert(文档数据)**
**集合存在,直接插入数据;集合不存在,创建。**
【说明】文档数据是BSON格式,key必须使用双引号引起来。但是在MongoDB下操作时,key可以不使用双引号。
-- 每次插入一条记录
```mysql
> db.getName()
test12
> show collections
> db.c1.insert({
... "name":"rose",
... "age":18
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
> db.c1.insert({
... "name":"joe",
... "age":20,
... "gender":"M"
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
> db.c1.insert({
... "name":"mike",
... "age":22,
... "gender":null
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
> db.c1.insert({
... "_id":4,
... "name":"X1"
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
{ "_id" : 4, "name" : "X1" }
```
-- 批量插入数据
```mysql
> datas = [
... {"name":"zs" , "age":12} ,
... {"name":"lisi", "email":"ls@qq.com" , "gender":"F"}
... ]
[
{
"name" : "zs",
"age" : 12
},
{
"name" : "lisi",
"email" : "ls@qq.com",
"gender" : "F"
}
]
> db.c1.insert(datas)
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
```
-- 支持js语法
(不是批量插入,而是insert执行了多次)
```mysql
> for(i = 1 ;i <=10 ;i++) {
... if(i%2==0) {
... db.c1.insert({"name":"isoft"+i , "gender":"F"})
... } else {
... db.c1.insert({"name":"isoft"+i , "gender":"M"})
... }
... }
WriteResult({ "nInserted" : 1 })
```
### 4. 更新文档
#### 1)update()
语法 :db.集合名.update(条件,更新后数据 [,{upsert:boolean , multi:boolean }])
```
条件描述 {}
更新后数据描述方式为 :
{“修改器" : { ... } , .... }
【注】不使用修改器,是进行替换
upsert : 按照条件没有找到匹配文档则执行插入(true-插入,false-不插入 默认)
multi:匹配成功所有数据都修改(true-是,false-否 默认)
```
| 修改器名称 | 作用 |
| ---------- | ------------------------------------------------------------ |
| $set | 修改key值,如果key在文档中不存在,则插入该属性以及值到条件匹配文档 |
| $inc | 在原值基础上递增减 |
| $rename | 重命名key |
| $unset | 根据key删除value,形式 {"$unset":{”key" : true}} |
```mysql
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
{ "_id" : 4, "name" : "X1" }
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zs", "age" : 12 }
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646140"), "name" : "isoft1", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646141"), "name" : "isoft2", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646142"), "name" : "isoft3", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646143"), "name" : "isoft4", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646144"), "name" : "isoft5", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646145"), "name" : "isoft6", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646146"), "name" : "isoft7", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646147"), "name" : "isoft8", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646148"), "name" : "isoft9", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646149"), "name" : "isoft10", "gender" : "F" }
# 演示不使用插入器
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
> db.c1.update(
... {"name":"joe"},
... {"email":"joe@qq.com"}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "email" : "joe@qq.com" }
# 插入器使用
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zs", "age" : 12 }
# 将 zs 的name修改为 zhangsan,email修改为zs@qq.com
> db.c1.update(
... {"name":"zs"},
... {"$set":{"name":"zhangsan","email":"zs@qq.com"}}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
# 将 rose的年龄 -1
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":-1}}
... )
# 将mike的gender属性移除,age+1,name属性修改为username
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
> db.c1.update(
... {"name":"mike"},
... {
... "$unset":{"gender":true},
... "$inc":{"age":1},
... "$rename":{"name":"username"}
... }
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
# 选项 upsert的使用
> db.c1.update(
... {"name":"Y1"},
... {"$set":{"name":"rose","age":8,"email":"rr@qq.com"}}
... )
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.c1.update(
... {"name":"Y1"},
... {"$set":{"name":"rose","age":8,"email":"rr@qq.com"}},
{"upsert":true}
... )
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("608269aa6d779b5456ab6259")
})
# 选项 multi的使用
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":10}}
... )
> db.c1.update( {"name":"rose"}, {"$inc":{"age":10}} )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":10}} ,
{"multi":true}
... )
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
```
#### 2)save()
**语法 :db.集合名.save({文档数据})**
通过传入的文档来**替换**已有文档,也可能执行**插入**
如果没有传入**_id**属性,或者**_id**属性的值不存在 ==> 插入,否则就是按照**_id**做条件来替换已有文档。
```mysql
{ "_id" : 4, "name" : "X1" }
> db.c1.save({
... "name":"X1",
... "gender":"M"
... }
... )
WriteResult({ "nInserted" : 1 })
> db.c1.save(
... {
... "_id": 6,
... "name":"X1",
... "age":6
... }
... )
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 6 })
> db.c1.save(
... {"_id":4 , "name":"XX1" , "gender":"F"}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
```
### 5. 删除文档
**语法:db.集合名.remove(条件 [, {justOne:boolean} ] )**
justOne是否删除一条,true-是;false-否 默认
条件描述 : {} json数据格式 , 如果条件描述中出现多个属性,是与and关系
**remove({}) ==> 删除集合所有文档**
```mysql
> db.c1.remove({"name":"X1"})
WriteResult({ "nRemoved" : 2 })
> db.c1.remove(
... {"name":"rose"},
... {"justOne":true}
... )
WriteResult({ "nRemoved" : 1 })
> db.c1.remove({"_id":4})
WriteResult({ "nRemoved" : 1 })
> db.c1.remove({"_id": ObjectId("608221c66baab5abac64613b")})
WriteResult({ "nRemoved" : 1 })
> for(i=1;i<=10;i++){
db.c2.insert({"name" : "N" + i})
}
> db.c2.remove({})
WriteResult({ "nRemoved" : 10 })
```
### 6. 查询文档--find()
**语法:db.集合名.find(条件 [, 要显示的key])**
> tip : 可以最后加一个pretty() 格式化展示数据
#### 1)基本查询
```
条件描述 : "="
{} 或者 不写 查询所有文档
{"age":26} age=26
{"age":26,"gender":"M"} age=26 and gender='M'
{"age":12,"age":23} age=23(age=23覆盖条件age=12)
查询的属性
不写 所有key
{"age":1} 只显示key为age
{"age":1 , "gender":1} 显示key为age和gender
{“age":0} 除了key为age的其他
【注】_id 属性默认显示,如果不希望出现使用 "_id:0"
```
```mysql
> db.c1.find(
... {"name":"lisi"}
... )
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
>
> db.c1.find(
... {"gender":"F","name":"isoft"}
... )
>
> db.c1.find(
... {"age":12}
... )
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
>
> db.c1.find(
... {"age":12,"age":23}
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
> db.c1.find(
... {"age":23,"age":12}
... )
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
> db.c1.find(
... {},
... {"name":1}
... )
> db.c1.find(
... {},
... {"name":0}
... )
> db.c1.find(
... {},
... {"_id":0,"name":1,"email":1}
... )
```
#### 2)条件运算符的使用
```
描述 :{"key" : {"运算符" : value}}
```
| 运算符 | 作用 |
| ------ | :-------------------------------------------- |
| $gt | > |
| $gte | >= |
| $lt | < |
| $lte | <= |
| $ne | != |
| $in | in alue值是数组, {"age" : {"$in" : [10,20]} |
| $nin | not in |
```mysql
# 数据准备
> for(i=1;i<=10;i++){
... db.c1.update({"name":"isoft"+i},{"$set":{"age":10+i}})
... }
> db.c1.find( {"age":{"$gt":20}})
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
> db.c1.find(
... {"age":{"$lte":15}}
... )
> db.c1.find(
... {"gender":{"$in":['f']}}
... )
> db.c1.find( {"gender":{"$in":['f','F','0']}} )
> db.c1.find(
... {"age":{"$nin":[20]}}
... )
> db.c1.find(
{"age":{"$ne":20}}
)
```
#### 3)逻辑与、或
$and , $or
```
{“$and|$or" : [{} , {} ,...]
```
```mysql
# 查询age<12或者 age>20
> db.c1.find(
... {"$or":[
... {"age":{"$lt":12}},
... {"age":{"$gt":20}}
... ]}
... )
# 查询20<age<12
> db.c1.find(
... {"$and":[
... {"age":{"$gt":12}},
... {"age":{"$lt":20}}
... ]}
... )
#查询姓名是rose的文档 或者 年龄在18以上的男性文档
> db.c1.find(
... {"$or" : [
... {"name":"rose"},
... {"age":{"$gt":18} , "gender":"M"}
... ]}
... )
> db.c1.find(
{"$or" : [
{"name":"rose"},
{"$and":[
{"age":{"$gt":18}} ,
{"gender":"M"}
] }
] }
)
```
#### 4)$not、 $type
https://www.runoob.com/mongodb/mongodb-operators-type.html
double -- 1 , string--2 , object -- 3 , array -- 4 , null -- 10 , 32位int--16 , 64位int--18
NumberInt() , NumberLong()
【注意】**值是Array类型比较特殊,使用$not结合$type否定类型时,Array不能被检索到**
```mysql
# 数据准备
> db.c1.insert({"name":10,"age":11})
> db.c1.insert({"name":NumberInt(20),"age":20})
> db.c1.insert({"name":["r","o"],"age":20})
> db.c1.insert({"name":{"first":"rose","last":"L"}})
# 查询name值是 double类型
> db.c1.find(
... {"name":{"$type":1}}
... )
# 查询name的value值类型是数值类型
> db.c1.find(
... {"$or":[
... {"name":{"$type":1}},
... {"name":{"$type":16}},
... {"name":{"$type":18}}
... ]}
... )
> db.c1.find(
... {"name": {"$type":{"$in":[1,16,18]}}}
... )
Error: error: {
"ok" : 0,
"errmsg" : "type must be represented as a number or a string",
"code" : 14,
"codeName" : "TypeMismatch"
}
# 查询name的value值类型是string类型
> db.c1.find(
... {"name":{"$type":2}}
... )
# 查询name的value值类型不是string类型 ???? name值是array的
> db.c1.find(
... {"name":{"$not":{"$type":2}}}
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
{ "_id" : ObjectId("6084c12a95c3b0b8ed942f79"), "name" : 10, "age" : 11 }
{ "_id" : ObjectId("6084c1e495c3b0b8ed942f7a"), "name" : 20, "age" : 20 }
{ "_id" : ObjectId("6084c9b895c3b0b8ed942f7d"), "name" : { "first" : "rose", "last" : "L" } }
# 正确为
> db.c1.find(
... {"$or":[
... {"name":{"$not":{"$type":2}}} ,
... {"name": {"$type":4}}
... ]}
... )
```
#### 5)null与not null
【注】条件表述为 null ==》**该key不存在或者key的值是null的两种情况的文档**
```
value 值是null 的判定 :
{"key":null}
{"key":{"$in":[null]}}
value 值不是null 的判定 :
{"key":{"$nin":[null]}}
```
**可以结合 $in , $type , $exists , $not 一起使用**
```mysql
> db.c1.insert({"name":null,"age":0})
WriteResult({ "nInserted" : 1 })
# 查询 name 是 null ==> 结果是 name为null或者不存在name属性
> db.c1.find( {"name":null} )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
{ "_id" : ObjectId("6084c61495c3b0b8ed942f7c"), "name" : null, "age" : 0 }
# 查询name属性存在,且值是null的文档
> db.c1.find(
... {"name":{"$in":[null] , "$exists":true} }
... )
{ "_id" : ObjectId("6084c61495c3b0b8ed942f7c"), "name" : null, "age" : 0 }
# 查询 name 属性不是null ==> name不是null或者不存在name属性
> db.c1.find(
... {"name":{"$not":{"$type":10}}}
... )
# 查询 name 有确定值
> db.c1.find(
... {"$and" : [
... {"name":{"$not":{"$type":10}}} ,
... {"name":{"$exists":true}}
... ]}
... )
# 或者
> db.c1.find(
... {"name":{"$nin":[null]}}
... )
```
#### 6)正则表达式的使用
```
{"key": /正则表达式/修饰符 }
或者
{"key":
{"$regex":"正则表达式" [,{"$options":"修饰符"}]}
}
-- 含有字符 /字符/
-- 以字符开头 /^字符/
-- 以字符结尾 /字符$/
-- 常用的修饰符就是 i => 不区分大小写
```
```mysql
> db.c1.insert({"name":"Im"})
# name的值是i
> db.c1.find(
... {"name":"i"}
... )
# name的值含有i
> db.c1.find(
... {"name":/i/}
... )
# 使用修饰符
> db.c1.find(
... {"name":/i/i}
... )
# 使用正则表达式另外一种写法
> db.c1.find(
... {"name":{"$regex":"i"}}
... )
> db.c1.find( {"name":{"$regex":"i","$options":"i"}} )
# 以字符i开头
> db.c1.find(
... {"name":/^I/}
... )
{ "_id" : ObjectId("608508a0b2e7bc32dfacb336"), "name" : "Im" }
> db.c1.find(
... {"name":/I/i}
... )
# 以字符i结尾
> db.c1.find(
... {"name":/i$/}
... )
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
# 查询name属性是长度2--10的英文value值
> db.c1.find(
... {"name":/^[a-zA-Z]{2,10}$/}
... )
```
#### 7)查询数组值
##### -- 条件描述
```
$all : { "key“ : {"$all" : ["v1","v2" ,...]} } key in [v1 and v2]
key.index : index从0数起,用来限定数组的第几个值的条件描述
$size :限定数组元素值的个数 {"key":{"$size":int}}
```
```mysql
> data = [
... {"name":"A" , "fruit":["apple","banana","lemon"]} ,
... {"name":"B" , "fruit":["apple","orance","peach"]} ,
... {"name":"C" , "fruit":["cherry","banana","apple"]} ,
... {"name":"D" , "fruit":["cherry"]}
... ]
> db.c2.insert(data)
# 查找 fruit值含有 apple和banana的文档
-- 使用 $in 是 or
> db.c2.find(
... {"fruit":{"$in":["apple","banana"]}}
... )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb337"), "name" : "A", "fruit" : [ "apple", "banana", "lemon" ] }
{ "_id" : ObjectId("60850b58b2e7bc32dfacb338"), "name" : "B", "fruit" : [ "apple", "orance", "peach" ] }
{ "_id" : ObjectId("60850b58b2e7bc32dfacb339"), "name" : "C", "fruit" : [ "cherry", "banana", "apple" ] }
-- 正确写法
> db.c2.find(
... {"fruit":{"$all":["apple","banana"]}}
... )
# 查询 fruit的第三个元素值是 apple
> db.c2.find( {"fruit.2":"apple"} )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb339"), "name" : "C", "fruit" : [ "cherry", "banana", "apple" ] }
> db.c2.find(
... {"fruit.2":/apple/i}
... )
# 按照元素个数匹配
> db.c2.find(
... {"fruit":{"$size":1}}
... )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb33a"), "name" : "D", "fruit" : [ "cherry" ] }
> db.c2.find(
... {"fruit":{"$size":{"$gt":2}}}
... )
Error: error: {
"ok" : 0,
"errmsg" : "$size needs a number",
"code" : 2,
"codeName" : "BadValue"
}
```
##### -- 切片 $slice,返回限定个数
```mysql
> db.c2.find(
... {},
... {"fruit":{"$slice":1}} # 第一个元素
... )
> db.c2.find( {}, {"fruit":{"$slice":-1}} ) # 最后一个元素
> db.c2.find( {}, {"fruit":{"$slice":[1,3]}} ) # 索引位置从1开始,向后2个
```
##### -- 数组元素范围匹配 $elemMatch
> 如果查询条件只是一半范围,没有必要使用 $elemMath
```
数组元素值含有满足条件的情况的文档
{"key" : {"$elemMatch":{条件1, 条件2}}}
```
```mysql
> arr = [
... {"_id":1 , "x":5} ,
... {"_id":2 , "x":15} ,
... {"_id":3 , "x":25} ,
... {"_id":4 , "x":[5,25]} ,
... {"_id":5 , "x":[5,15,20,25,30]} ,
... {"_id":6 , "x":[8,16,24,36]}
... ]
>db.c3.insert(arr)
# 以下值限定不仅匹配普通value值,也匹配数组
> db.c3.find( {"x" : {"$gte":15}} )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 去掉数组类型值得匹配
> db.c3.find(
... {"$and":[
... {"x":{"$gte":15}},
... {"x":{"$not":{"$type":4}}}
... ]}
... )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
# 值在某个范围
> db.c3.find(
... {"$and":[
... {"x":{"$gt":10}},
... {"x":{"$lt":30}}
... ]}
... )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 , 含有值 >=8
> db.c3.find(
{"x":{"$elemMatch":{"$gte":8}}}
)
-- 等价于
> db.c3.find(
{"$and" :[
{"x":{"$gte":8}} ,
{"x" : {"$type":4}}
]}
)
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 ,含有值 >=8 并且 <40
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":40}}} )
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":20}}} )
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
```
#### 8)查询内嵌文档值
```
key.property
```
```mysql
> users = [
... {"name":{"first":"rose","last":"A"} , "age" : 18 },
... {"name":{"first":"joe","last":"B"} , "age" : 28 },
... {"name":{"first":"mike","last":"C"} , "age" : 23 }
... ]
> db.c4.insert(users)
# 使用 key.属性 进行查询
> db.c4.find(
... {"name.last":/a/i}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
>
> db.c4.find(
... {"name.first":/o/i}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
{ "_id" : ObjectId("60851611b2e7bc32dfacb33c"), "name" : { "first" : "joe", "last" : "B" }, "age" : 28 }
>
> db.c4.find(
... {"name":{"first":"rose","last":"A"}}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
# $elemMatch的使用
> books={
... "title":"A",
... "content":"helloworld is java",
... "comments":[
... {"author":"joe" , "score":3 , "comment":"good"} ,
... {"author":"rose" , "score":6 , "comment":"terrible"} ,
... ]
... }
> db.c5.insert(books)
# 查询 同一条评论作者是joe,并且分值在5分以上 ==》没有满足条件的文档
> db.c5.find(
... {"comments": {"$elemMatch":{"author":"joe" , "score":{"$gte":5}}}}
... )
# 查询 同一条评论作者是rose,并且分值在5分以上
> db.c5.find(
... {"comments": {"$elemMatch":{"author":"rose" , "score":{"$gte":5}}}}
... )
```
#### 9)$where
$where 可以执行任意的javascript函数,来实现查询条件描述。
!!!当使用$where时,要将集合中每个文档从Bson转换为js对象,然后在进行$where的js函数处理,性能非常低,**尽量不使用**。
```
{"$where": js函数 }
{"$where" : "js条件表达式" }
【注】在描述$where的条件时,js代码可以使用"this"指代当前文档 ; 函数返回boolean值
```
```mysql
> db.c3.find()
{ "_id" : 1, "x" : 5 }
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 ,含有值 >=8 并且 <40
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":40}}} )
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
> js = function() {
... arr = this.x ;
... for(n in arr) {
... return arr[n]>=8 && arr[n]<40
... }
... }
> db.c3.find(
... {"$where":js}
... )
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数据准备
> data = [
... {"name":"joe" , age : "12" , brotherAge : 20 },
... {"name":"rose" , age : "20" , brotherAge : 12 }
... ]
> db.c4.insert(data)
# 查询集合中当前文档记录的是弟弟或者妹妹信息的数据
> db.c4.find(
... {"$where":"this.age<this.brotherAge"}
... )
{ "_id" : ObjectId("60860de108e9f867708fcb4c"), "name" : "joe", "age" : "12", "brotherAge" : 20 }
```
#### 10)分页--用在find()后面
**skip(跳过的文档书) 与 limit(返回的文档数)**
```mysql
> db.c1.find({},{"_id":0}).skip(3)
> db.c1.find({},{"_id":0}).limit(3)
> db.c1.find({},{"_id":0}).skip(3).limit(10)
```
#### 11)排序--用在find()后面
**sort({"key" : 1|-1 , .... })** 1=>升序,-1=>降序
> tip : 分页和排序同时使用 :
>
> find().sort().skip().limit()
```mysql
> db.c1.find({},{"_id":0}).sort({"age":-1})
> db.c1.find({},{"_id":0}).sort({"age":-1,"name":1})
> db.c1.find({},{"_id":0}).sort({"name":1}).skip(5).limit(5)
```
### 7. 查询一条文档—findOne()
```mysql
> db.c1.findOne()
{
"_id" : ObjectId("608222976baab5abac64613d"),
"age" : 23,
"username" : "mike"
}
> db.c1.findOne(
... {"name":/o/i , "age":{"$gt":18}}
... )
{
"_id" : ObjectId("608225516baab5abac646148"),
"name" : "isoft9",
"gender" : "M",
"age" : 19
}
```
### 8. count()
**语法 : db.集合名.count(条件)**
作用:返回满足条件的文档数量
```mysql
> db.c1.count()
20
> db.c1.count(
... {"name":/o/i}
... )
12
> db.c1.count(
... {"$and":[
... {"age":{"$gte":10}},
... {"age":{"$lte":20}}
... ]}
... )
15
```
### 9. distinct()
**语法 :db.集合名.distinct("key" [,条件])**
作用 :返回满足条件的文档指定key的不同值有哪些,返回值类型是数组
```mysql
> db.c1.distinct("gender")
[ "F", "M" ]
> db.c1.distinct("age")
[ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23 ]
> db.c1.distinct("age" , {"name":/o/i})
[ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]
```
## 0425—find查询(笔记见上方)
## 0426—查询、游标、索引
### 1. find查询(笔记见上方)
### 2. findOne()(笔记见上方)
### 3. count()(笔记见上方)
### 4. distinct()(笔记见上方)
### 5. 管道(自学)
https://www.cnblogs.com/yunlongaimeng/p/11460291.html
https://www.cnblogs.com/qlqwjy/p/8652555.html
### 6. 游标
#### 1)游标
不是查询结果,而是查询的返回接口或者记录指针。
通过游标可以逐行读取数据。
声明游标时不执行查询,通过游标提取数据时才执行查询
#### 2)游标相关操作
-- 声明游标
**var 游标 = 查询**
-- 判断游标是否到达了末尾
**游标.hasNext()** 返回一个boolean
-- 取出游标所指向的下一个文档
**游标.next()**
-- 循环遍历游标
**while(游标.hasNext()) {**
...
游标.next()
...
**}**
或者
**游标.forEach(回调函数)**
-- 将游标所对应的查询结果文档转为一个数组
**数组 = 游标.toArray()**
```mysql
> var cur = db.c2.find()
>
> print(cur.hasNext())
true
>
> print(cur.next())
[object BSON]
>
> printjson(cur.next())
{
"_id" : ObjectId("60850b58b2e7bc32dfacb338"),
"name" : "B",
"fruit" : [
"apple",
"orance",
"peach"
]
}
> while(cur.hasNext()){
... printjson(cur.next())
... }
{
"_id" : ObjectId("60850b58b2e7bc32dfacb339"),
"name" : "C",
"fruit" : [
"cherry",
"banana",
"apple"
]
}
{
"_id" : ObjectId("60850b58b2e7bc32dfacb33a"),
"name" : "D",
"fruit" : [
"cherry"
]
}
> var cur2 = db.c1.find({"gender":"F"})
> cur2.forEach(function(doc){
... printjson(doc)
... print(doc.name+"--"+doc.gender)
... })
> print(cur2.hasNext())
false
> var cur3 = db.c1.find({"age":{"$gte":20}})
> age20 = cur3.toArray()
[
{
"_id" : ObjectId("608222976baab5abac64613d"),
"age" : 23,
"username" : "mike"
},
{
"_id" : ObjectId("608225516baab5abac646149"),
"name" : "isoft10",
"gender" : "F",
"age" : 20
},
{
"_id" : ObjectId("6084c1e495c3b0b8ed942f7a"),
"name" : 20,
"age" : 20
},
{
"_id" : ObjectId("6084c37995c3b0b8ed942f7b"),
"name" : [
"r",
"o"
],
"age" : 20
}
]
> print(age20)
[object BSON],[object BSON],[object BSON],[object BSON]
> printjson(age20)
....
```
## 第三章 MongoDB的索引
### 1. 简介
**索引通常能够极大的提高查询的效率**,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文档并选取那些符合查询条件的文档。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
### 2. 优缺点
-- 优点
极大的提高查询的效率
-- 缺点
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。
【很少对集合进行读取操作,建议不使用索引】
### 3. 限制
- 集合中索引不能超过64个
- 索引名的长度不能超过128个字符
- 一个复合索引最多可以有31个字段
### 4. 查询性能分析
**语法 :db.集合名.find(..).explain(["executionStats"])**
显示结果 :关注 winningPlan ==>stage|inputState ==>stage的值
COLLSCAN 全集和扫描
IXSCAN 索引扫描
IDHACK _id为条件应用自动为该key创建的索引进行扫描
### 5. 索引的类型
-- 索引类型
单键索引、复合索引、多键索引、哈希索引、全文索引、地理空间索引
-- 索引属性
唯一索引、局部索引、稀疏索引、TTL索引
### 6. 创建索引
**语法形式 : db.createIndex({"key":1|-1 , ...} [, 额外选项] )**
额外选项 :
| background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为**false**。 |
| :---------------------------------- | ------------- | ------------------------------------------------------------ |
| unique(唯一索引) | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为**false**. |
| name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。<br />默认索引名是key_1\|key_-1 |
| partialFilterExpression(局部索引) | 条件 | 针对key满足条件的文档创建索引 |
| sparse(稀疏索引) | Boolean | 稀疏索引(或者称间隙索引)就是只包含有索引字段的文档的条目,跳过索引键不存在的文档 |
| expireAfterSeconds(TTL索引) | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
| v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
### 7. 删除索引
db.集合名.dropIndex(索引名)
db.集合名.dropIndexes()
### 8 . 查看索引
db.集合.getIndexes()
> tip : MongoDB中为集合默认为_id属性创建一个单键索引
```mysql
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test12.c1"
}
]
```
### 9. 索引类型
#### 1)单键索引
单键索引(Single Field Indexes)顾名思义就是单个字段作为索引列,mongoDB的所有collection默认都有一个单键索引_id。
```mysql
> db.c1.createIndex(
... {"name":1},
... {"name":"name_index1"}
... )
> db.c1.createIndex({"age":1})
> db.c1.dropIndex("age_1")
> db.c1.dropIndex("name_index1")
```
#### 2)复合索引
复合索引(Compound Indexes)指一个索引包**含多个key**,用法和单键索引基本一致。使用复合索引时要注意字段的顺序。mongoDB中一个复合索引最多可以包含32个字段。
```mysql
> db.c1.find(
... {"$and":[
... {"name":/o/i},
... {"age":{"$gt":15}}
... ]}
... ).explain()
{
"queryPlanner" : {
"....
"winningPlan" : {
"stage" : "COLLSCAN",
....
},
....
}
>
> db.c1.createIndex(
... {"name":1 , "age":1}
... )
> db.c1.getIndexes()
[
....
{
"v" : 2,
"key" : {
"name" : 1,
"age" : 1
},
"name" : "name_1_age_1",
"ns" : "test12.c1"
}
]
> db.c1.find( {"$and":[ {"name":/o/i}, {"age":{"$gt":15}} ]} ).explain()
{
"queryPlanner" : {
....
"winningPlan" : {
....
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1,
"age" : 1
},
"indexName" : "name_1_age_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"name" : [
"name"
],
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
.....
}
```
#### 3)多键索引
多键索引(mutiKey Indexes)是建在数组上的索引 。
```mysql
# 准备数据
> db.classes.insert([
... ... {
... ... "classname":"class1",
... ... "students":[{name:'jack',age:20},
... ... {name:'tom',age:22},
... ... {name:'lilei',age:25}]
... ... },
... ... {
... ... "classname":"class2",
... ... "students":[{name:'lucy',age:20},
... ... {name:'jim',age:23},
... ... {name:'jarry',age:26}]
... ... }
... ... ])
> db.classes.createIndex({"students.age":1})
> db.classes.find({"students.age":20}).explain()
```
#### 4)哈希索引
哈希索引(hashed Indexes)就是将field的值进行hash计算后作为索引,其强大之处在于实现0(1)查找,当然用哈希索引最主要的功能也就是实现**定值查找**(key=value),对于经常需要排序或查询范围查询的集合不要使用哈希索引。
**语法 : {"key":"hashed"}**
```mysql
> db.c1.createIndex({"gender":"hashed"})
> db.c1.find({"gender":"F"}).explain()
```
### 10. 索引属性
#### 1)唯一索引
唯一索引(unique indexes)用于为collection添加唯一约束,即**强制要求collection中的索引key没有重复值**。
语法 :**{unique:true}**
【注】创建该索引前,创建索引的key不能有重复的value!
```mysql
> db.c1.createIndex(
... {"name":-1},
... {"unique":true}
... )
{
"ok" : 0,
"errmsg" : "E11000 duplicate key error collection: test12.c1 index: name_-1 dup key: { name: \"zhangsan\" }",
"code" : 11000,
"codeName" : "DuplicateKey",
"keyPattern" : {
"name" : -1
},
"keyValue" : {
"name" : "zhangsan"
}
}
# 移出name的重复值
> db.c1.createIndex( {"name":-1}, {"unique":true} )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}
{
"v" : 2,
"unique" : true,
"key" : {
"name" : -1
},
"name" : "name_-1",
"ns" : "test12.c1"
}
> db.c1.insert({"name":"zhangsan"})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test12.c1 index: name_-1 dup key: { name: \"zhangsan\" }"
}
})
```
#### 2)局部索引
只对collection的一部分添加索引。创建索引的时候,根据过滤条件判断是否对document添加索引,对于没有添加索引的文档查找时采用的全表扫描,对添加了索引的文档查找时使用索引。
语法 : {"partialFilterExpression" : {条件}}
```mysql
> db.c1.createIndex({"age":1},{"partialFilterExpression":{"age":{"$gt":15}}})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"age" : 1
},
"name" : "age_1",
"ns" : "test12.c1",
"partialFilterExpression" : {
"age" : {
"$gt" : 15
}
}
}
]
> db.c1.find({"age":{"$lt":15}}).explain()
> db.c1.find({"age":{"$gt":18}}).explain()
```
#### 3)稀疏索引
稀疏索引(sparse indexes)在有索引字段的document上添加索引,如在address字段上添加稀疏索引时,只有document有address字段时才会添加索引。而普通索引则是为所有的document添加索引,使用普通索引时如果document没有索引字段的话,设置索引字段的值为null。
语法 :**{"sparse": true}**
```mysql
# 数据准备
> db.scores.insert([
... { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" },
... { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 },
... { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }])
> db.scores.createIndex( { score: 1 } , { sparse: true } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.scores.find( { score: { $lt: 90 } } )
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
# 由于文档newbie并不包含score键,因此该文档不会出现在稀疏索引之中,也就不会被查询返回
> db.scores.find( { score: { $lt: 90 } } ).explain()
{
"queryPlanner" : {
。。。
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"score" : 1
},
"indexName" : "score_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"score" : [ ]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"score" : [
"[-inf.0, 90.0)"
]
}
}
},
。。。。
}
```
#### 4)TTL索引
TTL索引(time-to-live index 具有生命周期的索引)是一种特殊的单键索引,用于**设置document的过期时间**,mongoDB会在document过期后将其删除,TTL非常容易实现类似缓存过期策略的功能。
语法 : **{"expireAfterSeconds" : 秒值}**
【注】TTL索引只能设置**在date类型字段(或者包含date类型的数组)**上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;
```mysql
-- 数据准备 --
> db.logs.insert([
... {_id:1,createtime:new Date(),msg:"log1"},
... {_id:2,createtime:new Date(),msg:"log2"},
... {_id:3,createtime:new Date(),msg:"log3"},
... {_id:4,createtime:new Date(),msg:"log4"}
... ])
> db.logs.createIndex(
... {"createtime":1},
... {"expireAfterSeconds":20}
... )
{
... "v" : 2,
... "key" : {
... "createtime" : 1
... },
... "name" : "createtime_1",
... "ns" : "test12.logs",
... "expireAfterSeconds" : 20
... }
# 等待一会儿去执行查看
> db.logs.find()
```
【注】TTL索引只能设置在date类型字段(或者包含date类型的数组)上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;
### 11. 全文索引
【在mongodb中,每个数据**集合只能创建一个全文索引**】
全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
这个过程类似于通过字典中的检索字表查字的过程。
**语法:db.集合名.ensureIndex({字段名 : "text“});**
```mysql
-- 数据准备
> posts=[
{
"post_text": "enjoy the mongodb articles on Runoob",
"tags": [
"mongodb",
"runoob"
]
},
{
"post_text": "enjoy the python articles on Mrs Gao",
"tags": [
"python",
"gao"
]
},
]
> db.posts.insert(posts)
-- 创建全文索引
> db.posts.ensureIndex({"post_text":"text"})
-- 执行查询 , 使用索引
> db.posts.find({"$text":{"$search":"runoob"}})
```
**使用全文索引:使用全文索引跟使用其他索引不一样,我们不再需要key的名字,而是直接使用$text,$search进行查询。**
### 12. 地理空间索引
随着移动设备的应用的爆发式增长,有一种查询变得越来越流行:找到离当前位置最近的N个场所。MongoDB为坐标平面查询提供了专门的索引,称作地理空间索引。地理空间索引分2dsphere索引和2d索引。
## 0427
## 第四章 MongoDB GridFS
### 1. GridFS简介
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。
GridFS 可以更好的存储大于16M的文件(MongoDB的BSON格式的数据(文档)存储有尺寸限制,最大为16M)。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。
### 2. 使用场景
-- 如果您的文件系统在一个目录中存储的文件数量有限(太多将会影响文件的打开速度),你可以使用GridFS存储尽可能多的文件。
-- 当你想访问大型文件的部分信息,却不想加载整个文件到内存时,您可以使用GridFS存储文件,并读取文件部分信息,而不需要加载整个文件到内存。
-- 你想让你的文件和元数据自动同步并部署在多个系统和设施,你可以使用GridFS实现分布式文件存储。
### 3. 存储相关集合
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。
以下是简单的 **fs.files** 集合文档:
```
{
"_id": <ObjectId>, // 文档id,唯一标识
"filename": <string>,
"chunkSize": <num>,
"uploadDate": <ISODate>,
"md5": <string>,
"length": <num>,
"metadata":<dataObject>
}
```
以下是简单的 **fs.chunks** 集合文档:
```
{
"_id": <ObjectId>,
"files_id": <ObjectId>, // 对应fs.files文档的 _id
"n": <num>, // 序号,标识文档的第几个chunk
"data": <binary> // 文件二进制数据,不要显示该属性值
}
```
### 4. GridFS操作
使用MongoDB安装目录"bin/" 的mongofiles 工具
**命令格式 :**
**mongofiles \<options> \<command> \<filename or _id>**
```
command :
list , search , put , get , delete
put_id , get_id , delete_id
options :
-d 数据库名
-l 文件位置及名称
```
```mysql
# 上传一个20K+的文件
D:\green_software\mongodb\bin>mongofiles -d gridfs -l "d:\OO\Pictures\skb.png" put m1.png
2021-04-27T08:53:12.180+0800 connected to: mongodb://localhost/
2021-04-27T08:53:12.350+0800 added gridFile: m1.png
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.000GB
local 0.000GB
test12 0.001GB
> use gridfs
switched to db gridfs
> show collections
fs.chunks
fs.files
> db.fs.files.find().pretty()
{
"_id" : ObjectId("60876078661647544d94250a"),
"length" : NumberLong(24775),
"chunkSize" : 261120,
"uploadDate" : ISODate("2021-04-27T00:53:12.348Z"),
"filename" : "m1.png",
"metadata" : {
}
}
> db.fs.chunks.find({} , {"data":0})
{ "_id" : ObjectId("60876078661647544d94250b"), "files_id" : ObjectId("60876078661647544d94250a"), "n" : 0 }
# 上传一个 2M+ 的文件
D:\green_software\mongodb\bin>mongofiles -d gridfs -l d:\OO\Pictures\mongofile.jpg put m2.jpg
2021-04-27T08:59:39.923+0800 connected to: mongodb://localhost/
2021-04-27T08:59:40.008+0800 added gridFile: m2.jpg
D:\green_software\mongodb\bin>mongo
> use gridfs
switched to db gridfs
> show collections
fs.chunks
fs.files
> var fid=db.fs.files.findOne({"filename":"m2.jpg"})._id
> fid
ObjectId("60876ab999b849dc1310485c")
> db.fs.chunks.find({"files_id":fid},{"data":0})
{ "_id" : ObjectId("608761fb6f324480d836e155"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 0 }
{ "_id" : ObjectId("608761fb6f324480d836e156"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 1 }
{ "_id" : ObjectId("608761fb6f324480d836e157"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 2 }
{ "_id" : ObjectId("608761fb6f324480d836e158"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 3 }
{ "_id" : ObjectId("608761fb6f324480d836e159"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 4 }
{ "_id" : ObjectId("608761fb6f324480d836e15a"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 5 }
{ "_id" : ObjectId("608761fb6f324480d836e15b"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 6 }
{ "_id" : ObjectId("608761fb6f324480d836e15c"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 7 }
{ "_id" : ObjectId("608761fb6f324480d836e15d"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 8 }
{ "_id" : ObjectId("608761fb6f324480d836e15e"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 9 }
{ "_id" : ObjectId("608761fb6f324480d836e15f"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 10 }
# 测试其他命令
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:08:01.102+0800 connected to: mongodb://localhost/
m1.png 24775
m2.jpg 2852907
D:\green_software\mongodb\bin>mongofiles -d gridfs search m1.jpg
2021-04-27T09:08:40.609+0800 connected to: mongodb://localhost/
D:\green_software\mongodb\bin>mongofiles -d gridfs search m1.png
2021-04-27T09:08:45.972+0800 connected to: mongodb://localhost/
m1.png 24775
D:\green_software\mongodb\bin>mongofiles -d gridfs get m1.png
2021-04-27T09:09:21.635+0800 connected to: mongodb://localhost/
2021-04-27T09:09:21.649+0800 finished writing to m1.png
D:\green_software\mongodb\bin>mongofiles -d gridfs -l d:\mongofile.jpg get m2.jpg
2021-04-27T09:10:41.794+0800 connected to: mongodb://localhost/
2021-04-27T09:10:41.832+0800 finished writing to d:\mongofile.jpg
D:\green_software\mongodb\bin>mongofiles -d grifs delete m1.png
2021-04-27T09:11:25.403+0800 connected to: mongodb://localhost/
2021-04-27T09:11:25.415+0800 successfully deleted all instances of 'm1.png' from GridFS
D:\green_software\mongodb\bin>mongofiles -d grifs delete m2.jpg
2021-04-27T09:11:35.724+0800 connected to: mongodb://localhost/
2021-04-27T09:11:35.737+0800 successfully deleted all instances of 'm2.jpg' from GridFS
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:11:46.170+0800 connected to: mongodb://localhost/
m1.png 24775
m2.jpg 2852907
D:\green_software\mongodb\bin>net stop mongodb
MongoDB 服务正在停止.
MongoDB 服务已成功停止。
D:\green_software\mongodb\bin>net start mongodb
MongoDB 服务正在启动 ..
MongoDB 服务已经启动成功。
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:14:40.243+0800 connected to: mongodb://localhost/
```
## 第五章 数据库的备份与还原
### 1. 备份数据库 mongodump
**命令 :mongodump -h host:port - u username -p password -d dbName -o directory**
```
-h 服务器地址【不写默认本机】
:port 端口号(不写默认27017)
-u 账号
-p 密码
-d 需要备份的数据库实例,不写导出全局
-o 备份的数据存放位置,例如:c:\data\dump【目录已存在】
```
```mysql
# 备份本机服务器下所有数据库
D:\green_software\mongodb\bin>mongodump -o d:\mongodump
# 备份数据库 test12
D:\green_software\mongodb\bin>mongodump -d test12 -o d:\mongodump
```
### 2. 还原数据库 mongorestore
**命令 :mongorestore - h host:port - u username -p password -d dbName --drop directory**
```mysql
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
test12 0.001GB
> use test12
switched to db test12
> db.dropDatabase()
{ "dropped" : "test12", "ok" : 1 }
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
> exit
D:\green_software\mongodb\bin>mongorestore -d test d:\mongodump\test12
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
test 0.000GB
> use test
switched to db test
> show collections
c1
c2
c3
c4
c5
classes
logs
posts
scores
user
> db.coll.insert({"name":123})
WriteResult({ "nInserted" : 1 })
> exit
bye
D:\green_software\mongodb\bin>mongorestore -d test --drop d:\mongodump\test12
```
## 0429
### 1. Linux下MongoDB的配置(笔记见上方)
### 2. MongoDB的复制系统(见MongoDB复制笔记)
## 第六章 :MongoDB的复制系统
## 0428
## 第七章:其他语言访问MongoDB
### 1. 使用Java语言访问
> tip : 使用原生态方式访问 MongoDB
>
> SpringBoot中访问MongoDB,spring-boot-starter-data-mongodb
#### 1)添加jar包 ==> Maven 依赖项
```XML
<properties>
<mongo.driver.version>3.12.7</mongo.driver.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo.driver.version}</version>
</dependency>
</dependencies>
```
```java
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
```
#### 2)连接服务器
```java
/* mongodb://user:password@host:port */
String connStr = "mongodb://host:27017";
MongoClientURI uri = new MongoClientURI(connStr) ;
MongoClient mongoClient = new MongoClient(uri) ;
```
#### 3)选择数据库
```java
MongoDatabase db = mongoClient.getDatabase("dbname") ;
```
#### 4)选择集合
```java
MongoCollection<Document> collection = db.getCollection("collname")
```
#### 5)执行CRUD
-- insertOne() , insertMany()
```java
public void insert(MongoCollection<Document> collection) {
Document doc1 = new Document() ;
doc1.append("name" , "rose")
.append("pwd" , "123456")
.append("mobile" , "13812344321")
.append("gender" , "F") ;
collection.insertOne(doc1);
System.out.println(doc1);
/////////////////
List<Document> list = new ArrayList<>() ;
list.add(
new Document().append("name" , "java").append("pwd" , "111").append("gender" , "M")
) ;
list.add(
new Document().append("name" , "python").append("pwd" , "222").append("gender" , "F")
) ;
collection.insertMany(list);
System.out.println("insert many!");
}
```
-- find()
Bson
Filters 构造条件 , 返回 Bson对象
FindIterable 集合调用find()的返回值
MongoCursor FindIterable对象调用iterable() 获取
Document
```java
public void select(MongoCollection<Document> collection) {
// 查询所有文档
// FindIterable findIterable = collection.find() ;
// 条件过滤
// Bson bson = Filters.eq("name" , "rose") ;
Bson bson = Filters.regex("name" , "o" , "i") ;
FindIterable findIterable = collection.find(bson) ;
MongoCursor cursor = findIterable.iterator() ;
while(cursor.hasNext()) {
Document doc = (Document) cursor.next();
System.out.println(doc);
System.out.println(doc.getObjectId("_id") + " , " + doc.getString("name"));
}
}
```
```java
public void login(MongoCollection<Document> collection) {
String name = "java" ;
String pwd = "111" ;
Bson bson1 = Filters.eq("name" , name) ;
Bson bson2 = Filters.eq("pwd" , pwd) ;
Bson filterBson = Filters.and(bson1 , bson2) ;
FindIterable findIterable = collection.find(filterBson) ;
MongoCursor cursor = findIterable.iterator() ;
Document doc = null ;
if(cursor.hasNext()) {
doc = (Document) cursor.next() ;
}
System.out.println(doc);
}
```
-- updateOne() , updateMany()
UpdateResult
```java
public void update(MongoCollection<Document> collection) {
Bson filterBson = Filters.regex("name" , "o" , "i") ;;
Document doc = new Document("$set" ,
new Document().append("age" , 28).append("pwd" , 321)
);
UpdateResult updateResult = collection.updateMany(filterBson , doc) ;//collection.updateOne(filterBson , doc) ;
System.out.println(updateResult.getModifiedCount());
}
```
-- deleteOne() , deleteMany()
DeleteResult
```java
public void delete(MongoCollection<Document> collection) {
Bson filterBson = Filters.eq("_id" , new ObjectId("6088b901e819882d57589802")) ;
DeleteResult deleteResult = collection.deleteOne(filterBson) ;
System.out.println(deleteResult);
}
```
### 2. 使用Python语言访问MongoDB
https://www.runoob.com/python3/python-mongodb.html
#### 1) 相关库 pymongo
```
$ python -m pip install pymongo
```
#### 2)连接服务器
```python
import pymongo
conn = pymongo.MongoClient("localhost|127.0.0.1|host" , 27017 )
```
#### 3)选择数据库
```python
db = conn['dbname']
# db = conn.dbname
```
#### 4)选择集合
```python
coll = db['collname']
# coll = db.collname
```
```python
import pymongo
from bson import ObjectId
def conn() :
conn = pymongo.MongoClient("127.0.0.1" , 27017)
db = conn['test']
coll = db['python']
# print(coll)
return coll
```
#### 5)执行CRUD
-- insert_one() , insert_many()
```python
def insert(coll) :
user = {
"name" : "zhangsan2" ,
"pwd" : "123" ,
"gender" : "F"
}
result = coll.insert_one(user)
print(result.inserted_id)
print(user)
#############
u1 = {
"name" : "lisi" ,
"pwd" : "111" ,
"gender" : "M"
}
u2 = {
"name": "wangwu",
"pwd": "222",
"gender": "F"
}
results = coll.insert_many([u1 , u2])
print(results.inserted_ids)
print(u1)
print(u2)
```
-- find_one() , find()
```python
def findAll(coll) :
results2 = coll.find()
data = []
for doc in results2:
data.append({
"_id" : str(doc['_id']) ,
"name" : doc['name'] ,
"pwd" : doc['pwd'] ,
"gender" : doc['gender'],
# "email" : doc['email']
})
print(data)
print("-------------")
```
```python
def find(coll) :
row = coll.find_one()
print(row)
print(type(row))
print("-----------------")
row = coll.find_one({"name" : "zhangsan" , "pwd" : "123321"}) ;
print(row)
print("-------------")
results = coll.find({
"name" : {"$regex" : "a" , "$options" : "i"} ,
"gender" : "F"
})
print(results)
print(type(results))
for doc in results :
print(doc)
print("-------------")
num = coll.count_documents({})
# num = coll.count_documents({"name": "zhangsan"})
print(num)
results2 = coll.find().sort("name" , -1)
for doc in results2 :
print(doc)
print("-------------")
results3 = coll.find().skip(1).limit(2)
for doc in results3:
print(doc)
```
-- update_one() , update_many()
```python
def update(coll) :
query = {"name" : {"$regex" : "zhangsan"}}
new_value = {"$set" : {
"email" : "zzss@qq.com" ,
"pwd" : "686868"
}}
# result = coll.update_one(query , new_value)
# print(result.modified_count)
results = coll.update_many(query , new_value)
print(results.modified_count)
```
-- delete_one() , delete_many()
```python
def delete(coll) :
# query = {"name" : {"$regex" : "zhangsan"}}
query = {"_id" : ObjectId('6088ff8d5ed9981a6f6f3d45')}
result = coll.delete_one(query)
# result = coll.delete_many(query)
print(result.deleted_count)
```
NoSQL数据库技术:
出勤 20%,作业20%,笔试60%
课程目标:
掌握MongoDB的安装与配置--Windows,Linux(CentOS)
掌握MongoDB中基本操作命令
掌握MongoDB中文档相关操作——CRUD
了解MongoDB的复制系统,副本集的配置
掌握Python、Java访问MongoDB
理解索引的意义,学会几种常见索引的创建
## 0422
## 第一章 :MongoDB基础
### 1. 关系型数据库与非关系型数据库
1)关系型数据库
关系型数据库建立在关系型数据模型的基础上,是借助于集合代数等数学概念和方法来处理数据的数据库。Oracle、MySQL等都是。
在关系型数据库中,实体以及实体间的练习均由单一的结构类型来表示单一的数据结构----关系(表文件)。关系数据库的表采用二维表格来存储数据,是一种按行与列排列的具有相关信息的逻辑组,它类似于Excel工作表。一个数据库可以包含任意多个数据表。
在用户看来,一个关系模型的逻辑结构是一张二维表,由行和列组成。这个二维表就叫关系,通俗地说,一个关系对应一张表。
2)非关系型数据库
NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。
在信息化时代背景下,互联网数据增长迅猛,数据不仅仅是传统的结构化数据,还包含了大量的非结构化和半结构化数据,关系型数据库无法存储此类数据。
大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
### 2. NoSQL数据库分类
<https://baike.baidu.com/item/NoSQL/8828247?fromtitle=%E9%9D%9E%E5%85%B3%E7%B3%BB%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93>
键值数据库 :如 Redis
列存储数据库 :如 HBase
文档型数据库 :如MongoDB
图形数据库:如Neo4j
### 3. MongoDB简介
1)简介
**分布式**文件存储的数据库,由C++编写。**文档型**非关系型数据库。
MongoDB 将数据存储为一个文档,**数据结构由键值(key=>value)对**组成。MongoDB 文档类似于 JSON 对象的Bson格式数据。
```
{
“name” : “张三" ,
“age” : 18 ,
“groups” : ["足球队" , "IT队"] ,
“birth” : new Date(2000,2,13)
}
```
支持的数据结构非常松散,是类似json的bson格式,存储比较复杂的数据类型。
2)集合和文档
集合 ==》表(table);文档的集合,集合中的文档是各式各样的。
文档:key+value存储的数据 ==》记录,元组;就是键值对的一个有序集。
### 4. MongoDB的安装
**-- Windows系统下的安装(解压缩配置)**
【控制台下,以**管理员**身份操作】
安装 :解压缩 ==> 安装服务 ==> 开始使用
-- 下载zip包
-- 解压(不要出现中文路径、空格路径)
-- 解压后在文件中创建 "data" 与 "log" 两个目录
-- 以”**管理员**“身份打开cmd,**定位到MongoDB的解压缩bin目录**,然后执行如下命令
```
[安装服务--在MongoDB的解压缩bin目录下,如果配置了环境变量就任意]
d:\green_software\mongodb\bin> mongod --install --dbpath D:\green_software\mongodb\data --logpath D:\green_software\mongodb\log\mongodb.log
是否成功:打开服务 (启动 services.msc) , 查找是否由MongoDB的服务
[启动、停止、重启服务]
net start|stop mongodb
[移除服务]
先停止服务再操作
mongod --remove
[进入MongoDB的客户端]
mongo
```
配置环境变量
path变量增加 **<mongodb解压缩路径>\bin**
使用:启动服务 ==> 客户端
卸载:停止服务 ==> 移出服务 ==> 删除文件夹
**-- Linux系统下安装(CentOS7.8)**
-- 关闭防火墙
[root@CentosA ~]# systemctl stop firewalld
[root@CentosA ~]# systemctl disable firewalld
-- 选择好软件安装位置,将压缩包(.tar.gz ==>.tgz)传输到该位置
/usr/local
[root@CentosA ~]# cd /usr/local
-- 定位到 /usr/local , 解压
[root@CentosA local]# tar -zxvf xxx.tgz
-- 将压缩包重命名
[root@CentosA local]# mv xxx /usr/local/mongodb
-- 创建 data/ 与 log/ 目录
[root@CentosA local]# mkdir -p /usr/local/mongodb/data /usr/local/mongodb/log
-- 启动服务
[root@CentosA local]# cd /usr/local/mongodb/bin
[root@CentosA bin]# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --logappend --port=27017 --fork
-- 进入到MongoDB客户端
[root@CentosA bin]# ./mongo
```
systemctl stop firewalld
systemctl disable firewalld
cd /usr/local/
ls
tar -zxvf mongodb-linux-x86_64-rhel70-4.4.5.tgz
mv mongodb-linux-x86_64-rhel70-4.4.5 /usr/local/mongodb
ls
mkdir -p /usr/local/mongodb/data /usr/local/mongodb/log
cd mongodb/
ls
cd bin
ls
./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --logappend --port=27017 --fork
./mongo
```
-- 停止服务
```
方式一:在MongoDB的客户端下
> use admin
switched to db admin
> db.shutdownServer();
server should be down...
方式二 :在Shell命令下
[root@CentosA bin]# ./mongod --dbpath=/usr/local/mongodb/data --logpath=/usr/local/mongodb/log/mongodb.log --shutdown
方式三 : 杀死进程
[root@CentosA bin]# kill -9 进程号
```
-- 查看端口号占用情况
[root@CentosA bin]# netstat -tunlp | grep 27017
##
## 0423
## 第二章 MongoDB的基本操作
### 1. 基本操作
1)获取当前所访问的服务器对象
db.getMongo()
2)查看当前服务器下的数据库
show dbs | show databases
3)打开数据库
use 库名
> tip : MongoDB中不需要创建数据库,也不需要创建集合。
>
> 当忘数据库中插入了文档或者创建集合,会检查数据库是否存在,不存在自动创建数据库;
>
> 也会检查集合是否存在,不存在自动创建集合。
```
1. 库名全部小写,不允许使用"_"以外的其他字符,基本上就是ASCII范围的字母和数字
2. 不允许数字开头
3. 最多64个字符
4. 库名将以文件夹的形式存在
5. 系统中已存在了 admin , config , local 不要使用这些名称
```
4)查看当前数据库下集合
show collections
5)查看当前打开的数据库
db.getName()
6)删除数据库
【注】先打开要删除的数据
db.dropDatabase()
7)创建集合
db.createCollection("集合名")
```
1. 集合名全部小写,不允许使用"_","."以外的其他字符
2. 不允许数字开头
3. 最多64个字符
4. 禁止使用 system 开头
```
8)删除集合
db.集合名.drop()
### 2. MongoDB支持的数据类型
```
-- null
-- 布尔值 true与false
-- 数值
> x = {
... "k1":3, double
... "k2":3.14, double
... "k3":NumberInt(3), 32-int
... "k4":NumberLong(3) 64-int
... }
{ "k1" : 3, "k2" : 3.14, "k3" : NumberInt(3), "k4" : NumberLong(3) }
-- 字符串
-- 日期
使用new Date()创建的将是ISODate类型的日期对象,同样可以使用ISODate()
> data = {
... "d1":new Date(),
... "d2":ISODate(),
... "d3":new Date(2020,3,14),
... "d4":new Date("2020-03-14")
... }
{
"d1" : ISODate("2021-04-23T01:05:40.849Z"),
"d2" : ISODate("2021-04-23T01:05:40.850Z"),
"d3" : ISODate("2020-04-13T16:00:00Z"),
"d4" : ISODate("2020-03-14T00:00:00Z")
}
> d2 = {"dd":new Date("2020-04-12T10:20:20Z")}
{ "dd" : ISODate("2020-04-12T10:20:20Z") }
-- 数组
> m = {
... "k1":["hello",123,true,34]
... }
{
"k1" : [
"hello",
123,
true,
34,
5
]
}
-- 内嵌文档
> n = {
... "name":"rose",
... "addr":{"prov":"HeBei","city":"Shijiazhuang"}
... }
{
"name" : "rose",
"addr" : {
"prov" : "HeBei",
"city" : "Shijiazhuang"
}
}
-- 正则表达式
> t={
... "pro":/hello/i
... }
{ "pro" : /hello/i }
-- ObjectID 类型
构成 :时间戳+机器+PID+计数器==>24位十六进制数字组成的字符串
> k = {
... id: ObjectId()
... }
{ "id" : ObjectId("60763d98d5046809b857e4dc") }
-- 二进制数据
-- 代码
> u = {
... "pro" : function(){
... return "hello";
... }
... }
{ "pro" : function(){ return "hello"; } }
```
### 3. 插入文档
**语法:db.集合名.insert(文档数据)**
**集合存在,直接插入数据;集合不存在,创建。**
【说明】文档数据是BSON格式,key必须使用双引号引起来。但是在MongoDB下操作时,key可以不使用双引号。
-- 每次插入一条记录
```mysql
> db.getName()
test12
> show collections
> db.c1.insert({
... "name":"rose",
... "age":18
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
> db.c1.insert({
... "name":"joe",
... "age":20,
... "gender":"M"
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
> db.c1.insert({
... "name":"mike",
... "age":22,
... "gender":null
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
> db.c1.insert({
... "_id":4,
... "name":"X1"
... })
WriteResult({ "nInserted" : 1 })
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
{ "_id" : 4, "name" : "X1" }
```
-- 批量插入数据
```mysql
> datas = [
... {"name":"zs" , "age":12} ,
... {"name":"lisi", "email":"ls@qq.com" , "gender":"F"}
... ]
[
{
"name" : "zs",
"age" : 12
},
{
"name" : "lisi",
"email" : "ls@qq.com",
"gender" : "F"
}
]
> db.c1.insert(datas)
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
```
-- 支持js语法
(不是批量插入,而是insert执行了多次)
```mysql
> for(i = 1 ;i <=10 ;i++) {
... if(i%2==0) {
... db.c1.insert({"name":"isoft"+i , "gender":"F"})
... } else {
... db.c1.insert({"name":"isoft"+i , "gender":"M"})
... }
... }
WriteResult({ "nInserted" : 1 })
```
### 4. 更新文档
#### 1)update()
语法 :db.集合名.update(条件,更新后数据 [,{upsert:boolean , multi:boolean }])
```
条件描述 {}
更新后数据描述方式为 :
{“修改器" : { ... } , .... }
【注】不使用修改器,是进行替换
upsert : 按照条件没有找到匹配文档则执行插入(true-插入,false-不插入 默认)
multi:匹配成功所有数据都修改(true-是,false-否 默认)
```
| 修改器名称 | 作用 |
| ---------- | ------------------------------------------------------------ |
| $set | 修改key值,如果key在文档中不存在,则插入该属性以及值到条件匹配文档 |
| $inc | 在原值基础上递增减 |
| $rename | 重命名key |
| $unset | 根据key删除value,形式 {"$unset":{”key" : true}} |
```mysql
> db.c1.find()
{ "_id" : ObjectId("608221c66baab5abac64613b"), "name" : "rose", "age" : 18 }
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
{ "_id" : 4, "name" : "X1" }
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zs", "age" : 12 }
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646140"), "name" : "isoft1", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646141"), "name" : "isoft2", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646142"), "name" : "isoft3", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646143"), "name" : "isoft4", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646144"), "name" : "isoft5", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646145"), "name" : "isoft6", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646146"), "name" : "isoft7", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646147"), "name" : "isoft8", "gender" : "F" }
{ "_id" : ObjectId("608225516baab5abac646148"), "name" : "isoft9", "gender" : "M" }
{ "_id" : ObjectId("608225516baab5abac646149"), "name" : "isoft10", "gender" : "F" }
# 演示不使用插入器
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "name" : "joe", "age" : 20, "gender" : "M" }
> db.c1.update(
... {"name":"joe"},
... {"email":"joe@qq.com"}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{ "_id" : ObjectId("6082223d6baab5abac64613c"), "email" : "joe@qq.com" }
# 插入器使用
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zs", "age" : 12 }
# 将 zs 的name修改为 zhangsan,email修改为zs@qq.com
> db.c1.update(
... {"name":"zs"},
... {"$set":{"name":"zhangsan","email":"zs@qq.com"}}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
# 将 rose的年龄 -1
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":-1}}
... )
# 将mike的gender属性移除,age+1,name属性修改为username
{ "_id" : ObjectId("608222976baab5abac64613d"), "name" : "mike", "age" : 22, "gender" : null }
> db.c1.update(
... {"name":"mike"},
... {
... "$unset":{"gender":true},
... "$inc":{"age":1},
... "$rename":{"name":"username"}
... }
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
# 选项 upsert的使用
> db.c1.update(
... {"name":"Y1"},
... {"$set":{"name":"rose","age":8,"email":"rr@qq.com"}}
... )
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
> db.c1.update(
... {"name":"Y1"},
... {"$set":{"name":"rose","age":8,"email":"rr@qq.com"}},
{"upsert":true}
... )
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("608269aa6d779b5456ab6259")
})
# 选项 multi的使用
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":10}}
... )
> db.c1.update( {"name":"rose"}, {"$inc":{"age":10}} )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.c1.update(
... {"name":"rose"},
... {"$inc":{"age":10}} ,
{"multi":true}
... )
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
```
#### 2)save()
**语法 :db.集合名.save({文档数据})**
通过传入的文档来**替换**已有文档,也可能执行**插入**
如果没有传入**_id**属性,或者**_id**属性的值不存在 ==> 插入,否则就是按照**_id**做条件来替换已有文档。
```mysql
{ "_id" : 4, "name" : "X1" }
> db.c1.save({
... "name":"X1",
... "gender":"M"
... }
... )
WriteResult({ "nInserted" : 1 })
> db.c1.save(
... {
... "_id": 6,
... "name":"X1",
... "age":6
... }
... )
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 6 })
> db.c1.save(
... {"_id":4 , "name":"XX1" , "gender":"F"}
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
```
### 5. 删除文档
**语法:db.集合名.remove(条件 [, {justOne:boolean} ] )**
justOne是否删除一条,true-是;false-否 默认
条件描述 : {} json数据格式 , 如果条件描述中出现多个属性,是与and关系
**remove({}) ==> 删除集合所有文档**
```mysql
> db.c1.remove({"name":"X1"})
WriteResult({ "nRemoved" : 2 })
> db.c1.remove(
... {"name":"rose"},
... {"justOne":true}
... )
WriteResult({ "nRemoved" : 1 })
> db.c1.remove({"_id":4})
WriteResult({ "nRemoved" : 1 })
> db.c1.remove({"_id": ObjectId("608221c66baab5abac64613b")})
WriteResult({ "nRemoved" : 1 })
> for(i=1;i<=10;i++){
db.c2.insert({"name" : "N" + i})
}
> db.c2.remove({})
WriteResult({ "nRemoved" : 10 })
```
### 6. 查询文档--find()
**语法:db.集合名.find(条件 [, 要显示的key])**
> tip : 可以最后加一个pretty() 格式化展示数据
#### 1)基本查询
```
条件描述 : "="
{} 或者 不写 查询所有文档
{"age":26} age=26
{"age":26,"gender":"M"} age=26 and gender='M'
{"age":12,"age":23} age=23(age=23覆盖条件age=12)
查询的属性
不写 所有key
{"age":1} 只显示key为age
{"age":1 , "gender":1} 显示key为age和gender
{“age":0} 除了key为age的其他
【注】_id 属性默认显示,如果不希望出现使用 "_id:0"
```
```mysql
> db.c1.find(
... {"name":"lisi"}
... )
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
>
> db.c1.find(
... {"gender":"F","name":"isoft"}
... )
>
> db.c1.find(
... {"age":12}
... )
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
>
> db.c1.find(
... {"age":12,"age":23}
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
> db.c1.find(
... {"age":23,"age":12}
... )
{ "_id" : ObjectId("608223c96baab5abac64613e"), "name" : "zhangsan", "age" : 12, "email" : "zs@qq.com" }
> db.c1.find(
... {},
... {"name":1}
... )
> db.c1.find(
... {},
... {"name":0}
... )
> db.c1.find(
... {},
... {"_id":0,"name":1,"email":1}
... )
```
#### 2)条件运算符的使用
```
描述 :{"key" : {"运算符" : value}}
```
| 运算符 | 作用 |
| ------ | :-------------------------------------------- |
| $gt | > |
| $gte | >= |
| $lt | < |
| $lte | <= |
| $ne | != |
| $in | in alue值是数组, {"age" : {"$in" : [10,20]} |
| $nin | not in |
```mysql
# 数据准备
> for(i=1;i<=10;i++){
... db.c1.update({"name":"isoft"+i},{"$set":{"age":10+i}})
... }
> db.c1.find( {"age":{"$gt":20}})
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
> db.c1.find(
... {"age":{"$lte":15}}
... )
> db.c1.find(
... {"gender":{"$in":['f']}}
... )
> db.c1.find( {"gender":{"$in":['f','F','0']}} )
> db.c1.find(
... {"age":{"$nin":[20]}}
... )
> db.c1.find(
{"age":{"$ne":20}}
)
```
#### 3)逻辑与、或
$and , $or
```
{“$and|$or" : [{} , {} ,...]
```
```mysql
# 查询age<12或者 age>20
> db.c1.find(
... {"$or":[
... {"age":{"$lt":12}},
... {"age":{"$gt":20}}
... ]}
... )
# 查询20<age<12
> db.c1.find(
... {"$and":[
... {"age":{"$gt":12}},
... {"age":{"$lt":20}}
... ]}
... )
#查询姓名是rose的文档 或者 年龄在18以上的男性文档
> db.c1.find(
... {"$or" : [
... {"name":"rose"},
... {"age":{"$gt":18} , "gender":"M"}
... ]}
... )
> db.c1.find(
{"$or" : [
{"name":"rose"},
{"$and":[
{"age":{"$gt":18}} ,
{"gender":"M"}
] }
] }
)
```
#### 4)$not、 $type
https://www.runoob.com/mongodb/mongodb-operators-type.html
double -- 1 , string--2 , object -- 3 , array -- 4 , null -- 10 , 32位int--16 , 64位int--18
NumberInt() , NumberLong()
【注意】**值是Array类型比较特殊,使用$not结合$type否定类型时,Array不能被检索到**
```mysql
# 数据准备
> db.c1.insert({"name":10,"age":11})
> db.c1.insert({"name":NumberInt(20),"age":20})
> db.c1.insert({"name":["r","o"],"age":20})
> db.c1.insert({"name":{"first":"rose","last":"L"}})
# 查询name值是 double类型
> db.c1.find(
... {"name":{"$type":1}}
... )
# 查询name的value值类型是数值类型
> db.c1.find(
... {"$or":[
... {"name":{"$type":1}},
... {"name":{"$type":16}},
... {"name":{"$type":18}}
... ]}
... )
> db.c1.find(
... {"name": {"$type":{"$in":[1,16,18]}}}
... )
Error: error: {
"ok" : 0,
"errmsg" : "type must be represented as a number or a string",
"code" : 14,
"codeName" : "TypeMismatch"
}
# 查询name的value值类型是string类型
> db.c1.find(
... {"name":{"$type":2}}
... )
# 查询name的value值类型不是string类型 ???? name值是array的
> db.c1.find(
... {"name":{"$not":{"$type":2}}}
... )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
{ "_id" : ObjectId("6084c12a95c3b0b8ed942f79"), "name" : 10, "age" : 11 }
{ "_id" : ObjectId("6084c1e495c3b0b8ed942f7a"), "name" : 20, "age" : 20 }
{ "_id" : ObjectId("6084c9b895c3b0b8ed942f7d"), "name" : { "first" : "rose", "last" : "L" } }
# 正确为
> db.c1.find(
... {"$or":[
... {"name":{"$not":{"$type":2}}} ,
... {"name": {"$type":4}}
... ]}
... )
```
#### 5)null与not null
【注】条件表述为 null ==》**该key不存在或者key的值是null的两种情况的文档**
```
value 值是null 的判定 :
{"key":null}
{"key":{"$in":[null]}}
value 值不是null 的判定 :
{"key":{"$nin":[null]}}
```
**可以结合 $in , $type , $exists , $not 一起使用**
```mysql
> db.c1.insert({"name":null,"age":0})
WriteResult({ "nInserted" : 1 })
# 查询 name 是 null ==> 结果是 name为null或者不存在name属性
> db.c1.find( {"name":null} )
{ "_id" : ObjectId("608222976baab5abac64613d"), "age" : 23, "username" : "mike" }
{ "_id" : ObjectId("6084c61495c3b0b8ed942f7c"), "name" : null, "age" : 0 }
# 查询name属性存在,且值是null的文档
> db.c1.find(
... {"name":{"$in":[null] , "$exists":true} }
... )
{ "_id" : ObjectId("6084c61495c3b0b8ed942f7c"), "name" : null, "age" : 0 }
# 查询 name 属性不是null ==> name不是null或者不存在name属性
> db.c1.find(
... {"name":{"$not":{"$type":10}}}
... )
# 查询 name 有确定值
> db.c1.find(
... {"$and" : [
... {"name":{"$not":{"$type":10}}} ,
... {"name":{"$exists":true}}
... ]}
... )
# 或者
> db.c1.find(
... {"name":{"$nin":[null]}}
... )
```
#### 6)正则表达式的使用
```
{"key": /正则表达式/修饰符 }
或者
{"key":
{"$regex":"正则表达式" [,{"$options":"修饰符"}]}
}
-- 含有字符 /字符/
-- 以字符开头 /^字符/
-- 以字符结尾 /字符$/
-- 常用的修饰符就是 i => 不区分大小写
```
```mysql
> db.c1.insert({"name":"Im"})
# name的值是i
> db.c1.find(
... {"name":"i"}
... )
# name的值含有i
> db.c1.find(
... {"name":/i/}
... )
# 使用修饰符
> db.c1.find(
... {"name":/i/i}
... )
# 使用正则表达式另外一种写法
> db.c1.find(
... {"name":{"$regex":"i"}}
... )
> db.c1.find( {"name":{"$regex":"i","$options":"i"}} )
# 以字符i开头
> db.c1.find(
... {"name":/^I/}
... )
{ "_id" : ObjectId("608508a0b2e7bc32dfacb336"), "name" : "Im" }
> db.c1.find(
... {"name":/I/i}
... )
# 以字符i结尾
> db.c1.find(
... {"name":/i$/}
... )
{ "_id" : ObjectId("608223c96baab5abac64613f"), "name" : "lisi", "email" : "ls@qq.com", "gender" : "F" }
# 查询name属性是长度2--10的英文value值
> db.c1.find(
... {"name":/^[a-zA-Z]{2,10}$/}
... )
```
#### 7)查询数组值
##### -- 条件描述
```
$all : { "key“ : {"$all" : ["v1","v2" ,...]} } key in [v1 and v2]
key.index : index从0数起,用来限定数组的第几个值的条件描述
$size :限定数组元素值的个数 {"key":{"$size":int}}
```
```mysql
> data = [
... {"name":"A" , "fruit":["apple","banana","lemon"]} ,
... {"name":"B" , "fruit":["apple","orance","peach"]} ,
... {"name":"C" , "fruit":["cherry","banana","apple"]} ,
... {"name":"D" , "fruit":["cherry"]}
... ]
> db.c2.insert(data)
# 查找 fruit值含有 apple和banana的文档
-- 使用 $in 是 or
> db.c2.find(
... {"fruit":{"$in":["apple","banana"]}}
... )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb337"), "name" : "A", "fruit" : [ "apple", "banana", "lemon" ] }
{ "_id" : ObjectId("60850b58b2e7bc32dfacb338"), "name" : "B", "fruit" : [ "apple", "orance", "peach" ] }
{ "_id" : ObjectId("60850b58b2e7bc32dfacb339"), "name" : "C", "fruit" : [ "cherry", "banana", "apple" ] }
-- 正确写法
> db.c2.find(
... {"fruit":{"$all":["apple","banana"]}}
... )
# 查询 fruit的第三个元素值是 apple
> db.c2.find( {"fruit.2":"apple"} )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb339"), "name" : "C", "fruit" : [ "cherry", "banana", "apple" ] }
> db.c2.find(
... {"fruit.2":/apple/i}
... )
# 按照元素个数匹配
> db.c2.find(
... {"fruit":{"$size":1}}
... )
{ "_id" : ObjectId("60850b58b2e7bc32dfacb33a"), "name" : "D", "fruit" : [ "cherry" ] }
> db.c2.find(
... {"fruit":{"$size":{"$gt":2}}}
... )
Error: error: {
"ok" : 0,
"errmsg" : "$size needs a number",
"code" : 2,
"codeName" : "BadValue"
}
```
##### -- 切片 $slice,返回限定个数
```mysql
> db.c2.find(
... {},
... {"fruit":{"$slice":1}} # 第一个元素
... )
> db.c2.find( {}, {"fruit":{"$slice":-1}} ) # 最后一个元素
> db.c2.find( {}, {"fruit":{"$slice":[1,3]}} ) # 索引位置从1开始,向后2个
```
##### -- 数组元素范围匹配 $elemMatch
> 如果查询条件只是一半范围,没有必要使用 $elemMath
```
数组元素值含有满足条件的情况的文档
{"key" : {"$elemMatch":{条件1, 条件2}}}
```
```mysql
> arr = [
... {"_id":1 , "x":5} ,
... {"_id":2 , "x":15} ,
... {"_id":3 , "x":25} ,
... {"_id":4 , "x":[5,25]} ,
... {"_id":5 , "x":[5,15,20,25,30]} ,
... {"_id":6 , "x":[8,16,24,36]}
... ]
>db.c3.insert(arr)
# 以下值限定不仅匹配普通value值,也匹配数组
> db.c3.find( {"x" : {"$gte":15}} )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 去掉数组类型值得匹配
> db.c3.find(
... {"$and":[
... {"x":{"$gte":15}},
... {"x":{"$not":{"$type":4}}}
... ]}
... )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
# 值在某个范围
> db.c3.find(
... {"$and":[
... {"x":{"$gt":10}},
... {"x":{"$lt":30}}
... ]}
... )
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 , 含有值 >=8
> db.c3.find(
{"x":{"$elemMatch":{"$gte":8}}}
)
-- 等价于
> db.c3.find(
{"$and" :[
{"x":{"$gte":8}} ,
{"x" : {"$type":4}}
]}
)
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 ,含有值 >=8 并且 <40
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":40}}} )
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":20}}} )
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
```
#### 8)查询内嵌文档值
```
key.property
```
```mysql
> users = [
... {"name":{"first":"rose","last":"A"} , "age" : 18 },
... {"name":{"first":"joe","last":"B"} , "age" : 28 },
... {"name":{"first":"mike","last":"C"} , "age" : 23 }
... ]
> db.c4.insert(users)
# 使用 key.属性 进行查询
> db.c4.find(
... {"name.last":/a/i}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
>
> db.c4.find(
... {"name.first":/o/i}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
{ "_id" : ObjectId("60851611b2e7bc32dfacb33c"), "name" : { "first" : "joe", "last" : "B" }, "age" : 28 }
>
> db.c4.find(
... {"name":{"first":"rose","last":"A"}}
... )
{ "_id" : ObjectId("60851611b2e7bc32dfacb33b"), "name" : { "first" : "rose", "last" : "A" }, "age" : 18 }
# $elemMatch的使用
> books={
... "title":"A",
... "content":"helloworld is java",
... "comments":[
... {"author":"joe" , "score":3 , "comment":"good"} ,
... {"author":"rose" , "score":6 , "comment":"terrible"} ,
... ]
... }
> db.c5.insert(books)
# 查询 同一条评论作者是joe,并且分值在5分以上 ==》没有满足条件的文档
> db.c5.find(
... {"comments": {"$elemMatch":{"author":"joe" , "score":{"$gte":5}}}}
... )
# 查询 同一条评论作者是rose,并且分值在5分以上
> db.c5.find(
... {"comments": {"$elemMatch":{"author":"rose" , "score":{"$gte":5}}}}
... )
```
#### 9)$where
$where 可以执行任意的javascript函数,来实现查询条件描述。
!!!当使用$where时,要将集合中每个文档从Bson转换为js对象,然后在进行$where的js函数处理,性能非常低,**尽量不使用**。
```
{"$where": js函数 }
{"$where" : "js条件表达式" }
【注】在描述$where的条件时,js代码可以使用"this"指代当前文档 ; 函数返回boolean值
```
```mysql
> db.c3.find()
{ "_id" : 1, "x" : 5 }
{ "_id" : 2, "x" : 15 }
{ "_id" : 3, "x" : 25 }
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数组元素的值在某个范围之间 ,含有值 >=8 并且 <40
> db.c3.find( {"x":{"$elemMatch":{"$gte":8,"$lt":40}}} )
{ "_id" : 4, "x" : [ 5, 25 ] }
{ "_id" : 5, "x" : [ 5, 15, 20, 25, 30 ] }
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
> js = function() {
... arr = this.x ;
... for(n in arr) {
... return arr[n]>=8 && arr[n]<40
... }
... }
> db.c3.find(
... {"$where":js}
... )
{ "_id" : 6, "x" : [ 8, 16, 24, 36 ] }
# 数据准备
> data = [
... {"name":"joe" , age : "12" , brotherAge : 20 },
... {"name":"rose" , age : "20" , brotherAge : 12 }
... ]
> db.c4.insert(data)
# 查询集合中当前文档记录的是弟弟或者妹妹信息的数据
> db.c4.find(
... {"$where":"this.age<this.brotherAge"}
... )
{ "_id" : ObjectId("60860de108e9f867708fcb4c"), "name" : "joe", "age" : "12", "brotherAge" : 20 }
```
#### 10)分页--用在find()后面
**skip(跳过的文档书) 与 limit(返回的文档数)**
```mysql
> db.c1.find({},{"_id":0}).skip(3)
> db.c1.find({},{"_id":0}).limit(3)
> db.c1.find({},{"_id":0}).skip(3).limit(10)
```
#### 11)排序--用在find()后面
**sort({"key" : 1|-1 , .... })** 1=>升序,-1=>降序
> tip : 分页和排序同时使用 :
>
> find().sort().skip().limit()
```mysql
> db.c1.find({},{"_id":0}).sort({"age":-1})
> db.c1.find({},{"_id":0}).sort({"age":-1,"name":1})
> db.c1.find({},{"_id":0}).sort({"name":1}).skip(5).limit(5)
```
### 7. 查询一条文档—findOne()
```mysql
> db.c1.findOne()
{
"_id" : ObjectId("608222976baab5abac64613d"),
"age" : 23,
"username" : "mike"
}
> db.c1.findOne(
... {"name":/o/i , "age":{"$gt":18}}
... )
{
"_id" : ObjectId("608225516baab5abac646148"),
"name" : "isoft9",
"gender" : "M",
"age" : 19
}
```
### 8. count()
**语法 : db.集合名.count(条件)**
作用:返回满足条件的文档数量
```mysql
> db.c1.count()
20
> db.c1.count(
... {"name":/o/i}
... )
12
> db.c1.count(
... {"$and":[
... {"age":{"$gte":10}},
... {"age":{"$lte":20}}
... ]}
... )
15
```
### 9. distinct()
**语法 :db.集合名.distinct("key" [,条件])**
作用 :返回满足条件的文档指定key的不同值有哪些,返回值类型是数组
```mysql
> db.c1.distinct("gender")
[ "F", "M" ]
> db.c1.distinct("age")
[ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 23 ]
> db.c1.distinct("age" , {"name":/o/i})
[ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ]
```
## 0425—find查询(笔记见上方)
## 0426—查询、游标、索引
### 1. find查询(笔记见上方)
### 2. findOne()(笔记见上方)
### 3. count()(笔记见上方)
### 4. distinct()(笔记见上方)
### 5. 管道(自学)
https://www.cnblogs.com/yunlongaimeng/p/11460291.html
https://www.cnblogs.com/qlqwjy/p/8652555.html
### 6. 游标
#### 1)游标
不是查询结果,而是查询的返回接口或者记录指针。
通过游标可以逐行读取数据。
声明游标时不执行查询,通过游标提取数据时才执行查询
#### 2)游标相关操作
-- 声明游标
**var 游标 = 查询**
-- 判断游标是否到达了末尾
**游标.hasNext()** 返回一个boolean
-- 取出游标所指向的下一个文档
**游标.next()**
-- 循环遍历游标
**while(游标.hasNext()) {**
...
游标.next()
...
**}**
或者
**游标.forEach(回调函数)**
-- 将游标所对应的查询结果文档转为一个数组
**数组 = 游标.toArray()**
```mysql
> var cur = db.c2.find()
>
> print(cur.hasNext())
true
>
> print(cur.next())
[object BSON]
>
> printjson(cur.next())
{
"_id" : ObjectId("60850b58b2e7bc32dfacb338"),
"name" : "B",
"fruit" : [
"apple",
"orance",
"peach"
]
}
> while(cur.hasNext()){
... printjson(cur.next())
... }
{
"_id" : ObjectId("60850b58b2e7bc32dfacb339"),
"name" : "C",
"fruit" : [
"cherry",
"banana",
"apple"
]
}
{
"_id" : ObjectId("60850b58b2e7bc32dfacb33a"),
"name" : "D",
"fruit" : [
"cherry"
]
}
> var cur2 = db.c1.find({"gender":"F"})
> cur2.forEach(function(doc){
... printjson(doc)
... print(doc.name+"--"+doc.gender)
... })
> print(cur2.hasNext())
false
> var cur3 = db.c1.find({"age":{"$gte":20}})
> age20 = cur3.toArray()
[
{
"_id" : ObjectId("608222976baab5abac64613d"),
"age" : 23,
"username" : "mike"
},
{
"_id" : ObjectId("608225516baab5abac646149"),
"name" : "isoft10",
"gender" : "F",
"age" : 20
},
{
"_id" : ObjectId("6084c1e495c3b0b8ed942f7a"),
"name" : 20,
"age" : 20
},
{
"_id" : ObjectId("6084c37995c3b0b8ed942f7b"),
"name" : [
"r",
"o"
],
"age" : 20
}
]
> print(age20)
[object BSON],[object BSON],[object BSON],[object BSON]
> printjson(age20)
....
```
## 第三章 MongoDB的索引
### 1. 简介
**索引通常能够极大的提高查询的效率**,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文档并选取那些符合查询条件的文档。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
### 2. 优缺点
-- 优点
极大的提高查询的效率
-- 缺点
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。
【很少对集合进行读取操作,建议不使用索引】
### 3. 限制
- 集合中索引不能超过64个
- 索引名的长度不能超过128个字符
- 一个复合索引最多可以有31个字段
### 4. 查询性能分析
**语法 :db.集合名.find(..).explain(["executionStats"])**
显示结果 :关注 winningPlan ==>stage|inputState ==>stage的值
COLLSCAN 全集和扫描
IXSCAN 索引扫描
IDHACK _id为条件应用自动为该key创建的索引进行扫描
### 5. 索引的类型
-- 索引类型
单键索引、复合索引、多键索引、哈希索引、全文索引、地理空间索引
-- 索引属性
唯一索引、局部索引、稀疏索引、TTL索引
### 6. 创建索引
**语法形式 : db.createIndex({"key":1|-1 , ...} [, 额外选项] )**
额外选项 :
| background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为**false**。 |
| :---------------------------------- | ------------- | ------------------------------------------------------------ |
| unique(唯一索引) | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为**false**. |
| name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。<br />默认索引名是key_1\|key_-1 |
| partialFilterExpression(局部索引) | 条件 | 针对key满足条件的文档创建索引 |
| sparse(稀疏索引) | Boolean | 稀疏索引(或者称间隙索引)就是只包含有索引字段的文档的条目,跳过索引键不存在的文档 |
| expireAfterSeconds(TTL索引) | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
| v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
### 7. 删除索引
db.集合名.dropIndex(索引名)
db.集合名.dropIndexes()
### 8 . 查看索引
db.集合.getIndexes()
> tip : MongoDB中为集合默认为_id属性创建一个单键索引
```mysql
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test12.c1"
}
]
```
### 9. 索引类型
#### 1)单键索引
单键索引(Single Field Indexes)顾名思义就是单个字段作为索引列,mongoDB的所有collection默认都有一个单键索引_id。
```mysql
> db.c1.createIndex(
... {"name":1},
... {"name":"name_index1"}
... )
> db.c1.createIndex({"age":1})
> db.c1.dropIndex("age_1")
> db.c1.dropIndex("name_index1")
```
#### 2)复合索引
复合索引(Compound Indexes)指一个索引包**含多个key**,用法和单键索引基本一致。使用复合索引时要注意字段的顺序。mongoDB中一个复合索引最多可以包含32个字段。
```mysql
> db.c1.find(
... {"$and":[
... {"name":/o/i},
... {"age":{"$gt":15}}
... ]}
... ).explain()
{
"queryPlanner" : {
"....
"winningPlan" : {
"stage" : "COLLSCAN",
....
},
....
}
>
> db.c1.createIndex(
... {"name":1 , "age":1}
... )
> db.c1.getIndexes()
[
....
{
"v" : 2,
"key" : {
"name" : 1,
"age" : 1
},
"name" : "name_1_age_1",
"ns" : "test12.c1"
}
]
> db.c1.find( {"$and":[ {"name":/o/i}, {"age":{"$gt":15}} ]} ).explain()
{
"queryPlanner" : {
....
"winningPlan" : {
....
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"name" : 1,
"age" : 1
},
"indexName" : "name_1_age_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"name" : [
"name"
],
"age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
.....
}
```
#### 3)多键索引
多键索引(mutiKey Indexes)是建在数组上的索引 。
```mysql
# 准备数据
> db.classes.insert([
... ... {
... ... "classname":"class1",
... ... "students":[{name:'jack',age:20},
... ... {name:'tom',age:22},
... ... {name:'lilei',age:25}]
... ... },
... ... {
... ... "classname":"class2",
... ... "students":[{name:'lucy',age:20},
... ... {name:'jim',age:23},
... ... {name:'jarry',age:26}]
... ... }
... ... ])
> db.classes.createIndex({"students.age":1})
> db.classes.find({"students.age":20}).explain()
```
#### 4)哈希索引
哈希索引(hashed Indexes)就是将field的值进行hash计算后作为索引,其强大之处在于实现0(1)查找,当然用哈希索引最主要的功能也就是实现**定值查找**(key=value),对于经常需要排序或查询范围查询的集合不要使用哈希索引。
**语法 : {"key":"hashed"}**
```mysql
> db.c1.createIndex({"gender":"hashed"})
> db.c1.find({"gender":"F"}).explain()
```
### 10. 索引属性
#### 1)唯一索引
唯一索引(unique indexes)用于为collection添加唯一约束,即**强制要求collection中的索引key没有重复值**。
语法 :**{unique:true}**
【注】创建该索引前,创建索引的key不能有重复的value!
```mysql
> db.c1.createIndex(
... {"name":-1},
... {"unique":true}
... )
{
"ok" : 0,
"errmsg" : "E11000 duplicate key error collection: test12.c1 index: name_-1 dup key: { name: \"zhangsan\" }",
"code" : 11000,
"codeName" : "DuplicateKey",
"keyPattern" : {
"name" : -1
},
"keyValue" : {
"name" : "zhangsan"
}
}
# 移出name的重复值
> db.c1.createIndex( {"name":-1}, {"unique":true} )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}
{
"v" : 2,
"unique" : true,
"key" : {
"name" : -1
},
"name" : "name_-1",
"ns" : "test12.c1"
}
> db.c1.insert({"name":"zhangsan"})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test12.c1 index: name_-1 dup key: { name: \"zhangsan\" }"
}
})
```
#### 2)局部索引
只对collection的一部分添加索引。创建索引的时候,根据过滤条件判断是否对document添加索引,对于没有添加索引的文档查找时采用的全表扫描,对添加了索引的文档查找时使用索引。
语法 : {"partialFilterExpression" : {条件}}
```mysql
> db.c1.createIndex({"age":1},{"partialFilterExpression":{"age":{"$gt":15}}})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.c1.getIndexes()
[
{
"v" : 2,
"key" : {
"age" : 1
},
"name" : "age_1",
"ns" : "test12.c1",
"partialFilterExpression" : {
"age" : {
"$gt" : 15
}
}
}
]
> db.c1.find({"age":{"$lt":15}}).explain()
> db.c1.find({"age":{"$gt":18}}).explain()
```
#### 3)稀疏索引
稀疏索引(sparse indexes)在有索引字段的document上添加索引,如在address字段上添加稀疏索引时,只有document有address字段时才会添加索引。而普通索引则是为所有的document添加索引,使用普通索引时如果document没有索引字段的话,设置索引字段的值为null。
语法 :**{"sparse": true}**
```mysql
# 数据准备
> db.scores.insert([
... { "_id" : ObjectId("523b6e32fb408eea0eec2647"), "userid" : "newbie" },
... { "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 },
... { "_id" : ObjectId("523b6e6ffb408eea0eec2649"), "userid" : "nina", "score" : 90 }])
> db.scores.createIndex( { score: 1 } , { sparse: true } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.scores.find( { score: { $lt: 90 } } )
{ "_id" : ObjectId("523b6e61fb408eea0eec2648"), "userid" : "abby", "score" : 82 }
# 由于文档newbie并不包含score键,因此该文档不会出现在稀疏索引之中,也就不会被查询返回
> db.scores.find( { score: { $lt: 90 } } ).explain()
{
"queryPlanner" : {
。。。
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"score" : 1
},
"indexName" : "score_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"score" : [ ]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"score" : [
"[-inf.0, 90.0)"
]
}
}
},
。。。。
}
```
#### 4)TTL索引
TTL索引(time-to-live index 具有生命周期的索引)是一种特殊的单键索引,用于**设置document的过期时间**,mongoDB会在document过期后将其删除,TTL非常容易实现类似缓存过期策略的功能。
语法 : **{"expireAfterSeconds" : 秒值}**
【注】TTL索引只能设置**在date类型字段(或者包含date类型的数组)**上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;
```mysql
-- 数据准备 --
> db.logs.insert([
... {_id:1,createtime:new Date(),msg:"log1"},
... {_id:2,createtime:new Date(),msg:"log2"},
... {_id:3,createtime:new Date(),msg:"log3"},
... {_id:4,createtime:new Date(),msg:"log4"}
... ])
> db.logs.createIndex(
... {"createtime":1},
... {"expireAfterSeconds":20}
... )
{
... "v" : 2,
... "key" : {
... "createtime" : 1
... },
... "name" : "createtime_1",
... "ns" : "test12.logs",
... "expireAfterSeconds" : 20
... }
# 等待一会儿去执行查看
> db.logs.find()
```
【注】TTL索引只能设置在date类型字段(或者包含date类型的数组)上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;
### 11. 全文索引
【在mongodb中,每个数据**集合只能创建一个全文索引**】
全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
这个过程类似于通过字典中的检索字表查字的过程。
**语法:db.集合名.ensureIndex({字段名 : "text“});**
```mysql
-- 数据准备
> posts=[
{
"post_text": "enjoy the mongodb articles on Runoob",
"tags": [
"mongodb",
"runoob"
]
},
{
"post_text": "enjoy the python articles on Mrs Gao",
"tags": [
"python",
"gao"
]
},
]
> db.posts.insert(posts)
-- 创建全文索引
> db.posts.ensureIndex({"post_text":"text"})
-- 执行查询 , 使用索引
> db.posts.find({"$text":{"$search":"runoob"}})
```
**使用全文索引:使用全文索引跟使用其他索引不一样,我们不再需要key的名字,而是直接使用$text,$search进行查询。**
### 12. 地理空间索引
随着移动设备的应用的爆发式增长,有一种查询变得越来越流行:找到离当前位置最近的N个场所。MongoDB为坐标平面查询提供了专门的索引,称作地理空间索引。地理空间索引分2dsphere索引和2d索引。
## 0427
## 第四章 MongoDB GridFS
### 1. GridFS简介
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。
GridFS 可以更好的存储大于16M的文件(MongoDB的BSON格式的数据(文档)存储有尺寸限制,最大为16M)。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。
### 2. 使用场景
-- 如果您的文件系统在一个目录中存储的文件数量有限(太多将会影响文件的打开速度),你可以使用GridFS存储尽可能多的文件。
-- 当你想访问大型文件的部分信息,却不想加载整个文件到内存时,您可以使用GridFS存储文件,并读取文件部分信息,而不需要加载整个文件到内存。
-- 你想让你的文件和元数据自动同步并部署在多个系统和设施,你可以使用GridFS实现分布式文件存储。
### 3. 存储相关集合
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。
以下是简单的 **fs.files** 集合文档:
```
{
"_id": <ObjectId>, // 文档id,唯一标识
"filename": <string>,
"chunkSize": <num>,
"uploadDate": <ISODate>,
"md5": <string>,
"length": <num>,
"metadata":<dataObject>
}
```
以下是简单的 **fs.chunks** 集合文档:
```
{
"_id": <ObjectId>,
"files_id": <ObjectId>, // 对应fs.files文档的 _id
"n": <num>, // 序号,标识文档的第几个chunk
"data": <binary> // 文件二进制数据,不要显示该属性值
}
```
### 4. GridFS操作
使用MongoDB安装目录"bin/" 的mongofiles 工具
**命令格式 :**
**mongofiles \<options> \<command> \<filename or _id>**
```
command :
list , search , put , get , delete
put_id , get_id , delete_id
options :
-d 数据库名
-l 文件位置及名称
```
```mysql
# 上传一个20K+的文件
D:\green_software\mongodb\bin>mongofiles -d gridfs -l "d:\OO\Pictures\skb.png" put m1.png
2021-04-27T08:53:12.180+0800 connected to: mongodb://localhost/
2021-04-27T08:53:12.350+0800 added gridFile: m1.png
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.000GB
local 0.000GB
test12 0.001GB
> use gridfs
switched to db gridfs
> show collections
fs.chunks
fs.files
> db.fs.files.find().pretty()
{
"_id" : ObjectId("60876078661647544d94250a"),
"length" : NumberLong(24775),
"chunkSize" : 261120,
"uploadDate" : ISODate("2021-04-27T00:53:12.348Z"),
"filename" : "m1.png",
"metadata" : {
}
}
> db.fs.chunks.find({} , {"data":0})
{ "_id" : ObjectId("60876078661647544d94250b"), "files_id" : ObjectId("60876078661647544d94250a"), "n" : 0 }
# 上传一个 2M+ 的文件
D:\green_software\mongodb\bin>mongofiles -d gridfs -l d:\OO\Pictures\mongofile.jpg put m2.jpg
2021-04-27T08:59:39.923+0800 connected to: mongodb://localhost/
2021-04-27T08:59:40.008+0800 added gridFile: m2.jpg
D:\green_software\mongodb\bin>mongo
> use gridfs
switched to db gridfs
> show collections
fs.chunks
fs.files
> var fid=db.fs.files.findOne({"filename":"m2.jpg"})._id
> fid
ObjectId("60876ab999b849dc1310485c")
> db.fs.chunks.find({"files_id":fid},{"data":0})
{ "_id" : ObjectId("608761fb6f324480d836e155"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 0 }
{ "_id" : ObjectId("608761fb6f324480d836e156"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 1 }
{ "_id" : ObjectId("608761fb6f324480d836e157"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 2 }
{ "_id" : ObjectId("608761fb6f324480d836e158"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 3 }
{ "_id" : ObjectId("608761fb6f324480d836e159"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 4 }
{ "_id" : ObjectId("608761fb6f324480d836e15a"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 5 }
{ "_id" : ObjectId("608761fb6f324480d836e15b"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 6 }
{ "_id" : ObjectId("608761fb6f324480d836e15c"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 7 }
{ "_id" : ObjectId("608761fb6f324480d836e15d"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 8 }
{ "_id" : ObjectId("608761fb6f324480d836e15e"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 9 }
{ "_id" : ObjectId("608761fb6f324480d836e15f"), "files_id" : ObjectId("608761fb6f324480d836e154"), "n" : 10 }
# 测试其他命令
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:08:01.102+0800 connected to: mongodb://localhost/
m1.png 24775
m2.jpg 2852907
D:\green_software\mongodb\bin>mongofiles -d gridfs search m1.jpg
2021-04-27T09:08:40.609+0800 connected to: mongodb://localhost/
D:\green_software\mongodb\bin>mongofiles -d gridfs search m1.png
2021-04-27T09:08:45.972+0800 connected to: mongodb://localhost/
m1.png 24775
D:\green_software\mongodb\bin>mongofiles -d gridfs get m1.png
2021-04-27T09:09:21.635+0800 connected to: mongodb://localhost/
2021-04-27T09:09:21.649+0800 finished writing to m1.png
D:\green_software\mongodb\bin>mongofiles -d gridfs -l d:\mongofile.jpg get m2.jpg
2021-04-27T09:10:41.794+0800 connected to: mongodb://localhost/
2021-04-27T09:10:41.832+0800 finished writing to d:\mongofile.jpg
D:\green_software\mongodb\bin>mongofiles -d grifs delete m1.png
2021-04-27T09:11:25.403+0800 connected to: mongodb://localhost/
2021-04-27T09:11:25.415+0800 successfully deleted all instances of 'm1.png' from GridFS
D:\green_software\mongodb\bin>mongofiles -d grifs delete m2.jpg
2021-04-27T09:11:35.724+0800 connected to: mongodb://localhost/
2021-04-27T09:11:35.737+0800 successfully deleted all instances of 'm2.jpg' from GridFS
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:11:46.170+0800 connected to: mongodb://localhost/
m1.png 24775
m2.jpg 2852907
D:\green_software\mongodb\bin>net stop mongodb
MongoDB 服务正在停止.
MongoDB 服务已成功停止。
D:\green_software\mongodb\bin>net start mongodb
MongoDB 服务正在启动 ..
MongoDB 服务已经启动成功。
D:\green_software\mongodb\bin>mongofiles -d gridfs list
2021-04-27T09:14:40.243+0800 connected to: mongodb://localhost/
```
## 第五章 数据库的备份与还原
### 1. 备份数据库 mongodump
**命令 :mongodump -h host:port - u username -p password -d dbName -o directory**
```
-h 服务器地址【不写默认本机】
:port 端口号(不写默认27017)
-u 账号
-p 密码
-d 需要备份的数据库实例,不写导出全局
-o 备份的数据存放位置,例如:c:\data\dump【目录已存在】
```
```mysql
# 备份本机服务器下所有数据库
D:\green_software\mongodb\bin>mongodump -o d:\mongodump
# 备份数据库 test12
D:\green_software\mongodb\bin>mongodump -d test12 -o d:\mongodump
```
### 2. 还原数据库 mongorestore
**命令 :mongorestore - h host:port - u username -p password -d dbName --drop directory**
```mysql
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
test12 0.001GB
> use test12
switched to db test12
> db.dropDatabase()
{ "dropped" : "test12", "ok" : 1 }
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
> exit
D:\green_software\mongodb\bin>mongorestore -d test d:\mongodump\test12
D:\green_software\mongodb\bin>mongo
> show dbs
admin 0.000GB
config 0.000GB
gridfs 0.003GB
local 0.000GB
test 0.000GB
> use test
switched to db test
> show collections
c1
c2
c3
c4
c5
classes
logs
posts
scores
user
> db.coll.insert({"name":123})
WriteResult({ "nInserted" : 1 })
> exit
bye
D:\green_software\mongodb\bin>mongorestore -d test --drop d:\mongodump\test12
```
## 0429
### 1. Linux下MongoDB的配置(笔记见上方)
### 2. MongoDB的复制系统(见MongoDB复制笔记)
## 第六章 :MongoDB的复制系统
## 0428
## 第七章:其他语言访问MongoDB
### 1. 使用Java语言访问
> tip : 使用原生态方式访问 MongoDB
>
> SpringBoot中访问MongoDB,spring-boot-starter-data-mongodb
#### 1)添加jar包 ==> Maven 依赖项
```XML
<properties>
<mongo.driver.version>3.12.7</mongo.driver.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>${mongo.driver.version}</version>
</dependency>
</dependencies>
```
```java
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
```
#### 2)连接服务器
```java
/* mongodb://user:password@host:port */
String connStr = "mongodb://host:27017";
MongoClientURI uri = new MongoClientURI(connStr) ;
MongoClient mongoClient = new MongoClient(uri) ;
```
#### 3)选择数据库
```java
MongoDatabase db = mongoClient.getDatabase("dbname") ;
```
#### 4)选择集合
```java
MongoCollection<Document> collection = db.getCollection("collname")
```
#### 5)执行CRUD
-- insertOne() , insertMany()
```java
public void insert(MongoCollection<Document> collection) {
Document doc1 = new Document() ;
doc1.append("name" , "rose")
.append("pwd" , "123456")
.append("mobile" , "13812344321")
.append("gender" , "F") ;
collection.insertOne(doc1);
System.out.println(doc1);
/////////////////
List<Document> list = new ArrayList<>() ;
list.add(
new Document().append("name" , "java").append("pwd" , "111").append("gender" , "M")
) ;
list.add(
new Document().append("name" , "python").append("pwd" , "222").append("gender" , "F")
) ;
collection.insertMany(list);
System.out.println("insert many!");
}
```
-- find()
Bson
Filters 构造条件 , 返回 Bson对象
FindIterable 集合调用find()的返回值
MongoCursor FindIterable对象调用iterable() 获取
Document
```java
public void select(MongoCollection<Document> collection) {
// 查询所有文档
// FindIterable findIterable = collection.find() ;
// 条件过滤
// Bson bson = Filters.eq("name" , "rose") ;
Bson bson = Filters.regex("name" , "o" , "i") ;
FindIterable findIterable = collection.find(bson) ;
MongoCursor cursor = findIterable.iterator() ;
while(cursor.hasNext()) {
Document doc = (Document) cursor.next();
System.out.println(doc);
System.out.println(doc.getObjectId("_id") + " , " + doc.getString("name"));
}
}
```
```java
public void login(MongoCollection<Document> collection) {
String name = "java" ;
String pwd = "111" ;
Bson bson1 = Filters.eq("name" , name) ;
Bson bson2 = Filters.eq("pwd" , pwd) ;
Bson filterBson = Filters.and(bson1 , bson2) ;
FindIterable findIterable = collection.find(filterBson) ;
MongoCursor cursor = findIterable.iterator() ;
Document doc = null ;
if(cursor.hasNext()) {
doc = (Document) cursor.next() ;
}
System.out.println(doc);
}
```
-- updateOne() , updateMany()
UpdateResult
```java
public void update(MongoCollection<Document> collection) {
Bson filterBson = Filters.regex("name" , "o" , "i") ;;
Document doc = new Document("$set" ,
new Document().append("age" , 28).append("pwd" , 321)
);
UpdateResult updateResult = collection.updateMany(filterBson , doc) ;//collection.updateOne(filterBson , doc) ;
System.out.println(updateResult.getModifiedCount());
}
```
-- deleteOne() , deleteMany()
DeleteResult
```java
public void delete(MongoCollection<Document> collection) {
Bson filterBson = Filters.eq("_id" , new ObjectId("6088b901e819882d57589802")) ;
DeleteResult deleteResult = collection.deleteOne(filterBson) ;
System.out.println(deleteResult);
}
```
### 2. 使用Python语言访问MongoDB
https://www.runoob.com/python3/python-mongodb.html
#### 1) 相关库 pymongo
```
$ python -m pip install pymongo
```
#### 2)连接服务器
```python
import pymongo
conn = pymongo.MongoClient("localhost|127.0.0.1|host" , 27017 )
```
#### 3)选择数据库
```python
db = conn['dbname']
# db = conn.dbname
```
#### 4)选择集合
```python
coll = db['collname']
# coll = db.collname
```
```python
import pymongo
from bson import ObjectId
def conn() :
conn = pymongo.MongoClient("127.0.0.1" , 27017)
db = conn['test']
coll = db['python']
# print(coll)
return coll
```
#### 5)执行CRUD
-- insert_one() , insert_many()
```python
def insert(coll) :
user = {
"name" : "zhangsan2" ,
"pwd" : "123" ,
"gender" : "F"
}
result = coll.insert_one(user)
print(result.inserted_id)
print(user)
#############
u1 = {
"name" : "lisi" ,
"pwd" : "111" ,
"gender" : "M"
}
u2 = {
"name": "wangwu",
"pwd": "222",
"gender": "F"
}
results = coll.insert_many([u1 , u2])
print(results.inserted_ids)
print(u1)
print(u2)
```
-- find_one() , find()
```python
def findAll(coll) :
results2 = coll.find()
data = []
for doc in results2:
data.append({
"_id" : str(doc['_id']) ,
"name" : doc['name'] ,
"pwd" : doc['pwd'] ,
"gender" : doc['gender'],
# "email" : doc['email']
})
print(data)
print("-------------")
```
```python
def find(coll) :
row = coll.find_one()
print(row)
print(type(row))
print("-----------------")
row = coll.find_one({"name" : "zhangsan" , "pwd" : "123321"}) ;
print(row)
print("-------------")
results = coll.find({
"name" : {"$regex" : "a" , "$options" : "i"} ,
"gender" : "F"
})
print(results)
print(type(results))
for doc in results :
print(doc)
print("-------------")
num = coll.count_documents({})
# num = coll.count_documents({"name": "zhangsan"})
print(num)
results2 = coll.find().sort("name" , -1)
for doc in results2 :
print(doc)
print("-------------")
results3 = coll.find().skip(1).limit(2)
for doc in results3:
print(doc)
```
-- update_one() , update_many()
```python
def update(coll) :
query = {"name" : {"$regex" : "zhangsan"}}
new_value = {"$set" : {
"email" : "zzss@qq.com" ,
"pwd" : "686868"
}}
# result = coll.update_one(query , new_value)
# print(result.modified_count)
results = coll.update_many(query , new_value)
print(results.modified_count)
```
-- delete_one() , delete_many()
```python
def delete(coll) :
# query = {"name" : {"$regex" : "zhangsan"}}
query = {"_id" : ObjectId('6088ff8d5ed9981a6f6f3d45')}
result = coll.delete_one(query)
# result = coll.delete_many(query)
print(result.deleted_count)
```

浙公网安备 33010602011771号