應用Mongoose開發MongoDB(2)模型(models)

來源:互聯網
上載者:User

標籤:rtt   屬性   對象   同名   function   modules   one   oms   模式   

資料模型及基礎操作模板

 

為了使工程結構清晰,將資料模型(Schema, Model)的建立與增刪查改的基礎操作模板寫在一起,命名為資料庫設計中的Collection(對應於關係型資料庫中的表定義)名,並儲存在models檔案夾中。

 

Schema與Model的建立:

Schema是Mongoose裡的資料模式,可以理解為表結構定義;每個Schema會映射到MongoDB中的一個Collection,不具備操作資料庫的能力。

考慮以下代碼:

//引入mongoose模組var mongoose = require(‘mongoose‘);//以json對象形式定義Schemavar 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         }}); //匯出Modelvar 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對象形式定義Schemavar 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.htmlvar 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: Regex,產生驗證器判斷值是否符合給定的Regex

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時直接省略而不用Null 字元串佔位。但事實上,在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)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.