1、MongoDB是什么
- 存储文档的非关系型数据库 (非关系--表格不固定,新增加文档结构,直接存入也不会报错)
- 其它非关系型数据库:redis、cassandra、objectivity、neo4j
2、运行mongodb 基本设置
- 下载运行
# 使用docker运行MongoDB
# 下载镜像
dcker pull mongo:4
# 查看镜像
docker images
# 启动MongoDB
# 容器名称:mymongo,挂着数据目录:-v,后台启动:-d
docker run --name mymongo -v /mymongo/data:/data/db -d mongo:4
# 查看启动日志
docker logs mymongo
- 安装mongodb数据库管理器
# 下载mongo-express镜像
docker pull mongo-express
# 运行mongo-express
docker run --link mymongo:mongo -p 8081:8081 mongo-express
- localhost:8081 进入管理界面
- 自动创建三个系统数据库 不建议修改他们
3、基本命令
3.1进入docker 中mongo命令行界面
- docker exec -it mymongo mongo
- mongo shell 是javascript客户端界面,可以使用js命令。
- exit 退出界面
- 创建表
3.2主键 _id
- 唯一性、支持所有数据类型(数组除外)、复合主键
- 对象主键 ObjectId 官方的一种创建方式,不设置主键自动创建,快速生成12字节Id,包含创建时间
- 提取主键里的时间
- 复合主键
-
db.accounts.insert( { _id: { accountNo: "007",type: "cat"}, name: "gray", balance: 100 } )
-
3.3创建库
# 使用数据库(没有回创建数据库)
use test
# 查看数据库中的表
show collections
3.4创建表 insertOne
格式: writeConcern 是设置安全级别。用来判断一次写入操作是否成功,安全级别越高,丢失数据风险越低,写入延迟风险也可能越高。
# 这里自动创建了 accounts 表
# 如果这里不设置主键,会默认生成主键
db.accounts.insertOne(
{
_id: "account1",
name: "alice",
balance: 100
}
)
# 返回消息 true 表示安全写级别被启用
# 由于我们在创建命令中没有设置writeConcern 这里显示的是默认开启的安全写级别启用状态
# insertedId 显示了写入的文档id
{"acknowledged": true, "insertedId" : "account1"}
3.5创建多个文档
ordered 是否按照顺序写入。true如果 前面有执行错误,则后面的文档会添加失败。false 则只会是报错的文档添加失败
db.accounts.insertMany(
{
name: "charlie",
balance: 100
},
{
name: "david",
balance: 50
}
)
insert & save
db.accounts.insert(
{
name: "gray",
balance: 100
},
{
name: "cat",
balance: 50
}
)
# 返回消息
WriteResult({"ninserted":1})
# 这个也是创建文档,内部是调用的insert命令
db.accounts.save(
{
name: "gray2",
balance: 100
},
{
name: "cat2",
balance: 50
}
)
insert支持db.collection.explain()命令,另外insertMany和insertOne是不支持的。
3.6读取表
# 比较操作符
$eq 相等
$ne 不等
$gt 大于
$gte 大于等于
$lt 小于
$lte 小于等于
$in 在这个范围内查找
$nin 查找不在这个范围内的文档
$not 否定判断
$and 并列条件判断
$or 或者
$nor 既不要,也不要
$exists 匹配包含查询字段的文档
$type 匹配字段类型符合查询值的文档
$all 匹配包含数组字段的文档
$elemMatch 查询包含数组字段其中一个的文档
$regex 匹配正则表达式
# 读取alice的文档,且账户余额100
db.accounts.find({name: "alice",balance: 100})
# 和上面效果一样
db.accounts.find({name: {$eq:"alice"},balance: 100})
# 读取复合主键中 type = save的文档
db.accounts.find({"_id.type": "save"})
# 读取不属于alice 的文档
db.accounts.find({name:{ $ne: "alice"}})
# 复合查询 这样会把主键里没有type字段的也全部查出来
db.accounts.find({"_id.type":{ $ne: "save"}})
# 这样就只会在主键包含type的地方查询
db.accounts.find({"_id.type":{ $ne: "save",$exists: true}})
# 读取主键是字符串的文档
db.accounts.find({_id:{ $type: "string"}})
# 读取主键是 对象主键或者是复合主键的文档
db.accounts.find({_id:{ $type: ["objectedId","object"]}})
# 查询用户名是类型 null的文档
db.accounts.find({name:{ $type: "null"}})
# 查询用户名是alice或者charlie的文档
db.accounts.find({name:{ $in: ["alice","charlie"]}})
# 查询余额不小于500的账户文档
db.accounts.find({balance:{ $not: {$lt: 500}}})
# 查询余额大于100并且用户名排在fred之后的文档
db.accounts.find({
$and:[
{balance: {$gt:100}},
{name:{$gt:"fred"}}
]})
# 筛选条件用在不同字段上,可以省略 $and
db.accounts.find({
balance: {$gt:100},
name:{$gt:"fred"}
})
# 筛选条件在同一个字段上,可以简化命令
db.accounts.find({ balance: {$gt:100,$lt: 500} })
# 查询用户名是alice或者fred的文档
db.accounts.find({
$or:[
{name: {$eq:"alice"}},
{name:{$eq:"fred"}}
]})
# 读取用户名不属于alice也不属于charlie,且余额小于100的文档
db.accounts.find({
$nor: [
{name: "alice"},
{name: "charlie"},
{balance: {$lt: 100}}
]
})
# 查询地址位于中国北京的文档
db.accounts.find({address: {$all: ["China","BeiJing"]}})
# 读取电话号码在10000到20000的文档
db.accounts.find({photo: {$elemMatch: {$gt: "10000",$lt: "20000"}}})
# all和elemMatch 一起使用
# 查找电话号码在15000到20000的文档
db.accounts.find({
photo: {$all: [
$elemMatch: {$gt: "10000",$lt: "20000"},
$elemMatch: {$gt: "15000",$lt: "50000"},
]}
})
# 读取用户名以c或者j开头的文档
db.accounts.find({name: {$in: [/^c/,/^j/]}})
# 读取用户名包含LIE(不区分大小写)的文档,($options:'i' 不区分大小写)
db.accounts.find({name: {$regex: /LIE/,$options:'i'}})
3.7读取表(游标)
# 获取所有表
# 遍历完myCursor或者10分钟后 myCursor就会失效!!
var myCursor = db.accounts.find();
# 获取第一张表
myCursor[0]
# noCursorTimeout 设置myCursor 一直有效。用完后的主动关闭
var myCursor = db.accounts.find().noCursorTimeout();
# 关闭游标
myCursor.close()
# 游标函数
cursor.hasNext()
cursor.next()
cursor.forEach()
cursor.limit()
cursor.skip()
cursor.count()
cursor.sort() # 1表示由小及大排序,-1表示逆向排序
# 迭代
var myCursor = db.accounts.find({name:"grorge"}).noCursorTimeOut;
while(myCursor.hasNext()){
printjson(myCursor.next());
}
#循环遍历
myCursor.forEach(printjson)
myCursor.close()
# 查询用户george的第3到6篇文档(排除前2篇后选择前面4篇)
db.accounts.find({name:"george"}).skip(2).limit(4)
# 会显示george的全部文档
db.accounts.find({name:"george"}).limit(0)
# limit、skip与count一起使用时,需要设置count(true)
# 这个会显示george的全部文档数量
db.accounts.find({name:"george"}).limit(1).count()
# 这个才会显示数量1
db.accounts.find({name:"george"}).limit(1).count(true)
当数据库分布式机构较为复杂时,元数据中的文档数量可能不准确,在这种情况下,应当避免使用不提供筛选条件的cursor.count()函数。而使用聚合管道来计算文档数量。
# 用户余额从大到小,用户名按照字母排序的方式显示
db.zccounts.find().sort({balance: -1,name: 1})
# skip会自动在 limit之前执行
# sort 会自动在limit和skip之前执行
# 显示排除前三条后的5条数据
db.accounts.find().limit(5).skip(3)
3.7读取表(投射)
- 不是同投射,返回反正文档数据
- 1表示返回字段,0表示不返回字段
- 除了主键,我们不能再投影文档中混合使用包含和不包含这两种投影操作
# 查询文档,直返回用户名,默认主键会自动返回
db.accounts.find({},{name:1})
# 想不要主键
db.accounts.find({},{name: 1,_id: 0})
# 除了name和balance 其他字段都返回
db.accounts.find({},{name: 0,balance: 0})
# 显示name和contact 参数。contact只显示数组中的第一个
db.accounts.find({},{name: 1,contact: {$slide: 1}})
# 显示name和contact 参数。contact只显示数组中的前2个
db.accounts.find({},{name: 1,contact: {$slide: 2}})
# 显示name和contact 参数。contact只显示数组中的最后2个
db.accounts.find({},{name: 1,contact: {$slide: -2}})
# 显示name和contact 参数。contact只显示数组中,排除第一个后的前2个,skip 1, limit 2
db.accounts.find({},{name: 1,contact: {$slide: [1,2]}})
# elemMatch和$操作符可以返回数组字段中满足筛选条件的第一个元素
db.accounts.find({},
{ _id:0 ,name:1,
contact: {$elemMatch: {$gt: "Alabama"}}
})
# 同上效果,一行是条件,第二行是显示内容
db.accounts.find(
{contact: {$gt: "Alabama"}},
{ _id:0 ,name:1,"contact.$": 1}
)
3.8更新表 update 更新整篇文档
- 一共是可以传三个数据的
- 文档主键_id 是不可修改的,如果非要在修改信息中包含主键,一定要和被修改的主键一致。
# 把找到的名字是alice的第一篇文档,更新成下面数据
db.account.update({name: "alice"},balance : 123)
3.9更新表 update 更新特定字段
# $set
# 找到jack的第一个文档,更新信息
db.accounts.update(
{name: "jack"},
{$set:
{
balance: 3000,
info: {
dateOpened: "hello",
branch: "branchl"}
}
}
)
# 更新文档内数组信息
db.accounts.update(
{name: "jack"},
{$set:
{
"contact.0": "6666666"
}
}
)
# $unset删除字段 , "" 内设置任何值都没影响,都是清空
db.accounts.update(
{name: "jack"},
{$unset:
{
balance: "",
"info.branch": "",
}
}
)
# 删除集合内元素
db.accounts.update(
{name: "jack"},
{$unset:
{
"contact.0": ""
}
}
)
$rename命令中的旧字段和新字段都不可用指向数组 元素。
# $rename 重命名,如果集合内字段不存在,那么文档内容不会改变。
# $rename命令会先$unset旧字段然后在$set新字段
db.accounts.update(
{name: "jack"},
{$rename:
{
"info.branch": "branch",
"balance": "info.balance"
}
}
)
# 如果字段不存在,$inc会创建字段,并将字段加减设置中的值。$mul会创建字段,然后设置成0
# 表david 中的balance减少0.5
db.accounts.update(
{name: "david"},
{$inc:
{
"balance": -0.5
}
}
)
# 表david 中的balance乘以0.5
db.accounts.update(
{name: "david"},
{$mul:
{
"balance": 0.5
}
}
)
# 如果被更新的值不存在,会创建值,并把设置的值赋值进去。
# 设置参数与原始参数比较,选择较大的留下(即便类型不是数字,会用编码大小判断)
# 大小顺序判断 Null < Number < Symbol,String < Object < Array < BinData < ObjectId < Boolean < Date < Timestamp < Regular Expression
db.accounts.update(
{name: "david"},
{$max:
{
"balance": 500
}
}
)
# 设置参数与原始参数比较,选择较小的留下
db.accounts.update(
{name: "david"},
{$min:
{
"balance": 50
}
}
)
3.10更新表 文档更新操作符
$set 更新或新增字段
$unset 删除字段
$rename 重命名
$inc 加减字段
$mul 相乘字段
$min 设置参数与原始参数比较,选择较小的留下
$max 设置参数与原始参数比较,选择较大的留下
3.11更新表 数组更新操作符
$addToSet 向数组元素中添加字段
$push 向数组中添加元素(没有会新添加) 功能更强大
$pop 删除数组元素
$pull 删除数组元素(匹配一部分就行)
$pullAll 完全匹配删除(字段值和顺序都需要匹配)
$[] 指数组中的所有元素
# 如果插入值已经在数组字段中,则不会重复添加。(如果字段顺序不一样,不算重复)
# 向karen的文档中添加联系方式
db.accounts.update(
{name: "karen"},
{$addToSet: {contact: "China"}}
)
# 一次添加多个元素each
db.accounts.update(
{name: "karen"},
{$addToSet: {contact: {$each: ["contactl","contactl2"]}}}
)
# $pop只能用在数组字段上
# 删除contact中的最后一个元素
db.accounts.update(
{name: "karen"},
{$pop: {contact: 1}}
)
# contact.5 本身也是个数组,设置contact第6个数组中第一个参数为null
db.accounts.update(
{name: "karen"},
{$pop: {contact.5: -1}}
)
# 从update中删除包含"hi"字母的元素
db.accounts.update(
{name: "karen"},
{$pull: {contact: $regex: /hi/}}
)
# 也可以使用$elemMatch
# karen中删除电话号码222222
db.accounts.update(
{name: "karen"},
{$pull: {contact: {$elemMatch: {$eq: "222222"}}}}
)
# 完全匹配删除,数组得写完
db.accounts.update(
{name: "karen"},
{$pullAll: {contact: [["222222","333333"]]}}
)
复制表
ad.accounts.find(
{name: "karen"}, #找到karen这张表
{_id: 0} # 显示除主键意外其他字段
).forEach(function(doc){
var newDoc = doc; #获取查询到的表
newDoc.name = "lawrence"; #修改名称、
db.accounts.insert(newDoc); # 存入新的表
}
)
# 和each搭配使用,一次添加多个
db.accounts.update(
{name: "lawrence"},
{$push: {
newArray: {
$each: [2,3,4]
}
}}
)
# 只想截取集合中元素
db.accounts.update(
{name: "lawrence"},
{$push: {
newArray: {
$each: [],
$slice:5
}
}}
)
# 执行顺序固定 $position > $sort > $slice
# 元数据第二个位置插入新字段,排序后取5个保留
db.accounts.update(
{name: "lawrence"},
{$push: {
newArray: {
$each: ["push1","push2","push3"],
$slice:5,
$position: 2,
$sort: -1
}
}}
)
# 使用.$ 代替上面使用的查询
# newArray.$ 值 newArray = "pos2" 的值改成"updated"
db.accounts.update(
{name: "lawrence",
newArray: "pos2"
},
{$set: {
"newArray.$": "updated"
}
)
# contact 中第一个位置的数组,里面所有元素改成888888
db.accounts.update(
{name: "lawrence"},
{$set: {
"contact.0.$[]": "888888"
}
)
3.12更新表 update 更新多篇文档
Mongodb只能保证单个文档操作原子性,不能保证多个文档一起修改原子性。(使用事务功能才行)
# multi: true 同上更新多篇文档
# 设置所有文档currency = USD
db.accounts.update(
{},
{$set: {
currency: "USD"
}
},
{multi: true}
)
# upsert:<boolean> 更新或创建文档
# 文档有会赋值,没有会创建新的(会包含name和balance两字段和值)
db.accounts.update(
{name: "maggie"},
{$set: {balance: 800}},
{upsert: true}
)
# 但是字段是不确定的,则这个字段不会创建。(只会包含balance字段和值)
db.accounts.update(
{balance: {$gt: 20000}},
{$set: {balance: 800}},
{upsert: true}
)
3.13更新表 save 更新文档
# 底层调用的还是update()并且upsert: true
db.accounts.save(
{balance: 200,name: "animal"}
)
4.14删除表 remove()
# 默认情况,remove会删除所有符合筛选条件的文档
# 删除余额为50的账户文档
db.accounts.remove({balance: 50})
# 如果只想删除找到的第一篇文档
db.accounts.remove({balance: 50},{justOne: true})
4.15删除集合 drop()
# 如果数据非常多,需要清空集合,就直接删除集合。重新创建新集合。
# 删除整个accounts集合
db.accounts.drop()