文章目录
1、安装
官网:MongoDB官网
中文社区:中文社区
官方中文文档:官方中文文档
中文文档:中文文档
启动命令
bin目录下启动
mongod --dbpath=..\data\db #启动服务器
mongo #启动服务器
2、数据库简介
-
数据库 (Database)
- 数据库是按照数据结构来组织、存储和管理数据的仓库。
- 我们的程序都是在内存中运行的,一旦程序运行结束或者计算机断电,程序运行中的数据都会丢失。
- 所以我们就需要将一些程序运行的数据持久化到硬盘之中,以确保数据的安全性。而数据库就是数据持久化的最佳选择。
- 说白了,数据库就是存储数据的仓库。
-
数据库分类(两种)
- 关系型数据库(RDBMS)
- MySQL、Oracle、DB2、SQL Server ......
- 关系数据库中全都是表-
- 非关系型数据库(No SQL Not Only SQL)
- MongoDB、Redis ......
- 键值对数据库
- 文档数据库MongoDB
- 关系型数据库(RDBMS)
3、MongoDB简介
- MongoDB是一个NoSQL的数据库,是一款文档型数据库。数据库可以使我们完成对数据的持久化的操作
- MongoDB数据库中存储的数据的基本单位就是文档,MongoDB中存储的就是文档,所谓文档其实就是一个“JSON”。MongoDB中的“JSON”我们称为BSON,比普通的JSON的功能要更加的强大
- MongoDB数据库使用的是JavaScript进行操作的,在MongoDB含有一个对ES标准实现的引擎,在MongoDB中所有ES中的语法中都可以使用。
- MongoDB是为快速开发互联网Web应用而设计的数据库系统。
- MongoDB的设计目标是极简、灵活、作为Web应用栈的一部分。
- MongoDB的数据模型是面向文档的,所谓文档是一种类似于JSON的结构,简单理解MongoDB这个数据库中存的是各种各样的JSON。( BSON:二进制JSON)
具体的应用场景:
- 社交场景, 使用 MongoDB 存储存储用户信息, 以及用户发表的朋友圈信息, 通过地理位置索引实现附近的人, 地点等功能.
- 游戏场景, 使用 MongoDB 存储游戏用户信息, 用户的装备, 积分等直接以内嵌文档的形式存储, 方便查询, 高效率存储和访问.
- 物流场景, 使用 MongoDB 存储订单信息, 订单状态在运送过程中会不断更新, 以 MongoDB 内嵌数组的形式来存储, 一次查询就能将订单所有的变更读取出来.
- 物联网场景, 使用 MongoDB 存储所有接入的智能设备信息, 以及设备汇报的日志信息, 并对这些信息进行多维度的分析.
- 视频直播, 使用 MongoDB 存储用户信息, 点赞互动信息等.
4、数据库层级关系
数据库( database)
数据库是一个仓库,在仓库中可以存放集合。
集合( collection )(数组)
集合类似于数组,在集合中可以存放文档。类似与SQL中的数据表,本质上是一个数组,里面包含看多个文档对象,[{},{},{}]
文档( document )
文档数据库中的最小单位,我们存储和操作的内容都是文档。类似与SQL中的记录,一个文档对象{}就是一条记录。
一个数据库由多个集合构成,一个集合包含多个文档对象。
5、服务器和客户端
数据库的服务器
- 服务器用来保存数据
- mongod 用来启动服务器
- 数据库的客户端
- 客户端用来操作服务器,对数据进行增删改查的操作
- mongo 用来启动客户端
6、MongoDB的基本操作
在MongoDB中,数据库和集合都不需要我们手动创建,当我们创建文档时,如果文档所在的集合或数据库不存在,她会自动创建数据库和集合!
基本指令
#数据库
show dbs #显示当前的所有数据库
show databases #显示当前的所有数据库
db #db表示的是当前所处的数据库
use 数据库名 #创建数据库,进入到指定的数据库中(可以不存在)
db.dropDatabase() #删除当前数据库
#集合
show collections #显示数据库中所有的集合
db.createCollection("集合_name") #创建集合
db.<collection_name>.drop() #删除集合,sql理解删除表
案例
show dbs //查询数据库
use my_tests //创建数据库并进入,但注意如果没有创建集合的话是不会显示创建的数据库
show collections //查看集合
db.createCollection("神秘复苏") //创建集合“神秘复苏”
db.神秘复苏.drop() //删除集合“神秘复苏”
CRUE(增删改查)操作
1)添加数据 insert
db.collection.insert() #向数据库中插入文档
insert() #可以向集合中插入一个或多个文档
db.collection.insertOne() #向集合中插入一个文档
db.collection.insertMany() #向集合中插入多个文档
案例
#例子:向test数据库中的,stus集合中插入一个新的学生对象
db.stus.insert({name:"孙悟空",age:18,gender:"男"})
#多条文档插入的时候要加[]
db.神秘复苏.insert([
{_id:1,name:"杨间",sex:"男",age:16,sal:2000,deptno:10},
{_id:2,name:"李军",sex:"男",age:17,sal:3000,deptno:20},
{_id:3,name:"叶真",sex:"男",age:18,sal:4000,deptno:30},
{_id:4,name:"柳三",sex:"男",age:19,sal:1000,deptno:40},
{_id:5,name:"红新娘",sex:"女",age:20,sal:5000,deptno:10},
{_id:6,name:"杨戬",sex:"男",age:21,sal:6000,deptno:20}
]);
#当我们向集合中插入文档时,如果没有给文档指定_id属性,则数据库会自动为文档添加_id,该属性用来作为文档的唯一标识。
#_id我们可以自己指定,如果我们指定了数据库就不会添加了,如果自己指定_id,它必须确保它的唯一性
db.stus.insert({_id:"hello",name:"杨间",age:27,gender:"男"});
#添加两万条数据
for(var i=0;i<20000;i++){
db.users.insert({username:'liu'+i}) #需要执行20000次数据库的添加操作
}
db.users.find().count()//20000
#优化:
var arr=[];
for(var i=0;i<20000;i++){
arr.push({username:'liu'+i})
}
db.user.insert(arr) #只需执行1次数据库的添加操作,可以节约很多时间
2)查询数据 find()
- find()可以接受一个对象作为条件参数
- {}表示查询集合中所有的文档
- {字段名:值} 查询属性是指定值的文档
//查询数据库中的文档
db.<collection>.find() #查询当前集合中的所有的文档
#可以根据指定条件从集合中查询所有符合条件的文档
#结果返回的是一个数组
db.collection.findOne()
#查询第一个符合条件的文档/数据
#返回的是第一行对象/数据
db.collection.find().count()
#查询符合条件的文档的数量
详解:
# 1.mongodb支持直接通过内嵌文档的属性值进行查询
# 什么是内嵌文档:hobby就属于内嵌文档
{
name:'liu',
hobby:{
movies:['movie1','movie2'],
cities:['zhuhai','chengdu']
}
}
db.users.find({hobby.movies:'movie1'}) //错误
db.users.find({"hobby.movies":'movie1'})//此时查询的属性名必须加上引号
#2.查询操作符的使用
#比较操作符
$gt 大于
$gte 大于等于
$lt 小于
$lte 小于等于
$ne 不等于
$eq 等于的另一种写法
db.users.find({num:{$gt:200}}) #大于200
db.users.find({num:{$gt:200,$lt:300}}) #大于200小于300
$or 或者
db.users.find({$or:[{num:{$gt:300}},{num:{$lt:200}}]}) #大于300或小于200
案例
db.神秘复苏.findOne() #查询文档中第一条数据
db.神秘复苏.findOne().name #查询文档中第一条数据的名字
db.神秘复苏.find({name:"杨间"}) #查询杨间的数据
db.神秘复苏.find({sal:{$gt:2000}}) #查询sal大于2000的人
db.神秘复苏.find({sal:{$lt:3000}}) #查询sal小于3000的人
db.神秘复苏.find({sal:{$gt:2000,$lt:5000}}) #查询sal大于2000小于5000的人
db.神秘复苏.find({$or:[{sal:{$gt:3000}},{sal:{$lt:5000}}]}) #查询sal大于3000或小于5000的
db.collectionName.findOne() 返回的是查询到的对象数组中的第一个数据
db.students.find({_id:222}).name //错误
db.students.findOne({_id:222}).name //正确
3)修改数据 update()
- db.collection.update(查询条件,新对象)
- update()默认情况下会使用新对象会替换旧的对象,update默认只会修改第一个。
//修改数据库中的文档
db.collection.update() #可以修改、替换集合中的一个或多个文档
db.collection.updateOne() #修改集合中的一个文档
db.collection.updateMany() #修改集合中的多个文档
db.collection.replaceOne() #替换集合中的一个文档
- 如果需要修改指定的属性,而不是替换,需要使用“修改操作符”来完成修改
- $set:可以用来修改文档中的指定属性
- $unset:可以用来删除文档的指定属性
- $inc:递增
- $rename:重命名列
- $push:用于向数组中添加一个新的元素
- $addToSet:向数组中添加一个新元素(类似于向set集合中添加,如果数组中已经存在了该元素,则添加失败,因为不可重复)
讲解
# 1.替换整个文档
# db.collectionName.update(condiction,newDocument)
> db.students.update({_id:'222'},{name:'kang'})
# 2.修改对应的属性,需要用到修改操作符,比如$set,$unset,$push,$addToSet
db.collectionName.update(
# 查询条件
{_id:222},
{
#修改对应的属性
$set:{
name:'kang2',
age:21
}
#删除对应的属性
$unset:{
gender:1 //这里的1可以随便改为其他的值,无影响
}
}
)
# 3.update默认与updateOne()等效,即对于匹配到的文档只更改其中的第一个
# updateMany()可以用来更改匹配到的所有文档
db.students.updateMany(
{name:'liu'},
{
$set:{
age:21,
gender:222
}
}
)
# 4.向数组中添加数据
db.users.update({username:'liu'},{$push:{"hobby.movies":'movie4'}})
#如果数据已经存在,则不会添加
db.users.update({username:'liu'},{$addToSet:{"hobby.movies":'movie4'}})
# 5.自增自减操作符$inc
{$inc:{num:100}} #让num自增100
{$inc:{num:-100}} #让num自减100
db.emp.updateMany({sal:{$lt:1000}},{$inc:{sal:400}}) #给工资低于1000的员工增加400的工资
案例1
#这种方式有风险,因为这是替换,所有的数据清空只留下age:33
db.神秘复苏.update({name:"杨间"},{age:33})
#修改集合中的数据,替换
db.神秘复苏.update({_id:1},{$set:{name:"杨间",sex:"男",age:16,sal:2000,deptno:10}})
#给id:1的文档添加一个新的字段名
db.神秘复苏.update({_id:1},{$set:{address:"大昌市"}})
#删除id:1的文档的address的字段名,因为是删除,字段值随便
db.神秘复苏.update({_id:1},{$unset:{address:1}})
#把sal大于2000的文档address改为并州(多个文档)
db.神秘复苏.updateMany({sal:{$gt:2000}},{$set:{address:"并州"}})
#内嵌文档修改
db.user.update({username:"tangseng"},{$push:{"hobby.movies":"Interstellar"}})
db.user.update({username:"tangseng"},{$addToSet:{"hobby.movies":"Interstellar"}}
#给id:1加200工资
db.神秘复苏.update({_id:1},{$inc:{sal:200}})
#给id:5减1000工资
db.神秘复苏.update({_id:5},{$inc:{sal:-1100}})
案例2
准备:插入一个数据: db.c4.insert({username:"熊子阳",age:18,who:"男",other:"没钱"})
修改数据,将 熊子阳 改为 Aoi ,age 改为999 ,who 改为 sex ,other 删除
db.c4.update({username:"熊子阳"},
{$set:{username:"Aoi"}},
{$inc:{age:971}},
{$rename:{who:sex}},
{$unset:{other:true}})
正确写法:
db.c4.update({username:"熊子阳"},{
$set:{username:"Aoi"},
$inc:{age:971},
$rename:{who:"sex"},
$unset:{other:true}
})
#更新不存在的值,若不存在则不会有操作
> db.c3.update({username:"zs30"},{$set:{age:30}})
#在最后加一个true参数,作用是,如果不存在,则插入该条数据,默认为false则不管
> db.c3.update({username:"zs30"},{$set:{age:30}},true)
#第四个参数如果为true,当匹配到多条条件符合的元素时,都更改,默认为false,只改一条
> db.c3.update({},{$set:{age:20}},false,true)
4)删除文档 drop()
- db.collection.remove() 删除集合中的一个或多个文档(默认删除多个),传递条件跟find()一样,如果remove()第二个参数传递一个true,则只会删除第一个,find()不传参可以,remove()必须传参,如果只传递一个空对象作为参数,则会删除集合中的所有文档。删除集合(如果这是数据库中最后一个集合,那么数据库也就没了)
- db.collection.drop(); 一般数据库中的数据都不会删除,所以删除的方法很少调用,一般会在数据中添加一个字段,来表示数据是否被删除。
db.collection.deleteOne() #删除集合中的一个文档
db.collection.deleteMany() #删除集合中的多个文档
db.collection.remove({}) #清空一个集合(默认删除多个)
db.collection.drop() #删除一个集合
db.dropDatabase() #删除一个数据库
讲解
# 1. db.collectionName.remove()
# remove默认会删除所有匹配的文档。相当于deleteMany()
# remove可以加第二个参数,表示只删除匹配到的第一个文档。此时相当于deleteOne()
db.students.remove({name:'liu',true})
# 2. db.collectionName.deleteOne()
# 3. db.collectionName.deleteMany()
db.students.deleteOne({name:'liu'})
# 4. 删除所有数据:db.students.remove({})----性格较差,内部是在一条一条的删除文档。
# 可直接通过db.students.drop()删除整个集合来提高效率。
# 5.删除集合
db.collection.drop()
# 6.删除数据库
db.dropDatabase()
# 7.注意:删除某一个文档的属性,应该用update。 remove以及delete系列删除的是整个文档
# 8.当删除的条件为内嵌的属性时:
db.users.remove({"hobby.movies":'movie3'})
案例
db.神秘复苏.deleteOne({name:"杨间"}) #只删除了杨间这一行数据
db.神秘复苏.deleteMany({deptno:20}) #删除部门号为20的数据
db.神秘复苏.remove({}) #清空所有,但不清除集合
常用命令小结:
选择切换数据库:use articledb
插入数据:db.comment.insert({bson数据})
查询所有数据:db.comment.find();
条件查询数据:db.comment.find({条件})
查询符合条件的第一条记录:db.comment.findOne({条件})
查询符合条件的前几条记录:db.comment.find({条件}).limit(条数)
查询符合条件的跳过的记录:db.comment.find({条件}).skip(条数)
修改数据:db.comment.update({条件},{修改后的数据})
或
db.comment.update({条件},{$set:{要修改部分的字段:数据})
修改数据并自增某字段值:db.comment.update({条件},{$inc:{自增的字段:步进值}})
删除数据:db.comment.remove({条件})
统计查询:db.comment.count({条件})
模糊查询:db.comment.find({字段名:/正则表达式/})
条件比较运算:db.comment.find({字段名:{$gt:值}})
包含查询:db.comment.find({字段名:{$in:[值1, 值2]}})
或
db.comment.find({字段名:{$nin:[值1, 值2]}})
条件连接查询:db.comment.find({$and:[{条件1},{条件2}]})
或
db.comment.find({$or:[{条件1},{条件2}]})
7、聚合查询
语法
db.集合名称.aggregate([
{管道:{表达式}}
....
])
常用管道:
$group 将集合中的文档分组,用于统计结果
$match 过滤数据,只要输出符合条件的文档
$sort 聚合数据进一步排序
$skip 跳过指定文档数
$limit 限制集合数据返回文档数
....
常用表达式:
$sum 总和 $sum:1同count表示统计
$avg 平均
$min 最小值
$max 最大值
准备:
db.c3.insert({_id:1,name:"a",sex:1,age:1})
db.c3.insert({_id:2,name:"a",sex:1,age:2})
db.c3.insert({_id:3,name:"b",sex:1,age:3})
db.c3.insert({_id:4,name:"c",sex:2,age:4})
db.c3.insert({_id:5,name:"d",sex:2,age:5})
操作:
男女生的总年龄
#_id 必须加,后跟指定列
#rew 求和 返回结果数
db.c3.aggregate([
{
$group:{
_id:"$sex",
res:{$sum:"$sex"}
}
}
])
求男女总人数
db.c3.aggregate([
{
$group:{
_id:"$sex",
res:{$sum:1}
}
}
])
求学生总数和平均年龄
db.c3.aggregate([
{
$group:{
_id:null,
res:{$sum:1},
total_avg:{$avg:"$age"}
}
}
])
查询男生女生人数,升序排序
db.c3.aggregate([
{$group:{ _id:"$sex",res:{$sum:1}}},
{$sort:{res:1}}
])
8、练习
//1.进入my_test数据库
use my_test
show dbs
//2.向数据库的user集合中插入一个文档
db.users.insert({users:"猪八戒"});
//3.查询user集合中的文档
db.users.find();
//4.向数据库的user集合中插入一个文档
db.users.insert({users:"孙悟空"});
//5.查询数据库user集合中的文档
db.users.find();
//6.统计数据库user集合中的文档数量
db.users.find().count();
//7.查询数据库user集合中username为sunwukong的文档
db.users.find({users:"孙悟空"});
//8.向数据库user集合中的username为sunwukong的文档,添加一个address属性,属性值为huaguoshan
db.users.update({users:"孙悟空"},{$set:{address:"花果山"}});
//9.使用{username:"tangseng"} 替换 username 为 zhubajie的文档
db.users.update({users:"猪八戒"},{users:"唐僧"});
db.users.replaceOne({users:"猪八戒"},{users:"唐僧"});
//10.删除username为sunwukong的文档的address属性
db.users.update({users:"孙悟空"},{$unset:{address:1}});
//11.向username为sunwukong的文档中,添加一个hobby:{cities:["beijing","shanghai","shenzhen"] , movies:["sanguo","hero"]}
//MongoDB的文档的属性值也可以是一个文档,当一个文档的属性值是一个文档时,我们称这个文档为内嵌文档
db.users.update({users:"孙悟空"},{$set:{hobby:{cities:["beijing","shanghai","shenzhen"] , movies:["sanguo","hero"]}}});
//12.向username为tangseng的文档中,添加一个hobby:{movies:["A Chinese Odyssey","King of comedy"]}
db.users.update({users:"唐僧"},{$set:{hobby:{movies:["A Chinese Odyssey","King of comedy"]}}});
//13.查询喜欢电影hero的文档
//MongoDB支持直接通过内嵌文档的属性进行查询。注意:如果要通过内嵌文档来对文档进行查询,此时字段名必须要使用引号
db.users.find({"hobby.movies":"hero"});
//14.向tangseng中添加一个新的电影Interstellar
//$push用于数组中添加一个新的元素,不会考虑重复
//$addToSet向数组中添加一个新元素,如果数组中已经存在了该元素,则添加失败
db.users.update({users:"唐僧"},{$push:{"hobby.movies":"Interstellar"}});
//15.删除喜欢beijing的用户
db.users.remove({"hobby.cities":"beijing"});
//16.删除user集合
db.users.remove({});
//17.向numbers中插入20000条数据。添加两万条数据的性能高,尽量少调用系统的方法
for(var i=1;i<=20000;i++){
db.numbers.insert({num:i});
}
var arr = [];
for(var i=1;i<=20000;i++){
arr.push({num:i});
}
db.numbers.insert(arr);
db.numbers.find()
db.numbers.remove({})
//18.查询numbers中num为500的文档
db.numbers.find({num:500})
//19.查询numbers中num大于5000的文档
db.numbers.find({num:{$gt:5000}})
//20.查询numbers中num小于30的文档
db.numbers.find({num:{$lt:30}})
//21.查询numbers中num大于40小于50的文档
db.numbers.find({num:{$gt:40,$lt:50}})
//22.查询numbers中num大于19996的文档
db.numbers.find({num:{$gt:19996}})
//23.查看numbers集合中的前10条数据
db.numbers.find({num:{$lt:10}})
db.numbers.find().limit(10) //可以显示数据的上限
db.numbers.find()//在开发时绝对不会执行不带条件查询的语句
//24.查看numbers集合中的第11条到20条数据
db.numbers.find({num:{$gt:11,$lt:20}})
//分页:每页显示10条,1-10,11-20,skip()用于跳过指定数量的数据 //skip(页码-1 * 每页显示的条数).limit(每页显示的条数) 顺序反了结果也是一样的
db.numbers.find().skip(10).limit(10)
//25.查看numbers集合中的第21条到30条数据
db.numbers.find({num:{$gt:21,$lt:30}})
db.numbers.find().skip(20).limit(10)
9、文档之间的关系
一对一(one to one)
夫妻。在mongodb中可以通过内嵌文档的形式来体现出一对一的关系。
db.WifeAndHusband.insert([
{
wife:"黄蓉",
husband:{
name:"郭靖"
}
},
{
wife:"潘金莲",
husband:{
name:"武大郎"
}
}
])
一对多(one to many)/多对一(many to one)
父母孩子、用户订单、文章评论,也可以通过内嵌文档的方式来映射一对多的关系(将1的那个属性设置为多的里面的字段)。
db.order.insert({
list:["watermelor"],
user_id:ObjectId("5f87b1deda684b252c2fc7a5")
})
var user_id = db.users.findOne({username:"swk"})._id
//查询孙悟空的订单
db.order.find({user_id:user_id})
案例
#用户与订单:
db.users.insert([
{_id:100,username:'liu1'},
{_id:101,username:'liu2'}
])
db.orders.insert([
{list:['apple','banana'],user_id:100},
{list:['apple','banana2'],user_id:100},
{list:['apple'],user_id:101}
])
查询liu1的所有订单:
首先获取liu1的id: var user_id=db.users.findOne({name:'liu1'})._id;
根据id从订单集合中查询对应的订单: db.orders.find({user_id:user_id})
多对多(many to many)
分类商品、老师学生。通过内嵌文档的方式。
db.teacher.insert([
{name:"洪七公"},
{name:"黄药师"},
{name:"龟仙人"}
])
db.stus.insert([
{
name:"郭靖",
tech_ids:[
ObjectId("5f87b4b6da684b252c2fc7a8"),
ObjectId("5f87b4b6da684b252c2fc7a9")
]
},
{
name:"孙悟空",
tech_ids:[
ObjectId("5f87b4b6da684b252c2fc7a8"),
ObjectId("5f87b4b6da684b252c2fc7a9"),
ObjectId("5f87b4b6da684b252c2fc7aa")
]
}
])
#老师与学生
db.teachers.insert([
{
_id:100,
name:'liu1'
},
{
_id:101,
name:'liu2'
},
{
_id:102,
name:'liu3'
}
])
db.students.insert([
{
_id:1000,
name:'xiao',
tech_ids:[100,101]
},
{
_id:1001,
name:'xiao2',
tech_ids:[102]
}
])
//26.将dept和emp集合导入到数据库中
db.emp.find()
//27.查询工资小于2000的员工
db.emp.find({sal:{$lt:2000}})
//28.查询工资在1000-2000之间的员工
db.emp.find({sal:{$lt:2000,$gt:1000}})
//29.查询工资小于1000或大于2500的员工
db.emp.find({$or:[{sal:{$lt:1000}},{sal:{$gt:2500}}]})
//30.查询财务部的所有员工
var depno = db.dept.findOne({dname:"财务部"}).deptno
db.emp.find({depno:depno})
//31.查询销售部的所有员工
var depno = db.dept.findOne({dname:"销售部"}).deptno
db.emp.find({depno:depno})
//32.查询所有mgr为7698的所有员工
db.emp.find({mgr:7698})
//33.为所有薪资低于1000的员工增加工资400元
db.emp.updateMany({sal:{$lt:1000}},{$inc:{sal:400}});
10、sort和投影
find()查询文档时,默认情况是按照_id的值进行升序排列
#分页查询
db.users.find().skip(页码-1 * 每页显示的条数).limit(每页显示的条数)
db.users.find().limit(10) #前10条数据
db.users.find().skip(50).limit(10) #跳过前50条数据,即查询的是第61-70条数据,即第6页的数据
#排序
db.emp.find().sort({sal:1}) #1表示升序排列,-1表示降序排列
db.emp.find().sort({sal:1,empno:-1}) #先按照sal升序排列,如果遇到相同的sal,则按empno降序排列
#注意:skip,limit,sort可以以任意的顺序调用,最终的结果都是先调sort,再调skip,最后调limit
#设置查询结果的投影,即只过滤出自己想要的字段
db.emp.find({},{ename:1,_id:0}) #在匹配到的文档中只显示ename字段
sort()可以用来指定文档的排序的规则,需要传递一个属性来指定排序规则,1表示升序,-1表示降序
db.users.find({}).sort({sale:1})
db.users.find({}).sort({sale:1,qq:1}) //先指定sale的升序 再qq的降序
limit、skip、sort可以任意顺序的调用
查询时,我们可以在第二个参数的位置来设置查询结果的投影
db.users.find({},{sale:1})
11、索引
概述
索引支持在 MongoDB 中高效地执行查询.如果没有索引, MongoDB 必须执行全集合扫描, 即扫描集合中的每个文档, 以选择与查询语句 匹配的文档.这种扫描全集合的查询效率是非常低的, 特别在处理大量的数据时, 查询可以要花费几十秒甚至几分钟, 这对网站的性能是非常致命的.
如果查询存在适当的索引, MongoDB 可以使用该索引限制必须检查的文档数.
索引是特殊的数据结构, 它以易于遍历的形式存储集合数据集的一小部分.索引存储特定字段或一组字段的值, 按字段值排序.索引项的排 序支持有效的相等匹配和基于范围的查询操作.此外, MongoDB 还可以使用索引中的排序返回排序结果.
MongoDB 使用的是 B Tree, MySQL 使用的是 B+ Tree
// create index
db.<collection_name>.createIndex({ userid : 1, username : -1 })
// retrieve indexes
db.<collection_name>.getIndexes()
// remove indexes
db.<collection_name>.dropIndex(index)
// there are 2 ways to remove indexes:
// 1. removed based on the index name
// 2. removed based on the fields
db.<collection_name>.dropIndex( "userid_1_username_-1" )
db.<collection_name>.dropIndex({ userid : 1, username : -1 })
// remove all the indexes, will only remove non_id indexes
db.<collection_name>.dropIndexes()
索引的类型
单字段索引
MongoDB 支持在文档的单个字段上创建用户定义的升序/降序索引, 称为单字段索引 Single Field Index
对于单个字段索引和排序操作, 索引键的排序顺序(即升序或降序)并不重要, 因为 MongoDB 可以在任何方向上遍历索引
复合索引
MongoDB 还支持多个字段的用户定义索引, 即复合索引 Compound Index
复合索引中列出的字段顺序具有重要意义.例如, 如果复合索引由 { userid: 1, score: -1 }
组成, 则索引首先按 userid
正序排序, 然后 在每个 userid
的值内, 再在按 score
倒序排序.
其他索引
- 地理空间索引 Geospatial Index
- 文本索引 Text Indexes
- 哈希索引 Hashed Indexes
地理空间索引(Geospatial Index)
为了支持对地理空间坐标数据的有效查询, MongoDB 提供了两种特殊的索引: 返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引.
文本索引(Text Indexes)
MongoDB 提供了一种文本索引类型, 支持在集合中搜索字符串内容.这些文本索引不存储特定于语言的停止词(例如 “the”, “a”, “or”), 而将集合中的词作为词干, 只存储根词.
哈希索引(Hashed Indexes)
为了支持基于散列的分片, MongoDB 提供了散列索引类型, 它对字段值的散列进行索引.这些索引在其范围内的值分布更加随机, 但只支持相等匹配, 不支持基于范围的查询。
索引的管理操作
索引的查看
语法
db.collection.getIndexes()
默认 _id
索引: MongoDB 在创建集合的过程中, 在 _id
字段上创建一个唯一的索引, 默认名字为 _id
, 该索引可防止客户端插入两个具有相同值的文 档, 不能在 _id
字段上删除此索引.
注意:该索引是唯一索引, 因此值不能重复, 即 _id
值不能重复的.
在分片集群中, 通常使用 _id
作为片键.
索引的创建
语法
db.collection.createIndex(keys, options)
注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex()
, 之后的版本使用了 db.collection.createIndex()
方法, ensureIndex()
还能用, 但只是 createIndex()
的别名.
例子
$ db.comment.createIndex({userid:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
$ db.comment.createIndex({userid:1,nickname:-1})
...
索引的删除
语法
# 删除某一个索引
$ db.collection.dropIndex(index)
# 删除全部索引
$ db.collection.dropIndexes()
提示:
_id
的字段的索引是无法删除的, 只能删除非 _id
字段的索引
示例
# 删除 comment 集合中 userid 字段上的升序索引
$ db.comment.dropIndex({userid:1})
索引使用
执行计划
分析查询性能 (Analyze Query Performance) 通常使用执行计划 (解释计划 - Explain Plan) 来查看查询的情况
$ db.<collection_name>.find( query, options ).explain(options)
比如: 查看根据 user_id
查询数据的情况
未添加索引之前
"stage" : "COLLSCAN"
, 表示全集合扫描
添加索引之后
"stage" : "IXSCAN"
, 基于索引的扫描
涵盖的查询
当查询条件和查询的投影仅包含索引字段是, MongoDB 直接从索引返回结果, 而不扫描任何文档或将文档带入内存, 这些覆盖的查询十分有效
索引官方文档:索引官方文档
12、mongoose
简介:
-
1.mongoose是nodejs中的专门用于操作mongodb数据库的js库
-
2.mongoose中的对象:
- Schema 模式对象(用于约束文档的结构)
- Model 模型对象(即mongodb中的集合)
- Document 文档对象(即mongodb中的文档)
安装:
npm i -s mongoose
连接数据库:
// 1.引入mongoose
const mongooes = require("mongoose");
// 2.连接mongodb数据库
mongooes.connect("mongodb://localhost/users", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// 3.监听mongodb数据库的连接状态
// 绑定数据库连接成功事件
mongooes.connection.once("open", function () {
console.log("连接成功");
});
// 绑定数据库连接失败事件
mongooes.connection.once("close", function () {
console.log("数据库连接已经断开");
});
// 4.断开数据库连接(一般不用)
mongooes.disconnect();
创建模式对象和模型对象:
const Schema=mongooes.schema;
//创建模式对象
const stuSchema=new Schema({
name:String,
age:Number,
gender:{
type:String,
default:'female'
},
address:String
})
//创建模型对象
const StuModel=stuSchema.model("student",stuSchema); //第一个参数表示创建的集合的名称,第二个参数表示利用的模式对象
利用模型对象进行增删查改操作:
添加操作:
UserModel.create({ user_id: 100, name: "liu1" }, function (err) {
if (!err) {
console.log("插入成功");
} else {
console.log(err);
}
});
let data = [
{ user_id: 101, name: "liu2", age: 22 },
{ user_id: 102, name: "liu3" },
];
UserModel.create(data, function (err) {
console.log(arguments[1]); //第二个值表示的是所添加的文档对象,是一个数组
});
查询操作:
/*
查询:
model.find(conditions,[projection],[options],callback)
conditions:查询的条件
projection:投影 { name: 1, gender: 1, _id: 0 } 或 'name gender -_id'
options:查询选项 { skip: xx, limit: xx }
model.findOne(...)
model.findById(...)
model.countDocuments(conditions,callback) 查询文档的数量
*/
UserModel.find({}, function (err, data) {
console.log(data);
});
UserModel.find(
{ name: /liu/i },
"name gender -_id",
{ skip: 2, limit: 1 },
function (err, data) {
console.log(data); //返回的是一个文档对象数组
}
);
UserModel.findById("5f9fbfba14319e492c0f5bc4", function (err, data) {
console.log(data);
console.log(data instanceof UserModel); //true 返回的文档对象属于模型对象(即集合)的实例对象
});
UserModel.countDocuments({}, function (err, data) {
console.log(data);
});
修改操作:
/* 修改:
model.update(conditions,[doc],[options],callback)
doc:修改后的文档对象
model.updateMany(...)
model.uodateOne(...)
*/
UserModel.updateOne({ name: "liu1" }, { $set: { age: 22 } }, function (
err,
data
) {
if (!err) {
console.log("修改成功");
}
});
UserModel.find({ name: "liu1" }, function (err, data) {
console.log(data);
});
删除操作:
/*
删除:
model.remove(conditions,callback)
model.deleteOne(...)
model.deleteMany(...)
*/
UserModel.remove(
{
name: "liu2",
},
function (err, data) {
console.log("删除成功");
}
);
UserModel.find({}, function (err, data) {
console.log(data);
});
模块化处理:
- 1.单独创建一个数据库连接文件dbconncet.js
const mongooes = require("mongoose");
mongooes.connect("mongodb://localhost/mongooes_test", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
mongooes.connection.once("open", function () {
console.log("连接成功");
});
- 2.为每一个集合创建一个模型对象文件xxxModel.js
const mongooes = require("mongoose");
const Schema = mongooes.Schema;
const userSchema = new Schema({
user_id: String,
name: String,
age: Number,
gender: {
type: Number,
default: 0,
},
});
const UserModel = mongooes.model("user", userSchema);
module.exports = UserModel;
- 3.在最终的文件index.js中引入数据库连接文件和创建模型的文件:
const mongooes = require("./dbconncet");
const PostModel = require("./models/postModel");
PostModel.findOne({}, function (err, data) {
if (!err) {
console.log(data);
}
});
mongoose支持的用法:用法