mongodb 文档数据库,存储的是文档(Bson->json的二进制化),非关系型。
特点:内部执行引擎为JS解释器, 把文档存储成bson结构,在查询时,转换为JS对象,并可以通过熟悉的js语法来操作.
mongo和传统型数据库相比,最大的不同:
传统型数据库: 结构化数据, 定好了表结构后,每一行的内容,必是符合表结构的,就是说–列的个数,类型都一样.
mongo文档型数据库: 表下的每篇文档,都可以有自己独特的结构(json对象都可以有自己独特的属性和值), 缺点:事物和join。
优点: 支持丰富的查询能力,性能。 serverless的宠儿
安装
docker安装mongodb
1docker run -d --name mymongo -v /Users/maozhongyu/docker_data/mongo_data:/data/db mongo:4
2docker run -d --name mongodb -p 27017:27017 -v /Users/maozhongyu/docker_data/mongodata2:/data/db mongo:4
mongodb管理界面
1 docker run --link mymongo:mongo -p 8081:8081 mongo-express:0.54.0
mongodb vscode客户端
vscode 安装 MongoDB for VS Code插件
mongo 操作mongodb
mongo shell 客户端
1docker exec -it mymongo mongo
库表操作语句
数据库隐式创建
1show dbs # 查看库
2use test # 选择库
3show tables/collections # 看有哪些表
4
5db.help()
6db.dropDatabase()
crud
对象主键
- 默认的文档主键
- 可以快速生成的12字节id
- 包含创建时间
collection 允许隐式创建
1> db.createCollection('user');
2{ "ok" : 1 }
3> show collections;
4account
5user
删除表
1
2> db.user.drop() # 删除表
3true
4> db.user.find();
插入一条
insertOne
1> db.user.insertOne({name:"lisi",age:22});
2WriteResult({ "nInserted" : 1 })
3> db.user.find();
4{ "_id" : ObjectId("65031ad0134a83215e86c1ee"), "name" : "lisi", "age" : 22 }
增加多个文档
1db.collectionName.insert(
2[
3{time:'friday',study:'mongodb'},
4{_id:9,gender:'male',name:'QQ'}
5]
6)
查找
1
2> db.user.insertOne({name:"crmao",age:23,hoppy:{"basketbal":20}});
3WriteResult({ "nInserted" : 1 })
4> db.user.find();
5{ "_id" : ObjectId("65031ad0134a83215e86c1ee"), "name" : "lisi", "age" : 22 }
6{ "_id" : ObjectId("65031b39134a83215e86c1ef"), "name" : "crmao", "age" : 23, "hoppy" : { "basketbal" : 20 } }
7
8
9
10> db.test.insertOne({name:"crmao"});
11WriteResult({ "nInserted" : 1 })
12> db.test.findOne({name:"cr"})
13null
删除
1
2# 删除全部sn=001的
3 db.user.remove({sn:"001"})
4# true 只删除一行
5 db.user.remove({sn:"001"},true)
更新
改 update操作
操作选项 —– 可选参数
语法: db.collection.update(查询表达式,新值,选项);
例:
db.news.update({name:‘QQ’},{name:‘MSN’});
是指选中news表中,name值为QQ的文档,并把其文档值改为{name:’MSN’},
结果: 文档中的其他列也不见了,改后只有_id和name列了.
即–新文档直接替换了旧文档,而不是修改
如果是想修改文档的某列,可以用$set关键字
db.collectionName.update(query,{$set:{name:’QQ’}})
修改时的赋值表达式
- $set 修改某列的值
- $unset 删除某个列
- $rename 重命名某个列
- $inc 增长某个列
- $setOnInsert 当upsert为true时,并且发生了insert操作时,可以补充的字段.
Option的作用:
{upsert:true/false,multi:true/false}
Upsert—是指没有匹配的行,则直接插入该行.(和mysql中的replace一样)
例:db.stu.update({name:‘wuyong’},{$set:{name:‘junshiwuyong’}},{upsert:true});
如果有name=’wuyong’的文档,将被修改
如果没有,将添加此新文档
例:
db.news.update({_id:99},{x:123,y:234},{upsert:true});
没有_id=99的文档被修改,因此直接插入该文档
multi: 是指修改多行(即使查询表达式命中多行,默认也只改1行,如果想改多行,可以用此选项)
例:
db.news.update({age:21},{$set:{age:22}},{multi:true});
则把news中所有age=21的文档,都修改
1> db.test.update({name:"crmao"},{name:"cr"});
2WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
3> db.test.find();
4{ "_id" : ObjectId("6528074dac548ac3cbe95e0b"), "name" : "cr" }
5
6
7
8> db.test.insert({name:"crmao",age:100,hobby:"baasket",sex:"nan"});
9db.test.update({name:"crmao"},{$set:{name:"crmmmm"},$unset:{hobby:"baasket"},$rename:{sex:"nan"}});
10
11
12
13> db.test.find()
14{ "_id" : ObjectId("6528074dac548ac3cbe95e0b"), "name" : "cr" }
15{ "_id" : ObjectId("65280c2aac548ac3cbe95e0c"), "name" : "crmmmm", "age" : 100, "nan" : "nan" }
16
17
18> db.test.update({name:"crmmmm"},{$set:{name:"crmao"},$unset:{hobby:"baasket"},$rename:{sex:"nan"},$inc:{age:100}});
19WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
20> db.test.find()
21{ "_id" : ObjectId("6528074dac548ac3cbe95e0b"), "name" : "cr" }
22{ "_id" : ObjectId("65280c2aac548ac3cbe95e0c"), "name" : "crmao", "age" : 200, "nan" : "nan" }
更新一条updateOne()
1db.test.updateOne({open_id:102},{$set:{open_id:110}})
2{
3 "acknowledged": true,
4 "insertedId": null,
5 "matchedCount": 1,
6 "modifiedCount": 1,
7 "upsertedCount": 0
8}
findAndModify
可以进行更新,插入, 并返回 老的或者旧数据, 很全能的一个函数
1
2db.test.findAndModify({
3 query: { open_id:111},
4 update:{$set:{open_id:112}},
5 upset:true
6})
7找到返回老的记录, 没找到返回null, 并进行插入操作
8{
9 "_id": {
10 "$oid": "654ef081130618ef248c666b"
11},
12 "open_id": 111
13}
14
15
16db.test.findAndModify({
17 query: { open_id:111},
18 update:{$set:{open_id:112}},
19 upset:true,
20 new:true //默认false , 没有更新成功,则进行插入, 一直返回新的记录
21})
深入查询表达式
查: find, findOne
语法: db.collection.find(查询表达式,查询的列);
Db.collections.find(表达式,{列1:1,列2:1});
-
例1:db.stu.find()
查询所有文档 所有内容
-
例2: db.stu.find({},{gendre:1})
查询所有文档,的gender属性 (_id属性默认总是查出来)
-
例3: db.stu.find({},{gender:1, _id:0})
查询所有文档的gender属性,且不查询_id属性
-
例4: db.stu.find({gender:’male’},{name:1,_id:0});
查询所有gender属性值为male的文档中的name属性
1: 最简单的查询表达式
{filed:value} ,是指查询field列的值为value的文档
2: $ne — != 查询表达式
{field:{$nq:value}}
作用–查filed列的值 不等于 value 的文档
3: $nin –> not in
4: $all
语法: {field:{$all:[v1,v2..]}}
是指取出 field列是一个数组,且至少包含 v1,v2值
5: $exists
语法: {field:{$exists:1}}
作用: 查询出含有field字段的文档
6: $nor,
{$nor,[条件1,条件2]}
是指 所有条件都不满足的文档为真返回
7:用正则表达式查询 以”诺基亚”开头的商品
例:db.goods.find({goods_name:/诺基亚.*/},{goods_name:1});
8: 用$where表达式来查询
例: db.goods.find({$where:’this.cat_id != 3 && this.cat_id != 11’});
注意: 用$where查询时, mongodb是把bson结构的二进制数据转换为json结构的对象, 然后比较对象的属性是否满足表达式. 速度较慢
1查出满足以下条件的商品
21.1:主键为32的商品
3 db.goods.find({goods_id:32});
4
51.2:不属第3栏目的所有商品($ne)
6 db.goods.find({cat_id:{$ne:3}},{goods_id:1,cat_id:1,goods_name:1});
7
81.3:本店价格高于3000元的商品{$gt}
9 db.goods.find({shop_price:{$gt:3000}},{goods_name:1,shop_price:1});
10
111.4:本店价格低于或等于100元的商品($lte)
12 db.goods.find({shop_price:{$lte:100}},{goods_name:1,shop_price:1});
13
141.5:取出第4栏目或第11栏目的商品($in)
15 db.goods.find({cat_id:{$in:[4,11]}},{goods_name:1,shop_price:1});
16
17
181.6:取出100<=价格<=500的商品($and)
19db.goods.find({$and:[{price:{$gt:100},{$price:{$lt:500}}}]);
20
21
221.8:取出价格大于100且小于300,或者大于4000且小于5000的商品()
23db.goods.find({$or:[{$and:[{shop_price:{$gt:100}},{shop_price:{$lt:300}}]},{$and:[{shop_price:{$gt:4000}},{shop_price:{$lt:5000}}]}]},{goods_name:1,shop_price:1});
24
25
26
271.9:取出goods_id%5 == 1, 即,1,6,11,..这样的商品
28db.goods.find({goods_id:{$mod:[5,1]}});
29
30
311.10:取出有age属性的文档
32db.stu.find({age:{$exists:1}});
33含有age属性的文档将会被查出
游标
游标是什么?
通俗的说,游标不是查询结果,而是查询的返回资源,或者接口.
通过这个接口,你可以逐条读取.
就像php中的fopen打开文件,得到一个资源一样, 通过资源,可以一行一行的读文件.
批量插入数据
1for (var i=0;i<10000;i++){
2 db.collection.insertOne({"_id":i+1,"name":"hello","count":"aaa"+i })
3}
4db.collection.find().count()
声明游标:
1var cursor = db.collectioName.find(query,projection);
2Cursor.hasNext() ,判断游标是否已经取到尽头
3Cursor.Next() , 取出游标的下1个单元
循环游标
1var mycursor = db.bar.find({_id:{$lte:5}})
2while(mycursor.hasNext()) {
3 printjson(mycursor.next());
4}
5
6for(var doc=true;cursor.hasNext();) { printjson(cursor.next());}
7
8
9for(var cursor=db.goods.find(), doc=true;cursor.hasNext();) { printjson(cursor.next());}
游标还有一个迭代函数,允许我们自定义回调函数来逐个处理每个单元. cursor.forEach(回调函数); 例:
1var gettitle = function(obj) {print(obj.goods_name)}
2var cursor = db.goods.find();
3cursor.forEach(gettitle);
分页中的应用
比如查到10000行,跳过100页,取10行.
一般地,我们假设每页N行, 当前是page页
就需要跳过前 (page-1)*N 行, 再取N行, 在mysql中, limit offset,N来实现
在mongo中,用skip(), limit()函数来实现的
如 var mycursor = db.bar.find().skip(9995);
则是查询结果中,跳过前9995行
查询第901页,每页10条
则是 var mytcursor = db.bar.find().skip(9000).limit(10);
通过cursor一次性得到所有数据, 并返回数组. 例:
1var cursor = db.goods.find();
2printjson(cursor.toArray()); //看到所有行
3printjson(cursor.toArray()[2]); //看到第2行
注意: 不要随意使用toArray()
原因: 会把所有的行立即以对象形式组织在内存里.
可以在取出少数几行时,用此功能.
索引
1:索引提高查询速度,降低写入速度,权衡常用的查询字段,不必在太多列上建索引
2.在mongodb中,索引可以按字段升序/降序来创建,便于排序
3.默认是用btree来组织索引文件,2.4版本以后,也允许建立hash索引.
查看查询计划
1db.find(query).explain();
“cursor” : “BasicCursor”, —-说明没有索引发挥作用
“nscannedObjects” : 1000 —理论上要扫描多少行
cursor" : “BtreeCursor sn_1”, 用到的btree索引
查看当前索引
1db.collection.getIndexes();
创建普通的单列索引
1
2db.collection.ensureIndex({field:1/-1}); // 1是升续 2是降续
3db.collection.createIndex({field:1/-1}); // 1是升续 2是降续
删除索引
1db.collection.dropIndex({filed:1/-1}); // 删除单个索引
2
3db.collection.dropIndexes(); // 删除所有索引
创建多列索引
1db.collection.ensureIndex({field1:1/-1, field2:1/-1});
创建子文档索引
1db.collection.ensureIndex({filed.subfield:1/-1});
创建唯一索引
1db.collection.ensureIndex({filed.subfield:1/-1}, {unique:true});
稀疏索引
稀疏索引的特点——如果针对field做索引,针对不含field列的文档,将不建立索引. 与之相对,普通索引,会把该文档的field列的值认为NULL,并建索引.
适宜于: 小部分文档含有某列时.
1db.collection.ensureIndex({field:1/-1},{sparse:true});
2db.account.createIndex(
3 {open_id:1},
4 {unique:true}
5)
1db.tea.find();
2{ "_id" : ObjectId("5275f99b87437c610023597b"), "email" : "a@163.com" }
3{ "_id" : ObjectId("5275f99e87437c610023597c"), "email" : "b@163.com" }
4{ "_id" : ObjectId("5275f9e887437c610023597e"), "email" : "c@163.com" }
5{ "_id" : ObjectId("5275fa3887437c6100235980") }
如上内容,最后一行没有email列, 如果分别加普通索引,和稀疏索引, 对于最后一行的email分别当成null 和 忽略最后一行来处理. 根据{email:null}来查询,前者能查到,而稀疏索引查不到最后一行.
哈希索引
哈希索引速度比普通索引快,但是,无能对范围查询进行优化.
适宜于—随机性强的散列
1db.collection.ensureIndex({file:"hashed"});
重建索引
一个表经过很多次修改后,导致表的文件产生空洞,索引文件也如此. 可以通过索引的重建,减少索引文件碎片,并提高索引的效率. 类似mysql中的optimize table
1db.collection.reIndex()
用户管理
auth启动mongo
1docker run --name mongodb3 -p 27019:27017 -d mongo:4 --auth
添加用户
先要添加管理员账号
1 use admin
2 db.createUser({user:"admin123",pwd:"123456",roles:[{role:"root",db:"admin"}]})
3 use admin
4 db.auth("admin123","123456")
1 use `database_name`
2 db.createUser({user:"root",pwd:"pwdxxx",roles:[{role:"readWrite",db:"database_name"}]})
下次连上来
1use `database_name`
2db.auth("root","pwdxxx")
3// 然后接着 crud 操作