应用Mongoose开发MongoDB(2)模型(models)
数据模型及基础操作模板
为了使工程结构清晰,将数据模型(Schema, Model)的建立与增删查改的基础操作模板写在一起,命名为数据库设计中的Collection(对应于关系型数据库中的表定义)名,并存储在models文件夹中。
Schema与Model的建立:
Schema是Mongoose里的数据模式,可以理解为表结构定义;每个Schema会映射到MongoDB中的一个Collection,不具备操作数据库的能力。
考虑以下代码:
//引入mongoose模块
var mongoose = require('mongoose');
//以json对象形式定义Schema
var taskSchema = new mongoose.Schema({
userId: String,
invalidFlag:Number,
task: [
{
_id:0,
type: {type:String},
details:[{
startTime : Date,
frequencyTimes : Number,
frequencyUnits : String,
status:Number
}]
}
],
revisionInfo:{
operationTime:Date,
userId:String
}
}); //导出Model
var taskModel = mongoose.model('task', taskSchema);
这就定义了一个Schema和Model,映射到MongoDB中的一个Collection。实际操作过程中,需要注意以下几点:
1. 命名规范:首字母小写,如果命名中有多个单词,第一个单词首字母小写,其他单词首字母大写。关于这一点,是本文这一系列的默认习惯规范,不同开发者有不同习惯。
2. 定义Schema时以json对象形式定义,键为属性,值为属性说明,关于属性说明,至少需要定义属性的类型(即type),如果有其他需要说明的,同样以json的形式说明,键为属性,值为说明。
3. Schema.Types: 可用的Schema Types有8种,其中String, Number, Date, Buffer, Boolean直接定义即可;Mixed, ObjectId需要引入mongoose模块后定义;array使用中括号加元素Type定义,不说明也可以。Mixed类型可以看做嵌套类型,可以不指定内部元素的键,若需要指定内部元素的键,可以直接使用大括号声明(如上的’revisionInfo’)
//引入mongoose模块
var mongoose = require('mongoose');
//以json对象形式定义Schema
var taskSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId, //主键
doctor_id: {type: mongoose.Schema.Types.ObjectId, ref:’doctor’}, //外键链接到“doctor”
content: mongoose.Schema.Types.Mixed //混合或嵌套类型
});
4. 在定义Schema时的其他操作:
a) 对于全部Type有效:
required: boolean或function. 如果布尔值为真则会对模型进行验证。
default: 设置属性的默认值,可以是value或者function。
select: boolean 查询时默认输出该属性。
validate: function, 对属性进行自定义验证器。
get, set: function, 自定义属性的值
//get, set使用例子
//参考: http://mongoosejs.com/docs/schematypes.html
var numberSchema = new Schema({
integerOnly: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v)
}
}); var Number = mongoose.model('Number', numberSchema); var doc = new Number();
doc.integerOnly = 2.001;
doc.integerOnly; // 2
b) 索引Indexes
index: Boolean 属性是否索引
unique: Boolean 是否唯一索引
sparse: Boolean 是否稀疏索引:稀疏索引,如果索引键中存储值为null,就跳过这个文档,这些文档将不会被索引到。不过查询时默认是不使用稀疏索引的,需要使用hint()指定使用在模型中建立的稀疏索引。
c) 对字符串String有效
lowercase: Boolean 转成小写,即对值调用.toLowerCase()
uppercase: Boolean 转成大写,即对值调用.toUpperCase()
trim: Boolean 去掉开头和结尾的空格,即对值调用.trim()
match: 正则表达式,生成验证器判断值是否符合给定的正则表达式
enum: 数组,生成验证器判断值是否在给定的数组中
d) 对数字Number或时间Date有效
min, max: Number或Date 生成验证器判断是否符合给定条件
5. 注意:
声明Mixed类型时,以下几种方式是等价的:
//引入mongoose模块
var mongoose = require('mongoose'); //声明Mixed类型
var Any = new Schema({ any: {} });
var Any = new Schema({ any: Object });
var Any = new Schema({ any: mongoose.Schema.Types.Mixed});
关于数组(Array):
a) 声明:
//引入mongoose模块
var mongoose = require('mongoose'); //声明类型为Mixed的空数组
var Empty1 = new Schema({ any: [] });
var Empty2 = new Schema({ any: Array });
var Empty3 = new Schema({ any: [mongoose.Schema.Types.Mixed] });
var Empty4 = new Schema({ any: [{}] });
b) 默认属性:
数组会隐式地含有默认值(default: []),要将这个默认值去掉,需要设定默认值(default: undefined)
如果数组被标记为(required: true),存入数据时该数组必须含有一个元素,否则会报错。
6. 自定义Schema Type:
从mongoose.SchemaType继承而来,加入相应的属性到mongoose.Schema.Type中,可以使用cast()函数实现,具体例子参见:
http://mongoosejs.com/docs/customschematypes.html
7. Schema Options:对Schema进行的一系列操作,因为我没有验证过,就不细说了。
参考 http://mongoosejs.com/docs/guide.html
=========================================================================
在这个文件中,除了导出和编译数据模型外,另外建立了数据库增删查改的基础方法,生成函数,导出模块供其他文件调用。
仍然以上文中的../models/task.js文件作为示例:
//设置collection同名函数,并导出模块
function Task(task) {
this.task = task;
} //添加基本的增删查改操作函数模板
//... module.exports = Task;
增:
Task.prototype.save = function(callback) {
var task = this.task;
var newTask = new taskModel(task);
newTask.save(function(err, taskItem) {
if (err) {
return callback(err);
}
callback(null, taskItem);
});
}
需要注意的是,数据库文档存储方法是在Task原型链上修改,使用save()函数实现。在进行数据存储的操作过程中,首先从原型对象生成实例,这里原型对象就是所要存储的文档。完成从原型对象生成实例的操作,使用new运算符实现,然而new运算符无法共享属性和方法,save()函数恰恰是需要共享的方法,因此使用prototype来设置一个名为save()的函数作为文档的通用方法。
删:
与增加方法不同,删除、查找及修改方法直接在Task增加方法,因为这些方法是对模型进行操作,而模型的方法已在node_modules/mongoose/lib/model.js内定义。
与删除有关的方法:
//删除第一个匹配conditions的文档,要删除所有,设置'justOne' = false
remove(conditions, [callback]);
//删除第一个匹配conditions的文档,会忽略justOne操作符
deleteOne(conditions, [callback]);
//删除所有匹配conditions的文档,会忽略justOne操作符
deleteMany(conditions, [callback]);
//实现MongoDB中的findAndModify remove命令,并将找到的文档传入callback中
//options: 'sort', 'maxTimeMS', 'select'
findOneAndRemove(conditions, [options], [callback]);
//以主键作为查询条件删除文档,并将找到的文档传入callback中
findByIdAndRemove(id, [options], [callback]);
Task.removeOne = function(query, callback, opts) {
var options = opts || {}; taskModel
.findOneAndRemove(query, options, function(err, task) {
if (err) {
return callback(err);
}
callback(null, task);
});
};
这个例子中,将导出的函数取名为Task.removeOne(), 在传入参数时,将[option]放到了最后,这样做的本意,是因为实际应用时,options往往是空的,不需要传入,这样做就可以在写controller时直接省略而不用空字符串占位。但事实上,在model.js中定义时,已经做了处理:conditions必须传入,且不能为function, 当第二个参数options是function时,将这个function认为是callback, 并将options设置为undefined
if (arguments.length === 1 && typeof conditions === 'function') {
var msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n'
+ ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
+ ' ' + this.modelName + '.findOneAndRemove(conditions)\n'
+ ' ' + this.modelName + '.findOneAndRemove()\n';
throw new TypeError(msg);
} if (typeof options === 'function') {
callback = options;
options = undefined;
}
改:
与修改有关的方法:
//更新文档而不返回他们
//option: ‘upsert’: if true, 如果没有匹配条件的文档则新建
//option: ‘multi’: if true, 更新多文档
//option: ‘runValidators’, if true, 在更新之前进行模型验证
//option: ‘setDefaultsOnInsert’, 如果此操作符与’upsert’同时为true, 将schema中的默认值新建到新文档中
//注意不要使用已存在的实例作为更新子句,有可能导致死循环
//注意更新子句中不要存在_id字段,因为MongoDB不允许这样做
//使用update时,值会转换成对应type, 但是defaults, setters, validators, middleware不会应用,如果要应用这些,应使用findOne()然后在回调函数里调用.save()函数
update(conditions, doc, [options], [callback]);
//忽略multi操作符,将所有符合conditions的文档修改
updateMany(conditions, doc, [options], [callback]);
//忽略multi操作符,仅将第一个符合conditions的文档修改
updateOne(conditions, doc, [options], [callback]);
//使用新文档替换而不是修改
replaceOne(conditions, doc, [options], [callback]);
//找到匹配的文档,并根据[update]更新文档,将找到的文档传入[callback]
//option: ‘new’: if true,返回更新后的文档
//’upsert’, ‘runValidators’, ‘setDefaultsOnInsert’, ’sort’, ‘select’等操作符也可用
findOneAndUpdate([conditions], [update], [options], [callback]);
//通过主键找到匹配的文档,并根据[update]更新文档,将找到的文档传入[callback]
findByIdAndUpdate(id, [update], [options], [callback]);
Task.updateOne = function(query, obj, callback, opts, populate) {
var options = opts || {};
var populate = populate || ''; taskModel
.findOneAndUpdate(query, obj, options)
.populate(populate)
.exec(function(err, uptask) {
if(err){
return callback(err);
}
callback(null, uptask);
});
}; Task.update = function(query, obj, callback, opts, populate) {
var options = opts || {};
var populate = populate || ''; taskModel
.update(query, obj, options)
.populate(populate)
.exec(function(err, uptask) {
if(err){
return callback(err);
}
callback(null, uptask);
});
};
与删除方法不同,callback不传入.update()或.findOneAndUpdate()中,而在之后调用了.exec()中传入了一个回调函数,如果err有内容则返回err, 否则返回uptask,也就是MongoDB的返回。这样的处理,可以不需要等待MongoDB的响应。
populate是联表查询时使用的参数,将在之后的内容提到。
查:
与查询有关的方法:
//conditions会在命令发送前自动被转成对应的SchemaTypes
find(conditions, [projection], [options], [callback]);
//通过_id查询到一条文档
findById(id, [projection], [options], [callback]);
//查询一条文档,如果condition = null or undefined, 会返回任意一条文档
findOne([conditions], [projection], [options], [callback]);
Task.getOne = function(query, callback, opts, fields, populate) {
var options = opts || {};
var fields = fields || null;
var populate = populate || ''; taskModel
.findOne(query, fields, opts)
.populate(populate)
.exec(function(err, taskInfo) {
if(err){
return callback(err);
}
callback(null, taskInfo);
});
}; Task.getSome = function(query, callback, opts, fields, populate) {
var options = opts || {};
var fields = fields || null;
var populate = populate || '';
taskModel
.find(query, fields, options)
.populate(populate)
.exec(function(err, tasks) {
if(err) {
return callback(err);
}
callback(null, tasks);
});
};
在构造出的.getOne()和.getSome()函数的传入参数中,可以看到option, field, populate在callback后面,因为最基本的情况是只有query和callback传入,而后面的较少用到。而在一些要求复杂的查询中,这三者是必不可少的。
虽然查询最为复杂,不过都是通过.find()与.findOne()与各种操作符组合而成。同样因为最基本的参数是condition与callback, 因此在导出函数时将这两个参数放在最前面。值得注意的是,当查询不到文档时,.findOne()返回null, .find()返回空数组,这使得在调用getOne()函数时的某些情况下需要进行必要的输出验证,否则会报错引起程序崩溃。
应用Mongoose开发MongoDB(2)模型(models)的更多相关文章
- 应用Mongoose开发MongoDB(1)数据库连接
最近因为项目,接触了MongoDB,因为是分工合作,我负责的部分主要是实现前端对数据库增删查改的需求,因此以下内容只着重于针对不同问题如何进行解决. 整个工程的最终目的是通过mongoose编写数据库 ...
- 应用Mongoose开发MongoDB(3)控制器(controllers)
控制器的基本构成与如何通过路由调用 控制器中通过建立函数并导出,实现前端对数据库的查询.新建.删除与修改的需求,并使之可以在路由中调用,完成API的封装.本文着重于结构之间的关系,具体问题解决方法将在 ...
- Node.js开发——MongoDB与Mongoose
为了保存网站的用户数据和业务数据,通常需要一个数据库.MongoDB和Node.js特别般配,因为MongoDB是基于文档的非关系型数据库,文档是按BSON(JSON的轻量化二进制格式)存储的,增删改 ...
- nodejs(一) 简单登录验证 使用mongoose 操作MongoDB
---恢复内容开始--- 开发使用webstorm 9 新建nodejs+express 项目 newfarmer 文章目录 配置Mongoose 创建目录及文件 插入数据,POST提交JSON增加 ...
- mongodb学习(3)--- NodeJs使用mongoose操作mongodb
转载: https://cnodejs.org/topic/50c145ed637ffa4155c7eaee 首先对于以下错误说明(有写 db.close): Error: db object alr ...
- node-express项目的搭建并通过mongoose操作MongoDB实现增删改查分页排序(四)
最近写了一个用node来操作MongoDB完成增.删.改.查.排序.分页功能的示例,并且已经放在了服务器上地址:http://39.105.32.180:3333. Mongoose是在node.js ...
- 在express中使用Mongoose连接MongoDB
为何要学Mongoose? Mongoose是MongoDB的一个对象模型工具,封装了MongoDB对文档的的一些增删改查等常用方法,让NodeJS操作Mongodb数据库变得更加灵活简单. 0.安装 ...
- Backbone.js 为复杂Javascript应用程序提供模型(models)、集合(collections)、视图(views)的结构
Backbone.js 为复杂Javascript应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和 自定义事件:集合附有可枚举函数 ...
- Node使用Mongoose操作MongoDB数据库——增删改查的实现
当初刚出社会时就规划了下自己的职业生涯:先成为一名优秀的前端工程师,再成为一名全栈工程师(精通前端开发.后台开发和客户端开发),最后成为一名优秀的系统架构师.转眼间已经工作快三年,是时候迈出关键性的一 ...
随机推荐
- 自动更新前加密:Clickonce用法
一.加密dll 新建一个windows form application: static void Main(string[] args) { Process. ...
- Link Cut Tree 总结
Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...
- QeePHP View视图的默认变量与新增变量
新版本的QeePHP(V13.1)中,视图页面已经默认添加了几个常用变量 $_app; //控制器所属的应用程序 $_login_user; //登陆用户信息 直接在视图页面直接使用变量即可. 如果 ...
- ubuntu系统问题解决集
1.解决ubuntu 14 system setttings失效的问题 sudo apt-get install unity-control-center 2. 支持root用户登录 修改以下配置文件 ...
- 个推应用统计产品(个数)Android集成实践
前段时间,我们公司的产品又双叒叕给我们提了新需求,要求我们把APP相关的数据统计分析一下,这些指标包括但不限于应用每日的新增.活跃.留存率等等,最好每天都能提供数据报表.这种事情真是想想就麻烦,大家最 ...
- Hadoop生态圈-Kafka常用命令总结
Hadoop生态圈-Kafka常用命令总结 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.管理Kafka服务的命令 1>.开启kafka服务 [yinzhengjie@s ...
- Solr记录-solr文档xml
Solr添加文档(XML) 在上一章中,我们学习解释了如何向Solr中添加JSON和.CSV文件格式的数据.在本章中,将演示如何使用XML文档格式在Apache Solr索引中添加数据. 示例数据 假 ...
- ActiveMQ基础教程JMS概述
什么是JMS JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息 ...
- Docker学习笔记三 Dockerfile 指令 定制镜像
本文地址:https://www.cnblogs.com/veinyin/p/10412079.html 镜像是分层存储的,每一层都是独立存在的,修改当前层并不会修改其依赖的上一层,删除某一层也只是 ...
- MFS - MooseFS 文件系统
MFSMooseFS 文件系统 可以实现RAID 功能:节约成本 实现在线扩展:是一种半分布式文件系统. 一.MFS文件系统的组成 1.mfsmaster 元数据服务器. 在整个体系中负责管理管理文件 ...