名詞解釋
Schema: 一種以文件形式存儲的數據庫模型骨架,不具備數據庫的操作能力
Model: 由Schema編譯而成的假想(fancy)構造器,具有抽象屬性和行為。Model的每一個實例(instance)就是一個document。document可以保存到數據庫和從數據庫返回。
Instance: 由Model創建的實例。
概念解析SQL術語/概念MongoDB術語/概念解釋/說明databasedatabase tablecollection數據庫表/集合rowdocument數據記錄行/文檔columnfield數據字段/域indexindex索引table joins 表連接,MongoDB不支持primary keyprimary key主鍵,MongoDB自動將_id字段設置為主鍵定義Schema
mongoose中任何任何事物都是從Schema開始的。每一個Schema對應MongoDB中的一個集合(collection)。Schema中定義了集合中文檔(document)的樣式。
var mongoose = require('mongoose');var Schema = mongoose.Schema;var blogSchema = new Schema({ title: String, author: String, body: String, comments: [{ body: String, date: Date }], date: { type: Date, default: Date.now }, hidden: Boolean, meta: { votes: Number, favs: Number }});
如果之后想要在Schema中添加鍵,可以使用Schema#add方法。
創造一個model
為了使用schema定義,我們需要轉換blogSchema為一個Model。使用mongoose.model(modelName, schema)。
var BlogModel = mongoose.model('Blog', blogSchema);// 開始吧!
實例方法
Model的實例是document。實例有很多內置的方法,我們也可以給實例自定義方法。
var animalSchema = new Schema({ name: String, type: String });animalSchema.methods.findSimilarTypes = function (cb) { return this.model('Animal').find({ type: this.type }, cb);}
現在所有的動物實例有findSimilarTypes方法。
var AnimalModel = mongoose.model('Animal', animalSechema);var dog = new AnimalModel({ type: 'dog' });dog.findSimilarTypes(function (err, dogs) { console.log(dogs); // woof});
重寫一個默認的實例方法可能會導致不期待的結果。
Statics
給Model添加一個靜態方法也是簡單的。
animalSchema.statics.findByName = function (name, cb) { this.find({ name: new RegExp(name, 'i') }, cb);}var AnimalModel = mongoose.model('Animal', animalSchema);AnimalModel.findByName('fido', function (err, animals) { console.log(animals);});
methods和statics的區別
區別就是一個給Model添加方法(statics),一個給實例添加方法(methods)。下面是stackOverflow的兩個答案。
答案一
答案二
索引
MongoDB支持二級索引,定義索引有兩種方式
路徑級別 schema級別
var animalSchema = new Schema({ name: String, type: String, tags: { type: [String], index: true } // field level});animalSchema.index({ name: 1, type: -1 }); // schema level, 1是正序,-1是倒序
如果要建立復合索引的話,在schema級別建立是必要的。
索引或者復合索引能讓搜索更加高效,默認索引就是主鍵索引ObjectId,屬性名為_id。
數據庫中主要的就是CRUD操作,建立索引可以提高查詢速度。但是過多的索引會降低CUD操作。深度好文如下
http://www.cnblogs.com/huangxincheng/archive/2012/02/29/2372699.html
虛擬屬性
Schema中如果定義了虛擬屬性,那么該屬性將不寫入數據庫。寫入數據庫的還是原來的屬性。
// 定義一個schemavar personSchema = new Schema({ name: { first: String, last: String }});// 編譯var Person = mongoose.model('Person', personSchema);// 創造實例var bad = new Person({ name: { first: 'Walter', last: 'White' }});
我們將名字分成名字和姓,如果要得到全名,我們需要
console.log(bad.name.first + ' ' + bad.name.last); // Walter White
這樣無疑是麻煩的,我們可以通過虛擬屬性的getter來解決這個問題。
personSchema.virtual('name.full').get(function () { return this.name.first + ' ' + this.name.last;});
那么就可以使用bad.name.full直接調用全名了。
反之,如果我們知道虛擬屬性name.full,通過setter也可以得到組成name.full的每一項。
personSchema.virtual('name.full').set(function (name) { var split = name.split(' '); this.name.first = split[0]; this.name.last = split[1];});...mad.name.full = 'Breaking Bad';console.log(mad.name.first); // Breakingconsole.log(mad.name.last); // Bad
配置項
schema有一些配置項可以使用,有兩種方式:
new Schema({...}, options)
var schema = new Schema({...});schema.set(option, value);
有效的配置有:
autoIndex(默認true) capped collection id _id(默認true) read safe(默認true) shardKey strict(默認true) toJSON toObject versionKey typeKey validateBeforeSave skipVersioning timestamps useNestedStrict retainKeyOrderautoIndex–自動索引
應用開始的時候,Mongoose對每一個索引發送一個ensureIndex的命令。索引默認(_id)被Mongoose創建。當我們不需要設置索引的時候,就可以通過設置這個選項。
var schema = new Schema({..}, { autoIndex: false });var Clock = mongoose.model('Clock', schema);Clock.ensureIndexes(callback);
bufferCommands
似乎是說這個(mongoose buffer)管理在mongoose連接關閉的時候重連,如果取消buffer設置,如下:(存疑)
var schema = new Schema({..}, { bufferCommands: false });
capped–上限設置
如果有數據庫的批量操作,該屬性能限制一次操作的量,例如:
new Schema({...},{capped:1024}); //一次操作上線1024條數據
當然該參數也可是對象,包含size、max、autiIndexId屬性
new Schema({...},{capped:{size:1024,max:100,autoIndexId:true}});
collection–集合名字
在MongDB中默認使用Model的名字作為集合的名字,如過需要自定義集合的名字,可以通過設置這個選項。
var schema = new Schema({...}, {collection: 'yourName'});
id
mongoose分配給每一個schema一個虛擬屬性id,它是一個getter。返回的是_id轉換為字符串后的值。如果不需要為schema添加這個getter,可以通過id配置修改。
// 默認行為var pageSchema = new Schema({ name: String });var pageModel = mongoose.model('Page', pageSchema);var p = new pageModel({ name: 'mongodb.org' });console.log(p.id); // '50341373e894ad16347efe01'// 禁止idvar pageSchema = new Schema({ name: String }, { id: false } );var pageModel = mongoose.model('Page', pageSchema);var p = new pageModel({ name: 'mongodb.org' });console.log(p.id); // undefined
_id
在一個schema中如果沒有定義_id域(field),那么mongoose將會默認分配一個_id域(field)。類型是ObjectId。如果不需要使用這個默認的選擇,可以通過設置這個選項。
通過在schema中設置這個字段可以阻止生成mongoose獲得_id。但是在插入的時候仍然會生成_id。設置這個字段之后,如果再使用Schema.set('_id', false)將無效。
// 默認行為var pageSchema = new Schema({ name: String });var pageModel = mongoose.model('Page', pageSchema);var p = new pageModel({ name: 'mongodb.org' });console.log(p); // { _id: '50341373e894ad16347efe01', name: 'mongodb.org' }// 禁用 _idvar pageSchema = new Schema({ name: String }, { _id: false });// schema構造器設置之后,不要再像下面這樣設置// var schema = new Schema({ name: String });// schema.set('_id', false);var PageModel = mongoose.model('Page', pageSchema);var p = new pageModel({ name: 'mongodb.org' });console.log(p); // { name: 'mongodb.org' }// 當插入的時候,MongoDB將會創建_idp.save(function (err) { if (err) return handleError(err); pageModel.findById(p, function (err, doc) { if (err) return handleError(err); console.log(doc); // { name: 'mongodb.org', _id: '50341373e894ad16347efe12' } })})
為什么不建議使用set
read
允許在schema級別設置query#read,對于所有的查詢,提供給我們一種方法應用默認的ReadPreferences。
safe
這個配置會在MongoDB所有的操作中起作用。如果設置成true就是在操作的時候要等待返回的MongoDB返回的結果,比如update,要返回影響的條數,才往后執行,如果safe:false,則表示不用等到結果就向后執行了。
默認設置為true能保證所有的錯誤能通過我們寫的回調函數。我們也能設置其它的安全等級如:
{ j: 1, w: 2, wtimeout: 10000 }
表示如果10秒內寫操作沒有完成,將會超時。
關于j和w,這里有很好的解釋。
http://kyfxbl.iteye.com/blog/1952941
shardKey
需要mongodb做分布式,才會使用該屬性。
strict
默認是enabled,如果實例中的域(field)在schema中不存在,那么這個域不會被插入到數據庫。
var ThingSchema = new Schema({a:String});var ThingModel = db.model('Thing',SchemaSchema);var thing = new Thing({iAmNotInTheThingSchema:true});thing.save();//iAmNotInTheThingSchema這個屬性將無法被存儲
// 通過doc.set()設置也會受到影響。var thingSchema = new Schema({..})var Thing = mongoose.model('Thing', thingSchema);var thing = new Thing;thing.set('iAmNotInTheSchema', true);thing.save(); // iAmNotInTheSchema is not saved to the db
如果取消嚴格選項,iAmNotInTheThingSchema將會被存入數據庫
var thingSchema = new Schema({..}, { strict: false });var thing = new Thing({ iAmNotInTheSchema: true });thing.save(); // iAmNotInTheSchema is now saved to the db!!
該選項也可以在Model級別使用,通過設置第二個參數,例如:
var ThingModel = db.model('Thing');var thing1 = new ThingModel(doc,true); //啟用嚴格var thing2 = new ThingModel(doc,false); //禁用嚴格
strict也可以設置為throw,表示出現問題將會拋出錯誤而不是拋棄不合適的數據。
注意:
- 不要設置為false除非你有充分的理由。
在mongoose v2里默認是false。
在實例上設置的任何鍵值對如果再schema中不存在對應的,將會被忽視。
var thingSchema = new Schema({..})var Thing = mongoose.model('Thing', thingSchema);var thing = new Thing;thing.iAmNotInTheSchema = true;thing.save(); // iAmNotInTheSchema 不會保存到數據庫。
toJSON
和toObject類似,選擇這個選項為true后,但是只有當實例調用了toJSON方法后,才會起作用。
var schema = new Schema({ name: String });schema.path('name').get(function (v) { return v + ' is my name';});schema.set('toJSON', { getters: true, virtuals: false });var M = mongoose.model('Person', schema);var m = new M({ name: 'Max Headroom' });console.log(m.toObject()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom' }console.log(m.toJSON()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }console.log(JSON.stringify(m)); // { "_id": "504e0cd7dd992d9be2f20b6f", "name": "Max Headroom is my name" }
可以看出,配置屬性name對toObject沒影響,對toJSON有影響。
toObject
選擇這個選項為true后,默認對這個schema所有的實例都有作用。不需要實例手動調用。
ar schema = new Schema({ name: String });schema.path('name').get(function (v) { return v + ' is my name';});schema.set('toObject', { getters: true });var M = mongoose.model('Person', schema);var m = new M({ name: 'Max Headroom' });console.log(m); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }
較上面不同的是,沒有virtuals: false這個設置。
typeKey
在mongoose里,如果schema里有個對象,并且這個對象有個type鍵,mongoose將會將這個作為一種類型聲明。
// Mongoose 認為loc字段的類型是一個字符串,而不是有type這個字段 var schema = new Schema({ loc: { type: String, coordinates: [Number] } });
然而,對于一些應用來說,type字段是必要的。那么可以通過typeKey來設置。
var schema = new Schema({ // Mongoose 這時候認為loc字段有兩個鍵,一個是type,一個是coordinates loc: { type: String, coordinates: [Number] }, // Mongoose 這時候認為name字段的類型是字符串。 name: { $type: String }}, { typeKey: '$type' }); // '$type'鍵意味著這是一個類型宣告,而不是默認的type
validateBeforeSave
默認得,文檔被保存到數據庫的時候會自動驗證,這是為了防止無效的文檔。如果想要手動處理驗證,并且能保存不通過驗證的文檔,可以設置這個選項為false。
var schema = new Schema({ name: String });schema.set('validateBeforeSave', false);schema.path('name').validate(function (value) { return v != null;});var M = mongoose.model('Person', schema);var m = new M({ name: null });m.validate(function(err) { console.log(err); // 將會告訴你null不被允許});m.save(); // 盡管數據無效,但是仍然可以保存。
versionKey
版本鎖設置在每一個文檔(document)上,由mogoose生成。默認的值是__v,但是可以自定義。
var schema = new Schema({ name: 'string' });var Thing = mongoose.model('Thing', schema);var thing = new Thing({ name: 'mongoose v3' });thing.save(); // { __v: 0, name: 'mongoose v3' }// 自定義版本鎖new Schema({..}, { versionKey: '_somethingElse' })var Thing = mongoose.model('Thing', schema);var thing = new Thing({ name: 'mongoose v3' });thing.save(); // { _somethingElse: 0, name: 'mongoose v3' }
不要將這個選項設置為false除非你知道你在做什么。
skipVersioning
http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-
按照這里的說法,大致是說,加入在一個博客系統中,一個人所有的評論是一個數組,那么所有的評論是有索引的,比如某一條評論的body,comments.3.body,這里3是索引。假如一個評論者(A)想要修改自己的評論,但是此時另一個評論者(B)刪除(或其他操作)了自己的評論,那么對A的索引可能會造成變化,此時對A的操作會發生錯誤。
為了改變這個問題,mongoose v3添加了version key配置。無論什么時候修改一個數組潛在地改變數組元素位置,這個version key(__V)的值會加1。在where條件中也需要添加__v條件,如果能通過(數組索引沒改變),就可以修改,例如:
posts.update({ _id: postId, __v: verionNumber } , { $set: { 'comments.3.body': updatedText }}
如果在更新之前刪除了評論,那么就會發生錯誤。
post.save(function (err) { console.log(err); // Error: No matching document found.});
timestamps
如果在schema設置這個選項,createdAt和updatedAt域將會被自動添加的文檔中。它們默認的類型是Date,默認的名字是createdAt和updatedAt,不過我們可以自己修改。
var thingSchema = new Schema({..}, { timestamps: { createdAt: 'created_at' } });var Thing = mongoose.model('Thing', thingSchema);var thing = new Thing();thing.save(); // created_at & updatedAt將會被包含在文檔。
useNestedStrict
在mongoos 4, update()和findOneAndUpdate()方法只檢查頂級schema的strict的選項設置。
var childSchema = new Schema({}, { strict: false });// 這里parentSchema是topSchema,而childSchema是subSchema。var parentSchema = new Schema({ child: childSchema }, { strict: 'throw' });var Parent = mongoose.model('Parent', parentSchema);Parent.update({}, { 'child.name': 'Luke Skywalker' }, function(error) { // 發生錯誤因為parentSchema設置了{strict: 'throw'} // 即使childSchema設置了{strict: false}});var update = { 'child.name': 'Luke Skywalker' };var opts = { strict: false };Parent.update({}, update, opts, function(error) { // 這個可以通過因為重寫了parentSchema的strict選項});
如果設置了useNestedStrict為true,mogoose在更新時使用childSchema的strict選項。
var childSchema = new Schema({}, { strict: false });var parentSchema = new Schema({ child: childSchema }, { strict: 'throw', useNestedStrict: true });var Parent = mongoose.model('Parent', parentSchema);Parent.update({}, { 'child.name': 'Luke Skywalker' }, function(error) { // 可以更新});
retainKeyOrder
默認得,mongoose會轉換實體中鍵的順序。比如new Model({ first: 1, second: 2 })將會在MongoDB中存儲為{ second: 2, first: 1 };這帶來了極大的不方便。
Mongoose v4.6.4 有一個retainKeyOrder選項確保mongoose不會改變鍵的順序。
參考
http://cnodejs.org/topic/504b4924e2b84515770103dd?utm_source=ourjs.com
http://www.nodeclass.com/api/mongoose.html#schema_Schema-add
http://mongoosejs.com/docs/guide.html
看文倉www.kanwencang.com網友整理上傳,為您提供最全的知識大全,期待您的分享,轉載請注明出處。
歡迎轉載:http://www.kanwencang.com/bangong/20170215/101837.html
文章列表