Mongodb基础一:数据库创建删除、集合创建删除、数据增删改查
1. 数据库使用
- ./mongod –dbpath DB_PATH开启mongodb服务,然后./mongo开启客户端;
- 清屏: cls
- 查看所有数据库列表: show dbs
2. 创建数据库
使用数据库、创建数据库:
> use joyitsai
复制代码
如果真的想把这个数据库创建成功,那么必须插入一个数据。 数据库中不能直接插入数据,只能往集合(collections)中插入数据。
不需要专门创建集合,只需要写点语法插入数据就会创建集合, 系统发现student是一个陌生的集合名字,所以就自动创建了集合:
> db.student.insert({'name': 'xiaoming'});
复制代码
显示当前数据库中所有集合(mysql中叫表):
> show collections;
复制代码
删除当前所在的数据库:
> db.dropDatabase();
复制代码
删除指定集合(表):
db.COOLECTION_NAME.drop(),例如删除上面的student集合:
> db.student.drop();
复制代码
3. 插入数据:
插入数据,随着数据的插入,数据库创建成功了,集合也创建成功了:
db.集合名.insert({"name":"zhangsan"});
复制代码
4. 数据查找
查询userinfo集合中所有数据:
> db.userinfo.find(); /*相当于select * from userinfo;*/
复制代码
查询去重后的字段数据:
> db.userinfo.distinct('name');
复制代码
过滤掉name中的相同数据,相当于:select distict name from userinfo;
查询age = 22的记录:
db.userInfo.find({"age": 22});
复制代码
相当于: select * from userInfo where age = 22;
查询age > 22的记录:
db.userInfo.find({age: {$gt: 22}});
复制代码
相当于:select * from userInfo where age >22;
查询age < 22的记录 :
> db.userInfo.find({age: {$lt: 22}});
复制代码
查询age >= 25的记录:
> db.userInfo.find({age: {$gte: 25}});
复制代码
相当于:select * from userInfo where age >= 25;
查询age <= 25的记录:
db.userInfo.find({age: {$lte: 25}});
复制代码
查询name中包含 mongo的数据(模糊查询):
> db.userInfo.find({name: /mongo/});
复制代码
相当于select * from userInfo where name like ‘%mongo%’;
查询name中以mongo开头的 数据:
> db.userInfo.find({name: /^mongo/});
复制代码
相当于select * from userInfo where name like ‘mongo%’;
查询指定字段(name、age)数据:
> db.userInfo.find({}, {name: 1, age: 1});
复制代码
相当于:select name, age from userInfo;,当然name也可以用true或false,当用ture的情况下和name:1效果一样,如果用false就是排除name,显示name以外的列信息。
查询指定字段(name、age)数据且 age > 25 :
> db.userInfo.find({age: {$gt: 25}}, {name: 1, age: 1});
复制代码
相当于:select name, age from userInfo where age >25;
按照年龄排序 :
升序:
db.userInfo.find().sort({age: 1});
复制代码
降序:
> db.userInfo.find().sort({age: -1});
复制代码
查询name = zhangsan, age = 22的数据:
> db.userInfo.find({name: 'zhangsan', age: 22});
复制代码
相当于:select * from userInfo where name = ‘zhangsan’ and age = ‘22’;
查询前5条数据:
db.userInfo.find().limit(5);
复制代码
相当于:selecttop 5 * from userInfo;
查询10条以后的数据:
db.userInfo.find().skip(10);
复制代码
相当于:select * from userInfo where id not in ( selecttop 10 * from userInfo );
查询在5-10之间的数据:
db.userInfo.find().limit(10).skip(5);
复制代码
可用于分页,limit是pageSize,skip是第几页*pageSize
or查询:
db.userInfo.find({$or: [{age: 22}, {age: 25}]});
复制代码
相当于:select * from userInfo where age = 22 or age = 25;
findOne 查询第一条数据:
db.userInfo.findOne();
复制代码
相当于:selecttop 1 * from userInfo;或者db.userInfo.find().limit(1);
查询某个结果集的记录条数 统计数量:
db.userInfo.find({age: {$gte: 25}}).count();
复制代码
相当于:select count(*) from userInfo where age >= 20;
4. 数据的修改:
4.1一般查找修改语法:
db.集合名.update({查询条件},{set{修改的数据}});其中的set关键字是用来设置要修改的数据。如果没有用$set,那么整条数据会被重新覆盖成新设置的数据。
查找名字叫做小明的,把年龄更改为16岁:
> db.student.update({"name":"小明"},{$set:{"age":16}});
复制代码
查找数学成绩是70,把年龄更改为33岁:
> db.student.update({"math":70},{$set:{"age":33}});
复制代码
4.2 更改所有匹配项目:
默认的.update()只能更新一条数据,如果你需要更新所有与条件匹配的数据,就得加上multi选项。例如吧所有性别为男的数据将age更新为33:
> db.student.update({"sex":"男"},{$set:{"age":33}},{multi: true});
复制代码
4.3 没有使用$set关键字,原来的数据被新数据覆盖掉:
db.student.update({"name":"小明"},{"name":"大明","age":16});
复制代码
5. 删除数据
db.collectionsNames.remove( { 查询条件 } )
复制代码
默认的,.remove()方法会将匹配到的所有数据都删除掉,如果只想删除匹配到的第一条数据,使用 justOne:true:
Mongodb基础二:索引原理与使用
1. 索引的原理?
索引是对数据库表中一列或多列的值进行排序的一种数据结构,可以让我们查询数据库变得更快。为了方便后续介绍,先科普下MongoDB里的索引机制(同样适用于其他的数据库比如mysql)。
> db.person.find()
{ "_id" : ObjectId("571b5da31b0d530a03b3ce82"), "name" : "jack", "age" : 19 }
{ "_id" : ObjectId("571b5dae1b0d530a03b3ce83"), "name" : "rose", "age" : 20 }
{ "_id" : ObjectId("571b5db81b0d530a03b3ce84"), "name" : "jack", "age" : 18 }
{ "_id" : ObjectId("571b5dc21b0d530a03b3ce85"), "name" : "tony", "age" : 21 }
{ "_id" : ObjectId("571b5dc21b0d530a03b3ce86"), "name" : "adam", "age" : 18 }
复制代码
当你往某各个集合插入多个文档后,每个文档在经过底层的存储引擎持久化后,会有一个位置信息,通过这个位置信息,就能从存储引擎里读出该文档。为方便介绍,统一用pos(position的缩写)来代表位置信息。
比如上面的例子里,person集合里包含插入了5个文档,假设其存储后位置信息如下(为方便描述,文档省去_id字段)
位置信息 | 文档 |
---|---|
pos1 | {“name” : “jack”, “age” : 19 } |
pos2 | {“name” : “rose”, “age” : 20 } |
pos3 | {“name” : “jack”, “age” : 18 } |
pos4 | {“name” : “tony”, “age” : 21} |
pos5 | {“name” : “adam”, “age” : 18} |
假设现在有个查询 db.person.find( {age: 18} ), 查询所有年龄为18岁的人,这时需要遍历所有的文档(全表扫描),根据位置信息读出文档,对比age字段是否为18。当然如果只有4个文档,全表扫描的开销并不大,但如果集合文档数量到百万、甚至千万上亿的时候,对集合进行全表扫描开销是非常大的,一个查询耗费数十秒甚至几分钟都有可能。
如果想加速 db.person.find( {age: 18} ),就可以考虑对person表的age字段建立索引。
db.person.createIndex( {age: 1} ) // 按age字段创建升序索引
复制代码
建立索引后,MongoDB会额外存储一份按age字段升序排序的索引数据,索引结构类似如下,索引通常采用类似btree的结构持久化存储,以保证从索引里快速(O(logN)的时间复杂度)找出某个age值对应的位置信息,然后根据位置信息就能读取出对应的文档。
AGE | 位置信息 |
---|---|
18 | pos3 |
18 | pos5 |
19 | pos1 |
20 | pos2 |
21 | pos4 |
简单的说,索引就是将文档按照某个(或某些)字段顺序组织起来,以便能根据该字段高效的查询。
众所周知,MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段),_id是文档唯一的标识,为了保证能根据文档id快递查询文档,MongoDB默认会为集合创建_id字段的索引。
> db.person.getIndexes() // 查询集合的索引信息
[
{
"ns" : "test.person", // 集合名
"v" : 1, // 索引版本
"key" : { // 索引的字段及排序方向
"_id" : 1 // 根据_id字段升序索引
},
"name" : "_id_" // 索引的名称
}
]
复制代码
2. MongoDB索引的基本使用
2.1 单字段索引
创建单字段索引:
> db.user.ensureIndex({name: 1})
复制代码
上述语句针对name创建了单字段索引,其能加速对name字段的各种查询请求,是最常见的索引形式,MongoDB默认创建的id索引也是这种类型。
{name: 1}代表升序索引,也可以通过{name: -1}来指定降序索引,对于单字段索引,升序/降序效果是一样的。
查看当前集合的所有索引信息:
> db.user.getIndexes()
复制代码
删除单字段索引:
db.user.dropIndex({name: 1});
复制代码
2.2 复合索引:
数字1表示name键的索引按升序存储,-1表示age键的索引按照降序方式存储。
db.user.ensureIndex({"name":1, "age":-1})
复制代码
该索引被创建后,基于name和age的查询将会用到该索引,或者是基于name的查询也会用到该索引,但是只基于age的查询将不会用到该复合索引。因此可以说,如果想用到复合索引,必须在查询条件中包含复合索引中的前N个索引列。
如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB可以智能的帮助我们调整该顺序,以便使复合索引可以为查询所用。如:
db.user.find({"age": 30, "name": "stephen"})
复制代码
对于上面示例中的查询条件,MongoDB在检索之前将会动态的调整查询条件的顺序,以使该查询可以用到刚刚创建的复合索引。
对于上面创建的索引,MongoDB都会根据索引的keyname和索引方向为新创建的索引自动分配一个索引名,下面的命令可以在创建索引时为其指定索引名(userindex),如:
db.user.ensureIndex({"name":1},{"name":"userindex"})
复制代码
2.3 唯一索引
在缺省{unique:true}的情况下创建的索引均不是唯一索引。下面将创建唯一索引,如:
db.user.ensureIndex({"userid":1},{"unique":true})
复制代码
如果再次插入userid重复的文档时,MongoDB将报错,以提示插入重复键,如:
db.user.insert({"userid":5})
db.user.insert({"userid":5})
复制代码
E11000 duplicate key error index: user.user.$userid_1 dup key: { : 5.0 }
如果插入的文档中不包含userid键,那么该文档中该键的值为null,如果多次插入类似的文档,MongoDB将会报出同样的错误:
E11000 duplicate key error index: user.user.$userid_1 dup key: { : null }
3. 使用explain
explain是非常有用的工具,会帮助你获得查询方面诸多有用的信息:
explain会返回查询使用的索引情况,耗时和扫描文档数的统计信息。
3.1 explain( ‘executionStats’ )查询具体的执行时间
db.tablename.find().explain( "executionStats" )
复制代码
输出的如下数值:explain.executionStats.executionTimeMillis就是当前数据查询所用的时间
Mongodb基础三:mongoose实现数据增删改查
mongoose 的安装以及使用
安装
npm install mongoose --save
复制代码
引入mongoose并连接数据库
const mongoose = require('mongoose');
mongoose.connect('mongodb://jzcms:jzcms@152.136.136.13:27017/jzCms',(err)=>{
if(err){
console.log(err)
console.log('数据库链接失败')
}else{
console.log('数据库连接成功');
console.log('服务器启动成功');
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
}
})
复制代码
如果mongodb数据库有账号密码:
/**通过mongodb://username:password@host1:port/database_name?authSource指定验证前面身份信息的数据库来源 */
mongoose.connect('mongodb://账号:密码@ip地址:27017/集合名称?authSource=admin',{
useNewUrlParser: true,
});
复制代码
定义一个Schema
Schema是mongoose 里会用到的一种数据模式,可以理解为表结构的定义;每个schema 会映射到mongodb 中的一个collection,它不具备操作数据库的能力,只是对数据库集合的各个字段类型的规范定义。
<!--独立-->
const mongoose = require('mongoose');
module.exports = new mongoose.Schema({
title: String,
shareTitle: String,
shareImgUrl: String,
shareDesc: String,
content: Array
})
<!--非独立-->
var UserSchema=mongoose.Schema({
name: String,
age:Number,
status:'number'
})
复制代码
创建数据模型
- 定义好了Schema,接下就是生成Model。model 是由schema 生成的模型,可以对数据库的操作。
- mongoose.model 里面可以传入两个参数也可以传入三个参数:
- mongoose.model(参数1:模型名称(首字母大写),参数2:Schema)
- mongoose.model(参数1:模型名称(首字母大写),参数2:Schema,参数3:集合名称)
- 如果传入2 个参数的话:User模型会默认去操作与User模型同名的复数形式的集合,如通过下面方法创建模型,那么这个模型将会操作users这个集合:
var User=mongoose.model('User', UserSchema);
复制代码
如果传入3 个参数的话:模型默认操作第三个参数定义的集合名称,如下面的代码,模型User会指定操作user这个集合:
var User=mongoose.model('User', UserSchema, 'user');
复制代码
// 模型类 用于对用户的表进行操作
const mongoose = require('mongoose');
const taobaoSchema = require('../schemas/taobao');
// 创建一个模型
module.exports = mongoose.model('taobao',taobaoSchema, 'taobao');
复制代码
增删改查
查找数据
/**模型.find({查询条件}, (err, data)=>{回调函数}) */
User.find({}, (err, docs)=>{
if(err){
console.log(err);
return;
}
console.log(docs);
});
复制代码
增加数据
/*模型实例化*/
const news = new News({
title:'新闻标题',
author:'joyitsai',
pic:'01.png',
content:'新闻内容',
status: 1
});
/*实例.save((err,docs)=>{回调函数}) 来将数据保存到数据库*/
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
复制代码
修改数据
ews.updateOne(
{'_id':'5cf5e613ba3c6298a8734973'}, //条件
{title: '这是一则新闻111'}, //要更新的内容
/*回调函数*/
(err, docs)=>{
if(err){return console.log('更新数据失败');}
console.log(docs);
}
)
复制代码
删除数据
News.deleteOne(
{'_id':'5cf5e613ba3c6298a8734973'}, //查找条件
/*回调函数*/
(err,docs)=>{
if(err){return console.log('删除数据失败')}
console.log(docs);
}
)
复制代码
保存成功查找
News.updateOne(
{'_id':'5cf5e613ba3c6298a8734973'}, //条件
{title: '这是一则新闻111'}, //要更新的内容
(err, docs)=>{
if(err){
return console.log('更新数据失败');
}
/**更新数据成功,紧接着查询数据 */
News.find({},(err, docs)=>{
if(err){
return console.log(err)
}
console.log(docs);
})
}
)
复制代码
MongoDB基础四:mongoose设置默认值、模块化及性能测试
1. mongoose设置默认值
1.1 Schema中未定义的字段,其数据不能被添加
在上一篇文章里说到,首先要定义一个Schema,作为对数据库集合的各个字段类型的规范定义,此外,在添加数据时,如果添加的数据字段没有在Schema中定义,那么该字段数据不会被添加到集合里:
/*定义Schema*/
const NewsSchema = mongoose.Schema({
title: String,
author: String,
pic:String,
content: String,
status: Number
});
/*定义News模型*/
const News = mongoose.model('News', NewsSchema, 'news');
/*实例化模型,添加数据*/
const news = new News({
title:'最新新闻',
author:'joyitsai',
pic:'03.png',
content:'新闻内容',
status: 1,
views: 300
});
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
复制代码
上面再添加数据时,views:200字段的数据再Schema中并没有定义,那么再添加数据时,这个字段的数据并不会被加到集合中,查看一下news集合的数据
> db.news.find()
{ "_id" : ObjectId("5cf62d5333e8ab297050eb97"), "title" : "最新新闻", "author" : "joyitsai", "pic" :
"03.png", "content" : "新闻内容", "status" : 1, "__v" : 0 }
复制代码
1.2 Schema定义时,指定默认值:
在Schema中定义字段的默认值,添加数据时,如果该字段数据没有被添加,那么mongoose会自动使用Schema中该字段的默认值作为数据添加到集合里:
/*定义Schema*/
const NewsSchema = mongoose.Schema({
title: String,
author: String,
pic:String,
content: String,
/*指定字段的type、默认值*/
status: {
type: Number,
default: 1
}
});
const News = mongoose.model('News', NewsSchema, 'news');
/*添加数据,不添加status字段时,会自动使用默认值添加*/
const news = new News({
title:'最新新闻007',
author:'joyitsai',
pic:'07.png',
content:'新闻内容007'
});
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
复制代码
查看刚刚添加的数据,会发现数据中有status:1字段:
> db.news.find()
{ "_id" : ObjectId("5cf630516b0b333d40a59f31"), "status" : 1, "title" : "最新新闻007", "author" :
"joyitsai", "pic" : "07.png", "content" : "新闻内容007", "__v" : 0 }
复制代码
2. mongoose模块化:
在每次使用mongoose来操作数据库时,都要写连接数据库、定义Schema、定义模型,显得很繁琐。那么现在就用模块化来解决这个问题。
简单说,就是把连接数据库、定义Schema、定义模型都写成独立的模块,在相应功能代码中直接引入模块,进行数据库操作即可。
2.1 连接数据库——模块化:
// db.js
const mongoose = require('mongoose');
mongoose.connect(
'mongodb://127.0.0.0:27017/databaseName',
{useNewUrlParser: true,},
(err)=>{
if(err){
console.log('数据库连接失败');
return;
}
}
)
module.exports = mongoose;
复制代码
2.2 定义Schema,定义模块
在定义Schema和模块的代码中导入上面连接成功后的mongoose模块:
// news.js
const mongoose = require('./db');
// 定义Schema
const NewsSchema = mongoose.Schema({
title: String,
author: String,
pic:String,
content: String,
status: {
type: Number,
default: 1
}
});
// 导出News模型
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
2.3 引入news.js模型,直接操作数据库:
//insertData.js
const News = require('./myDatabase/news');
const news = new News({
title:'最新新闻007',
author:'joyitsai',
pic:'07.png',
content:'新闻内容007'
});
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
复制代码
以上就实现了对mongoose连接数据库、定义Schema和定义模型的基本模块化就完成了。
3. 模块化性能测试
现在有人可能会对这种模块化的性能产生疑问,因为如果要对多个集合的数据进行操作,必然要在同一个文件中导入多个模型。
然而,每次导入一个模型,上层都会导入一次数据库连接的模块,会不会导致导入多个模型时数据库重复连接,而严重影响程序执行性能
其实,mongoose在内部给我们解决了这个问题,即使在一次性导入多个模型,只由第一次导入时的模块担任数据库连接工作,下面就测试一下:
//假设又封装了一个User集合模型
console.time('news')
const News = require('./myDatabase/news');
const news = new News({
title:'最新新闻007',
author:'joyitsai',
pic:'07.png',
content:'新闻内容007'
});
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
// 测试向news集合中添加数据的时间
console.timeEnd('news');
console.time('user')
const User = require('./myDatabase/user');
const user = new User({
name:'zhangsan',
age: 36,
pic:'07.png',
});
user.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
// 测试向user集合中添加数据的时间
console.timeEnd('user');
复制代码
下面看一下测试结果:
news: 341.740ms
user: 2.666ms
{ status: 1,
_id: 5cf67e2fc3aa2254349ed978,
title: '最新新闻007',
author: 'joyitsai',
pic: '07.png',
content: '新闻内容007',
__v: 0 }
{ status: 1,
_id: 5cf67e2fc3aa2254349ed979,
name: 'zhangsan',
age: 36,
pic: '07.png',
__v: 0 }
复制代码
很显然,在第一次引入News模型的用时是第二次引入User模型的150倍,这验证了在多次引入模型的情况下,并不会重复连接数据库。
MongoDB基础五:Mongoose 预定义模式修饰符、Setters与Getters自定义修饰符
一、mongoose 预定义模式修饰符
mongoose 提供的预定义模式修饰符,可以对我们增加的数据进行一些格式化。mongoose提供了lowercase、uppercase 、trim预定义模式修饰符,其中trim能够对字段数据做两端去空格的格式化:
/*定义带trim修饰符的Schema*/
const NewsSchema = mongoose.Schema({
title: {
type:String,
trim: true
},
author: String,
pic:String,
content: String,
status: {
type: Number,
default: 1
}
});
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
const News = require('./myDatabase/news');
/*添加数据时,将title字段的数据两端加一些空格*/
const news = new News({
title:' 最新新闻007 ',
author:'joyitsai',
pic:'07.png',
content:'新闻内容007'
});
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
复制代码
添加数据成功后,查看发现数据如下:
{ "_id" : ObjectId("5cf72b415b4e487a34c85cb4"), "status" : 1, "title" : "最新新闻007", "author" :
"joyitsai", "pic" : "07.png", "content" : "新闻内容007", "__v" : 0 }
复制代码
你会发现,刚刚添加的数据中title字段数据两端的空格被格式化去掉了,就是trim的作用。
二、Mongoose Getters 与Setters 自定义修饰符
除了mongoose 内置的修饰符以外,我们还可以通过set(建议使用) 修饰符在增加数据的时候对数据进行格式化。也可以通过get(不建议使用)在实例获取数据的时候对数据进行格式化。
2.1 Setters
set(用户添加数据){ return 真正添加到数据库的数据}来自定义修饰符,在set内部对用户添加的数据进行格式化操作,最终返回格式化后的数据作为真正存入数据库的数据:
const NewsSchema = mongoose.Schema({
title: {
type:String,
trim: true
},
author: String,
/*
url为空,则返回空的url;
url前面没有`http://`或`https:// `,则在前面拼接上`http://`
返回带有`http://`或`https://`的url,作为存入数据库的数据
*/
redirect: {
type: String,
set(url){
if(!url) return url;
if(url.indexOf('http://')!=0 && url.indexOf('https://')!=0){
url = 'http://' + url;
}
return url;
}
}
});
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
添加数据时,向redirect字段添加一个www.baidu.com,然后查看存入数据库的数据是否是www.baidu.com,如果是,说明set自定义修饰符有效了:
const News = require('./myDatabase/news');
const news = new News({
title:' 最新新闻007 ',
author: 'joyitsai',
redirect: 'www.baidu.com'
});
news.save((err, docs)=>{
if(err){
console.log('添加数据出错');
return;
}
console.log(docs);
});
复制代码
查看一下数据:
{ "_id" : ObjectId("5cf731f8d8bda03afc8367b7"), "title" : "最新新闻007", "author" : "joyitsai",
"redirect" : "http://www.baidu.com", "__v" : 0 }
复制代码
set自定义修饰符有效了。
2.2 Getters
get将用户添加的数据格式化后,并没有存入数据库;仅仅是用户在实例化模型时,这个实例中的数据被做了格式化操作,在常规应用中意义不大。
MongoDB基础六:Mongoose 索引、内置CURD 方法、扩展静态方法和实例方法
一、Mongoose 索引
索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快。
1.1 创建索引
mongoose 中除了以前创建索引的方式,我们也可以在定义Schema时创建索引:
const NewsSchema = mongoose.Schema({
news_id:{
type:Number,
// 唯一索引
unique: true
},
title: {
type:String,
// 普通索引
index: true
},
author: String,
});
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
上面代码中,通过在Schema的字段中定义unique: true创建唯一索引,和index: true创建一般索引。
1.2 测试索引查询数据的性能
首先看一下没有索引时(将上面代码中unique: true和index: true注释掉)查询数据的时间(news集合中有100万条数据):
console.time('news');
News.find({title: '新闻200000'}, (err, docs)=>{
if(err) return console.log(err);
console.log(docs);
console.timeEnd('news');
})
复制代码
查询用时:
[ { _id: 5cf7795fb1f4664f499f265c,
news_id: 200000,
title: '新闻200000',
author: 'joyitsai' } ]
news: 469.795ms
复制代码
当依照上面创建索引之后,再对这条数据进行查询,查询用时:
[ { _id: 5cf77921b1f4664f499c673c,
news_id: 20000,
title: '新闻20000',
author: 'joyitsai' } ]
news: 92.108ms
复制代码
在mongoose中使用索引查询数据的性能对于没有索引的情况下有了5倍左右的提升,但没有在mongo命令行中查询时的性能好。
二、Mongoose 内置CURD
CURD: CURD是一个数据库技术中的缩写词,一般的项目开发的各种参数的基本功能都是CURD。作用是用于处理数据的基本原子操作。
Model.deleteMany()
Model.deleteOne()
Model.find()
Model.findById()
Model.findByIdAndDelete()
Model.findByIdAndRemove()
Model.findByIdAndUpdate()
Model.findOne()
Model.findOneAndDelete()
Model.findOneAndRemove()
Model.findOneAndUpdate()
Model.replaceOne()
Model.updateMany()
Model.updateOne()
复制代码
三、扩展Mongoose CURD 方法
3.1 在Schema上自定义静态方法:
在定义的Schema上通过Schema.statics.yourFind封装自己的数据查找方法,方便后期数据查找工作。callback为数据查找完成后的回调函数,回调函数中可以进行错误处理或者数据处理等操作:
const NewsSchema = mongoose.Schema({
news_id:{
type:Number,
// 唯一索引
unique: true
},
title: {
type:String,
// 普通索引
index: true
},
author: String
});
// 通过Schema来自定义模型的静态方法,自定义数据查找的方法
NewsSchema.statics.findByNewsId = function(news_id, callback){
//this指向当前模型,调用模型上的find()方法,封装自己的静态方法
this.find({news_id: news_id}, function(err, docs){
//数据查找完成后,调用callback,对错误信息或者查找到的数据进行处理
callback(err, docs);
})
}
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
然后在相关功能代码中,就可以通过News.findByNewsId()来查找数据了:
News.findByNewsId(20000, (err, docs)=>{
if(err) return console.log(err);
console.log(docs);
});
复制代码
查找结果如下:
[ { _id: 5cf77921b1f4664f499c673c,
news_id: 20000,
title: '新闻20000',
author: 'joyitsai' } ]
复制代码
3.2 在Schema上自定义实例方法:
实例方法是通过Schema.methods.yourMethod来定义的,其中this指向了NewsSchema对应模型的实例:
NewsSchema.methods.print = function(){
console.log('这是一个实例方法');
console.log(this.news_id);
}
复制代码
调用实例方法:
// 对News模型实例化
const news = new News({
news_id: 1,
title: '新闻1',
author: 'joyitsai'
})
//在实例上调用实例方法
news.print();
复制代码
调用实例方法后的结果对应了实例方法的定义:
这是一个实例方法
新闻1
复制代码
实例方法不太常用,但如果你在项目中需要对数据模型实例化之后,进行一些特殊的操作,可以通过实例化方法来执行,提高功能性操作的效率。
MongoDB基础七:Mongoose 数据校验
一、Mongoose 校验参数
在定义Schema时,可以设置数据的校验参数,这样可以对操作数据库数据时,对其做进一步的规范。主要的校验参数有:
- ==required== : 数据可以是任意类型,表示这个数据必须传入
- ==max==: 用于Number 类型数据,最大值
- ==min==: 用于Number 类型数据,最小值
- ==enum==:枚举类型(数组中的类型是String),要求数据必须满足枚举值enum: [‘0’, ‘1’, ‘2’]
- ==match==:增加的数据必须符合match(正则)的规则
- ==maxlength==:数据必须是String类型,数据最大长度
- ==minlength==:数据必须是String类型,数据最小长度
1.1 required示例:
// 定义news_id是必须要传入的数据
const NewsSchema = mongoose.Schema({
news_id:{
type:Number,
// 唯一索引
unique: true,
required: true
},
title: {
type:String,
// 普通索引
index: true
},
author: String
});
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
现在我们在news集合中添加数据时,如果没有传入news_id的字段数据,就会报错:
const news = new News({
/*此处没有传入news_id数据*/
title: '新闻1',
author: 'joyitsai'
})
news.save((err, docs)=>{
if(err) return console.log(err);
console.log(docs);
})
复制代码
运行报错:
{ ValidationError: News validation failed: news_id: Path `news_id` is required.
.....
}
复制代码
1.2 max和min示例:
如果Schema定义时,指定max和min,那么传入的数据值必须在min和max之间,否则报错:
const NewsSchema = mongoose.Schema({
news_id:{
type:Number,
// 唯一索引
unique: true,
},
title: {
type:String,
// 普通索引
index: true
},
author: String,
words: {
type: Number,
min: 200,
max: 1000
}
});
const News = mongoose.model('News', NewsSchema, 'news');
module.exports = News;
复制代码
现在如果添加数据时words为100:
const news = new News({
news_id: 2,
title: '新闻2',
author: 'joyitsai',
words: 100
})
news.save((err, docs)=>{
if(err) return console.log(err);
console.log(docs);
})
复制代码
就会提示数据不能小于min的值,相反如果数据大于max也会提示相应错误:
{ ValidationError: News validation failed: words: Path `words` (100) is less than minimum allowed value (1000).
......
}
复制代码
其他的几个参数就不一一举例了,请自行验证。
二、自定义校验:
类似于之前set的用法,在定义Schema时可以用validate关键字自定义一个校验器函数,来检测数据是否满足想要的规范,如果通过验证返回true,没有通过则返回false:
const NewsSchema = mongoose.Schema({
news_id:{
type:Number,
// 唯一索引
unique: true,
},
title: {
type:String,
// 普通索引
index: true
},
author: String,
content: {
type: String,
/*words的长度要大于8且小于20*/
validate: function(words){
return (words.length>=8 && words.length<=20)
}
}
});
复制代码
添加数据时:
const news = new News({
news_id: 2,
title: '新闻2',
author: 'joyitsai',
words: 'qwert'
})
news.save((err, docs)=>{
if(err) return console.log(err);
console.log(docs);
})
复制代码
提示报错:
{ ValidationError: News validation failed: words: Validator failed for path `words` with value `qwert`
......
}
复制代码
如果添加的words数据长度在8-20之间,就没问题了:
{ _id: 5cfd15eacf0cee3f14f5bb85,
news_id: 2,
title: '新闻2',
author: 'joyitsai',
words: 'qwertyuio',
__v: 0 }
复制代码
MongoDB基础八:Mongodb自增id实现方法
本文实例讲述了Mongodb自增id实现方法,具体如下:
首先创建一个自动增长id集合 ids
>db.ids.save({name:"user", id:0});
复制代码
可以查看一下是否成功
> db.ids.find();
{ "_id" : ObjectId("4c637dbd900f00000000686c"), "name" : "user", "id" : 0 }
复制代码
然后每次在db.user集合里添加新用户之前,添加新用户之前自增一下 ids集合 获得id:将db.ids集合中的name=”user”文档的id值加1,返回文档。
注:因为findAndModify是一个方法完成更新查找两个操作,所以具有原子性,多线程不会冲突。
然后保存相应的数据
>db.user.save({uid:userid.id, username:"dotcoo", password:"dotcoo"});
复制代码
将上面两端代码合并:
>db.user.save({
uid: db.ids .findAndModify({
update:{$inc:{'id':1}},
query:{"name":"user"},
new:true}).id,
username: "dotcoo",
password:"dotcoo"});
复制代码
查询一下刚刚添加的用户数据:
> db.user.find();
{ "_id" : ObjectId("4c637f79900f00000000686d"), "uid" : 1, "username" : "admin", "password" : "admin" }
复制代码
注意:
如果你是通过mongoose模块来进行上面的操作,应使用model.findOneAndUpdate()方法对ids集合的id进行自增,另外,在新版本中,需要在mongoose.connect()时配置useFindAndModify: false,,具体原因如官方文档所提:
在js项目中使用mongoose模块实现id自增的具体代码如下:
const IDs = require('./dataModel/ids');
const Adminuser = require('./dataModel/adminuser');
IDs.findOneAndUpdate({name: 'user'}, {$inc: {id: 1}}, {new: true,}, (err, docs)=>{
if(err) return err;
console.log(docs);
const adminuser = Adminuser({
user_id: docs.id,
username: 'admin'+docs.id,
password: '12345678'
});
adminuser.save((err, docs)=>{
if(err) return err;
console.log(docs);
})
})
复制代码
{new: true}选项,会将更新后的文档输出。
以守护进程方式(Linux系统),开机自启动Mongodb服务
1. 编辑mongodb的配置文件:
修改/opt/mongo/bin/mongodb.conf配置文件,在/etc/init.d中编写自启动脚本时,需要让mongodb按照配置文件来启动和关闭:
/*数据存放目录*/
dbpath=/opt/mongo/data/db
/*指定日志文件,该文件将保存所有的日志记录、诊断信息*/
logpath=/opt/mongo/log/mongodb.log
/*进程ID,没有指定则启动时候就没有PID文件*/
pidfilepath=/opt/mongo/db.pid
//设置为true,修改数据目录存储模式,每个数据库的文件存储在DBPATH指定目录的不同的文件夹中。
//使用此选项,可以配置的MongoDB将数据存储在不同的磁盘设备上,以提高写入吞吐量或磁盘容量。
directoryperdb=true
//写日志的模式:设置为true为追加。默认是覆盖。如果未指定此设置,启动时MongoDB的将覆盖现有的日志文件。
logappend=true
//绑定地址
bind_ip= localhost
/*端口。默认27017*/
port=27017
//是否后台运行,设置为true 启动进程在后台运行的守护进程模式。
fork=true
//来禁用预分配的数据文件,会缩短启动时间,但在正常操作过程中,可能会导致性能显著下降。
noprealloc=true
//设置为true,使用较小的默认数据文件大小。smallfiles减少数据文件的初始大小,并限制他们到512M,也减少了日志文件的大小,
//并限制他们到128M。如果数据库很大,各持有少量的数据,会导致mongodb创建很多文件,会影响性能。
smallfiles=true
复制代码
- 创建mongodb服务脚本
文件夹/etc/init.d/是用来放服务脚本的,当Linux启动时,会寻找这些目录中的服务脚本,并根据脚本的run level确定不同的启动级别。
在/etc/init.d/中创建mongodb服务脚本文件,编辑内容如下
#!/bin/bash
#
#chkconfig: 2345 80 90
#description: mongodb
start() {
/opt/mongo/bin/mongod --config /opt/mongo/bin/mongodb.conf
}
stop() {
/opt/mongo/bin/mongod --config /opt/mongo/bin/mongodb.conf --shutdown
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo
$"Usage: $0 {start|stop|restart}"
exit 1
esac
复制代码
上面脚本,是让mongodb脚本服务,可以通过service mongodb start来实现mongod服务按照配置文件启动;通过service mongodb stop来实现mongod服务按照配置文件停止;还有service mongodb restart实现mongod服务重启。
增加服务并开机启动:
chmod +x /etc/init.d/mongodb
chkconfig --add mongodb
chkconfig --level 345 mongodb on
chkconfig --list mongodb
service mongodb start
复制代码
作者:自如大前端研发中心-FE研发部架构组-冯腾飞
招聘信息
自如大前端研发中心招募新同学!
FE/IOS/Android工程师
公司福利有:
- 全额五险一金,并额外购买商业保险
- 免费健身房+年度体检
- 公司附近租房9折优惠
- 晚间打车报销
- 每年2次晋升机会
欢迎对技术有执着热爱的你加入我们!简历请投递 zhangxl122@ziroom.com, 或加微信 v-nice-v 详聊!