应用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()函数时的某些情况下需要进行必要的输出验证,否则会报错引起程序崩溃。

时间: 2024-10-14 06:02:47

应用Mongoose开发MongoDB(2)模型(models)的相关文章

应用Mongoose开发MongoDB(3)控制器(controllers)

控制器的基本构成与如何通过路由调用 控制器中通过建立函数并导出,实现前端对数据库的查询.新建.删除与修改的需求,并使之可以在路由中调用,完成API的封装.本文着重于结构之间的关系,具体问题解决方法将在后文给出. 下面代码就是一个简单但完整的控制器文件,命名为comment_controller.js, 存放在~/controllers文件夹下: var config = require('../config'), Comment = require('../models/comment'); /

nodejs(一) 简单登录验证 使用mongoose 操作MongoDB

---恢复内容开始--- 开发使用webstorm 9  新建nodejs+express 项目 newfarmer 文章目录 配置Mongoose 创建目录及文件 插入数据,POST提交JSON增加一条记录 查询数据,取出刚增加的记录 1. 配置Mongoose 增加mongoose的类库 npm install mongoose --save 2.创建目录及文件 在models目录,增加mongodb.js文件 数据库连接文件1 /** 2 * Created by hao on 2014/

在BAE中使用mongoose操作mongodb

在BAE中使用mongoose操作mongodb 几乎网上所有的使用mongoose的例子都是长连接的方式,因为nodejs本身机制的关系,使用长连接一定程度上会提高性能,但是bae的免费mongodb不支持长链接,之前也有很多人做了尝试,比如断开后捕捉错误,然后自动重连的方式,现在来看都不太成功,于是还是中规中矩的使用短链接的方式来做吧,这里实现了一个连接打开和关闭的托管. mongoosekeeper.js 'use strict'; var mongoose = require('mong

Django基础之模型(models)层

目录 Django基础之模型(models)层 一 ORM简介 二.单表查询 Queryset队像和mployee对象 神奇的双下划线的模糊查询 聚合查询 分组查询 F与Q查询 F查询: Q查询 Q查询进阶 查询优化(面试) only与defer select_related与prefetch_related Django ORM中的事务操作 补充知识: Django基础之模型(models)层 Django测试环境搭建:拷贝manage.py中的行代码放到tests.py文件中导入模块 imp

node 通过mongoose实现 mongodb的增删改

node 通过mongoose实现 mongodb的增删改 新建文件test.js 内容如下: var mongoose = require('mongoose') , Schema = mongoose.Schema; mongoose.connect('mongodb://localhost/test'); var BlogSchema = new Schema({ id : {type : Number, index : true} ,title : {type : String} });

Unity3D游戏开发之动画模型导入

Unity3D游戏开发之动画模型导入 导入动画文件后,在工程(Porject)面板中选中,通过Inspector面板修改参数设置. 在项目视图中选中模型文件.如果想用旧版3.x的动画系统导入和编辑动画,请选择legacy选项.如果你有一个人性角色,比如,一个脑袋两条胳膊的两足动物,请选择Humanoid和'Create from this model',这样,一个完美匹配你角色骨骼关系的替身就可以被创建,或者你也可以选择其他预设好的替身. 如果你的角色不是人形物体,比如一个四足动物或者任何其它的

CSS3实战开发: 弹性盒模型之响应式WEB界面设计

各位网友大家好,如果你已经阅读过我先前写的关于CSS3弹性盒模型的实例演示,相信你对CSS3弹性盒模型属性知识点应该已经有了一个非常深刻的映像. 从本篇文章开始,我将带领大家,手把手地教大家如何来开发一个适合移动端浏览器的弹性盒模型的响应式页面.同时实战开发中的案例代码可以作为你项目中的精简框架了. 当你学习完成<CSS3实战开发: 弹性盒模型之响应式WEB界面设计>这个系列教程之后,相信你对目前比较流行的前端轻量级框架 Bootstrap等会有一个深刻的认识. Bootstrap(弹性流体布

Power Management开发的一般模型

本文作为一个提供挈领的介绍性文档,后面会以此展开,逐渐丰富. 一般开发模型 针对一个PM feature进行开发,设计模型是第一步.模型设计好之后,还要保留参数接口,可以基于这些参数针对特殊个体进行优化. 建立一个可以快速迭代.准确可靠.可量化的验证环境尤其重要.一方面可以验证设计的模型是否有效.正确:另一方面还可以调整找到最适合的参数. 针对嵌入式设备来说,最主要的是达到性能和功耗的平衡:在满足性能要求的前提下,尽量降低功耗. 这就涉及到如何去量化性能(Performance)和功耗(Powe

nodejs 使用mongoose 操作mongodb

nodejs操作mongodb可以使用mongoose: Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. 安装mongoose: npm install mongoose ///获取mongodb连接var conn = mongoose.connect('mongodb://localhost/mytest');      var Schema = mongo