mongoose

博客分类: 原创

mongoose

最近偶感浮躁之风。看了了不起的nodejs之后觉得这个更偏向于应用,读完此书,要建立一个后端系统是可以的,但是不可深究,对于node的基础和底层完全没有深入,于是觉得继续学习深入浅出nodejs

自己搭建了一个后端工程,数据库用的是mongoDB,所以觉得使用mongoose应该挺重要,而且在使用的时候也踩了很多坑。简单说一下API

mongoDB的安装及认证

在公司的开发机上布了mongoDB之后接到通知,说数据库没有认证不安全,要罚钱,微薄的工资听说要罚钱,立马提起尿湿了的裤子开始研究怎么做认证。数据库的安装就不说了。

// 启动数据库
mongod
// 新开bash,并连接数据库
mongo
// 切换到admin数据库
use admin
// 添加数据的方法由于数据库版本不一样,可能提供的方法也不一样。最好看自己的版本的数据库提供的什么API
db.addUser('karynsong','karyn13990')
// 查询添加用户
db.system.users.find()
{ '_id' : ObjectId('55f9542a0254ba1847790efc'), 'user' : 'karynsong', 'readOnly' : false, 'pwd' : '634a96191ae02cbdcfb3b126431cf2ef' }
// 用户认证
db.auth('karynsong','karyn13990')
// 此时就完成了在 admin 数据库添加用户的操作,重启数据库,并带上认证标志
mongod --auth
// 直接登录数据库也是可以登录的,但是所有操作都会说没有权限,带上用户名和密码登录
mongo 127.0.0.1/admin -ukarynsong -pkaryn13990
// 此时在数据库上的操作才是可执行的

如果需要在其他数据库上添加认证的话。需要先登录admin数据库,然后切到想要添加认证的数据上,addUser,auth,再携带用户名密码登录数据库。就行了

Mongoose

Mongoose就是一套操作MongoDB数据库的接口,是为了简化对于mongoDB的操作。

基础知识

  • Schema:一种以文件形式存储的数据库模型骨架,无法直接通往数据库端,也就是说它不具备对数据库的操作能力.可以说是数据属性模型(传统意义的表结构),又或着是“集合”的模型骨架。
  • Model:由 Schema 构造生成的模型,除了 Schema 定义的数据库骨架以外,还具有数据库操作的行为,类似于管理数据库属性、行为的类。
  • Entity:由 Model 创建的实体,使用 save 方法保存数据,Model 和 Entity 都有能影响数据库的操作,但 Model 比 Entity 更具操作性。
  • ObjectId:存储在 mongodb 集合中的每个文档(document)都有一个默认的主键 _id,这个主键名称是固定的,它可以是 mongodb 支持的任何数据类型,默认是 ObjectId。
  • 索引:数据量巨大的时候建立适当的索引会优化搜索效率。

数据库连接

在使用mongoose时不用担心数据库有没有连接上,连接上之前对数据库有操作,mongoose会缓存起来,知道数据库连接上之后做数据库的操作。

// 引入对 mongoose 的依赖
var mongoose = require('mongoose'),
// 数据库链接 karynsong 用户名 karyn13990 密码 localhost:27017 数据库地址 test 数据库名字
    db = 'mongodb://karynsong:karyn13990@localhost:27017/test';
// 数据库连接
mongoose.connect(db);
// 挂在数据库操作 连接后的回调
mongoose.connection.on('connected', callback);
// 错误回调
mongoose.connection.on('error', callback);
// 断开回调
mongoose.connection.on('disconnected', callback);

scheme

简单的来理解,就是创建要存入数据库的格式。在这个时候如果定义索引会再负载数据时变得更。

var Schema = mongoose.Schema;
var AnimalSchema = new Schema({
    name: String,
    type: String,
    tags: { type: [String], index: true } // field level 单索引
});

AnimalSchema.index({ name: 1, type: -1 }); // schema level 复合索引,复合索引只能建在schema上。1和-1代表升序降序

在开发环境时可以不用建索引,AnimalSchema.set('autoIndex', false);这样可以避免建立索引,但在线上要把这个东西去掉。

scheme支持的类型有五种

  • Date
  • String
  • Number
  • Array
  • Object

model

创建好了scheme之后,就可以创建model了,我的理解是,穿件model是指将数据存到哪张表中。

var AnimalModel = mongoose.model('Animal', AnimalSchema);

上面个这句话就是创建一个AnimalModel,这个model创建自AnimalSchema,将会被存在Animal表中,这里虽然是大写,但到了数据库中,对应着,表名为animal的表。

同时,model还可以具有一些静态化的方法。和自带的remove、find等方法差不多。比如:

AnimalSchema.statics.findByName = function(name, cb){
    this.find({name: new RegExp(name, 'i')}, cb);
}
var AnimalModel = mongoose.model('Animal', AnimalSchema);
AnimalModel.findByName('dog', function(err, animals){
    console.log(animals);
});

entity

创建好Model之后,用new的方式创造出一个数据实例。

var animalData = new AnimalModel({
    name: '旺财',
    type: '狗',
    tags: ['犬科']
});

animalData其实就是entity

API

model会自带一些方法。

// 保存该数据对象
animalData.save(data, callback);
// 在 model 中查找数据
AnimalModel.find({}, callback);
// 在 model 中查找数据,得到第一个数据即返回
AnimalModel.findOne({}, callback);
// 在 model 中根据 id 查找数据,只接受一个参数
AnimalModel.findById('obj._id', callback);
// 在 model 中插入数据,批量,data 不需要 entity
AnimalModel.create(data, callback);
// 在 model 中更新数据
AnimalModel.update(conditions, update, function(error);
// 在 model 删除数据
AnimalModel.remove(conditions,callback);
// 在 model 统计数据总条数,用于做分页非常方便
AnimalModel.count(callback);

更新修改器

为了更快进行更新的操作。在update中需要写很多选择器

$inc增减修改器,只对数字有效.下面的实例: 找到 age=22的文档,修改文档的age值自增1

Model.update({‘age’:22}, {‘$inc’:{‘age’:1} } );

执行后: age=23

$set指定一个键的值,这个键不存在就创建它.可以是任何MondoDB支持的类型

Model.update({‘age’:22}, {‘$set’:{‘age’:’haha’} } );

执行后: age=’haha’

$unset同上取反,删除一个键

Model.update({‘age’:22}, {‘$unset’:{‘age’:’haha’} } );

执行后: age键不存在

$push给一个键push一个数组成员,键不存在会创建

Model.update({‘age’:22}, {‘$push’:{‘array’:10} } );

执行后: 增加一个 array 键,类型为数组, 有一个成员 10

$addToSet向数组中添加一个元素,如果存在就不添加

Model.update({‘age’:22}, {‘$addToSet’:{‘array’:10} } );

执行后: array中有10所以不会添加

$each遍历数组, 和 $push 修改器配合可以插入多个值

Model.update({‘age’:22}, {‘$push’:{‘array’:{‘$each’: [1,2,3,4,5]}} } );

执行后: array : [10,1,2,3,4,5]

$pop向数组中尾部删除一个元素

Model.update({‘age’:22}, {‘$pop’:{‘array’:1} } );

执行后: array : [10,1,2,3,4] tips: 将1改成-1可以删除数组首部元素

$pull向数组中删除指定元素

Model.update({‘age’:22}, {‘$pull’:{‘array’:10} } );

执行后: array : [1,2,3,4] 匹配到array中的10后将其删除

条件查找

Model.find({'age':{ '$get':18 , '$lte':30 } } );

  • ‘$lt’ 小于
  • ‘$lte’ 小于等于
  • ‘$gt’ 大于
  • ‘$gte’ 大于等于
  • ‘$ne’ 不等于
  • ‘$in’ 一个键对应多个值
  • ‘$nin’ 同上取反, 一个键不对应指定值
  • ‘$or’ 多个条件匹配, 可以嵌套 $in 使用
  • ‘$not’ 同上取反, 查询与特定模式不匹配的文档

正则表达式

find( {'name' : /karyn/i } );

数组查询

确切的值查找

Model.find({‘array’:10} );

查询 array(数组类型)键中有10的文档, array : [1,2,3,4,5,10] 会匹配到

Model.find({‘array[5]’:10} );

查询 array(数组类型)键中下标5对应的值是10, array : [1,2,3,4,5,10] 会匹配到

$all匹配数组中多个元素

Model.find({‘array’:[5,10]} );

查询 匹配array数组中 既有5又有10的文档

$size匹配数组长度

Model.find({‘array’:{‘$size’ : 3} } );

查询 匹配array数组长度为3 的文档

$slice查询子集合返回

Model.find({‘array’:{‘$skice’ : 10} } );

查询 匹配array数组的前10个元素

Model.find({‘array’:{‘$skice’ : [5,10] } } );

查询 匹配array数组的第5个到第10个元素

where 查询

// this 是指每条数据
find( {"$where" :  "this.x + this.y === 10" } );

exec

mongoose查询语句支持链式调用,所以可以非常方便的写成一句话的调用,但回调函数怎么办呢?我们一般采用exec的方式接在后面。格式如下:

// 回调的方式
Model.find(query, function(){ });
// exec 的方式,可以完全链式调用
Model.find(query).exec(function(){})

limit

// 获取查询结果的前十条,得到十条就返回
Model.find(query).limit(10);

sort

// 对查询结果进行排序
Model.find(query).sort('name': 1);

select

// 对查询结果的字段进行赛选
Model.find(query).select('name age');

skip

// 跳过10条查询结果,可做分页
Model.find(query).skip(10);

搜索实例

// 复杂的搜索条件可以简化之后对于查询结果的处理
Person.
    find({
        occupation: /host/,
        'name.last': 'Ghost',
        age: { $gt: 17, $lt: 66 },
        likes: { $in: ['vaporizing', 'talking'] }
    }).
    skip(5).
    limit(10).
    sort({ occupation: -1 }).
    select({ name: 1, occupation: 1 }).
    exec(callback);

//上面的复杂查询条件可以简化成下面这种写法,这样的好处是,如果建了索引,搜索会更简单
Person.
    find({ occupation: /host/ }).
    where('name.last').equals('Ghost').
    where('age').gt(17).lt(66).
    where('likes').in(['vaporizing', 'talking']).
    skip(5).
    limit(10).
    sort('-occupation').
    select('name occupation').
    exec(callback);

总结

系统的对mongoose进行了解之后,会发现自己之前的使用代码有多么戳比。在不清楚API的情况下,贸然使用,会复杂化自己的操作。写完这个之后发现自己的浮躁的感觉少一点了。学习就应该系统一点。接着看书吧。