先決條件: | Express Tutorial Part 2: Creating a skeleton website |
---|---|
目的: | 能夠使用Mongoose設(shè)計(jì)和創(chuàng)建自己的模型。 |
圖書館工作人員將使用當(dāng)?shù)貓D書館網(wǎng)站存儲(chǔ)有關(guān)圖書和借閱者的信息,圖書館成員將使用它來瀏覽和搜索圖書,查看是否有任何副本,然后預(yù)訂或借閱。 為了有效地存儲(chǔ)和檢索信息,我們將其存儲(chǔ)在數(shù)據(jù)庫中。
Express應(yīng)用可以使用許多不同的數(shù)據(jù)庫,您可以使用多種方法來執(zhí)行 C reate, R ead, U pdate和 > D 電子(CRUD)操作。 本教程簡(jiǎn)要概述了一些可用選項(xiàng),然后詳細(xì)顯示所選的特定機(jī)制。
應(yīng)用程序可以使用節(jié)點(diǎn)支持的任何數(shù)據(jù)( Express 本身沒有定義數(shù)據(jù)庫管理的任何特定附加行為/要求)。 有許多常用選項(xiàng),包括PostgreSQL,MySQL,Redis,SQLite和MongoDB 。
在選擇數(shù)據(jù)庫時(shí),應(yīng)考慮時(shí)間 - 生產(chǎn)力/學(xué)習(xí)曲線,性能,易于復(fù)制/備份,成本,社區(qū)支持等。雖然沒有單一的"最佳"數(shù)據(jù)庫,但幾乎任何流行的解決方案 對(duì)于像我們的本地圖書館這樣的中小型網(wǎng)站應(yīng)該是可以接受的。
有關(guān)選項(xiàng)的詳細(xì)信息,請(qǐng)參閱:數(shù)據(jù)庫集成(Express docs)。
有兩種方法與數(shù)據(jù)庫交互:
使用SQL或數(shù)據(jù)庫支持的任何查詢語言都可以獲得最佳的性能。 ODM通常較慢,因?yàn)樗鼈兪褂梅g代碼在對(duì)象和數(shù)據(jù)庫格式之間進(jìn)行映射,這可能不會(huì)使用最高效的數(shù)據(jù)庫查詢(如果ODM支持不同的數(shù)據(jù)庫后端,則尤其如此,并且必須在什么數(shù)據(jù)庫 支持功能)。
使用ORM的好處是程序員可以繼續(xù)思考JavaScript對(duì)象而不是數(shù)據(jù)庫語義 - 如果你需要使用不同的數(shù)據(jù)庫(在相同或不同的網(wǎng)站上),這是特別真實(shí)的。 它們還提供了執(zhí)行數(shù)據(jù)驗(yàn)證和檢查的顯而易見的地方。
提示:使用ODM / ORM通常會(huì)降低開發(fā)和維護(hù)成本! 除非您非常熟悉本機(jī)查詢語言或性能至關(guān)重要,否則應(yīng)該強(qiáng)烈考慮使用ODM。
NPM軟件包管理器網(wǎng)站上提供了許多ODM / ORM解決方案(請(qǐng)查看 odm 和 orm 標(biāo)記的子集!)。
在寫作時(shí)流行的一些解決方案是:
作為一般規(guī)則,在選擇解決方案時(shí),應(yīng)考慮提供的功能和"社區(qū)活動(dòng)"(下載,貢獻(xiàn),錯(cuò)誤報(bào)告,文檔質(zhì)量等)。 在編寫時(shí)Mongoose是目前最流行的ORM,如果你使用MongoDB為你的數(shù)據(jù)庫是一個(gè)合理的選擇。
對(duì)于 Local Library 示例(以及本主題的其余部分),我們將使用 "> Mongoose ODM 訪問我們的庫數(shù)據(jù)。 Mongoose充當(dāng) MongoDB 的前端,這是一個(gè)開源 > NoSQL 數(shù)據(jù)庫,它使用面向文檔的數(shù)據(jù)模型。 "文檔"的"集合",在MongoDB數(shù)據(jù)庫中,類似 到關(guān)系數(shù)據(jù)庫中的"行"的"表"。
這種ODM和數(shù)據(jù)庫組合在Node社區(qū)中非常受歡迎,部分原因是文檔存儲(chǔ)和查詢系統(tǒng)看起來非常像JSON,因此JavaScript開發(fā)人員很熟悉。
提示:您不需要知道MongoDB以使用Mongoose,但部分 "> Mongoose文檔 更容易使用,并了解您是否已經(jīng)熟悉MongoDB。
本教程的其余部分將介紹如何定義和訪問 LocalLibrary網(wǎng)站示例的Mongoose模式和模型。
在你跳入并開始編碼模型之前,值得花幾分鐘時(shí)間考慮我們需要存儲(chǔ)什么數(shù)據(jù)以及不同對(duì)象之間的關(guān)系。
我們知道我們需要存儲(chǔ)關(guān)于圖書的信息(標(biāo)題,摘要,作者,類型,ISBN),并且我們可能有多個(gè)副本可用(具有全球唯一的ID,可用性狀態(tài)等)。 我們可能需要存儲(chǔ)有關(guān)作者的更多信息,而不僅僅是他們的姓名,并且可能有多個(gè)作者使用相同或相似的名稱。 我們希望能夠根據(jù)書名,作者,類型和類別對(duì)信息進(jìn)行排序。
在設(shè)計(jì)模型時(shí),為每個(gè)"對(duì)象"(相關(guān)信息組)分別建立模型是有意義的。 在這種情況下,明顯的對(duì)象是書,書實(shí)例和作者。
您可能還想使用模型來表示選擇列表選項(xiàng)(例如,像下拉列表中的選項(xiàng)),而不是將選擇硬編碼到網(wǎng)站本身 - 這是建議當(dāng)所有的選項(xiàng)都不知道前面或可能 更改。 這種類型的模型的明顯候選是書類型(例如科幻小說,法國詩歌等)
一旦我們決定了我們的模型和領(lǐng)域,我們需要考慮它們之間的關(guān)系。
考慮到這一點(diǎn),下面的UML關(guān)聯(lián)圖顯示了我們?cè)谶@種情況下定義的模型(如框)。 如上所述,我們已經(jīng)創(chuàng)建了書的模型(書的通用細(xì)節(jié)),書實(shí)例(系統(tǒng)中可用的書的特定物理副本的狀態(tài))和作者。 我們還決定了一個(gè)類型模型,以便可以動(dòng)態(tài)創(chuàng)建值。 我們決定不要為 BookInstance:status
建立一個(gè)模型 - 我們將硬編碼可接受的值,因?yàn)槲覀儾幌M@些值發(fā)生變化。 在每個(gè)框中,您可以看到模型名稱,字段名稱和類型,以及方法及其返回類型。
該圖還示出了模型之間的關(guān)系,包括它們的多重性。 多重性是圖上的數(shù)字,其示出了可能存在于關(guān)系中的每個(gè)模型的數(shù)量(最大和最小)。 例如,框之間的連接線示出 Book
和 Genre
是相關(guān)的。 靠近 Book
模型的數(shù)字表明一本圖書必須有零個(gè)或多個(gè) Genre
(盡可能多),而下一行的另一端的數(shù)字 到 Genre
表示它可以有零個(gè)或多個(gè)關(guān)聯(lián)的圖書。
注意:如下面我們的 Mongoose入門中所述,通常最好是在一個(gè)模型中定義文檔/模型之間的關(guān)系的字段 (您仍然可以通過搜索其他模型中相關(guān)的 _id
找到相反的關(guān)系)。 下面我們選擇定義Book模式中的Book / Genre和Book / Author之間的關(guān)系,以及BookInstance模式中的Book / BookInstance之間的關(guān)系。 這個(gè)選擇有點(diǎn)武斷 - 我們可以同樣有在另一個(gè)模式中的字段。
注意:下一節(jié)提供了一個(gè)基本的說明,說明如何定義和使用模型。 當(dāng)你閱讀它,考慮我們將如何構(gòu)建上面的圖中的每個(gè)模型。
本節(jié)提供如何將Mongoose連接到MongoDB數(shù)據(jù)庫,如何定義模式和模型以及如何進(jìn)行基本查詢的概述。
請(qǐng)注意:此底稿受 Mongoose快速入門的影響很大 在 npm 和官方文檔。
Mongoose安裝在您的項(xiàng)目中( app.json ),就像使用NPM的任何其他依賴項(xiàng)一樣。 要安裝它,請(qǐng)?jiān)陧?xiàng)目文件夾中使用以下命令:
npm install mongoose --save
安裝 會(huì)添加所有的依賴項(xiàng),包括MongoDB數(shù)據(jù)庫驅(qū)動(dòng)程序,但它不會(huì)安裝MongoDB本身。 如果要安裝MongoDB服務(wù)器,則可以從此處下載安裝程序,以查看各種操作系統(tǒng)并安裝 它在本地。 您還可以使用基于云的MongoDB實(shí)例。
注意:在本教程中,我們將使用基于云端的mLab數(shù)據(jù)庫作為服務(wù) / plans / pricing /"> sandbox tier 來提供數(shù)據(jù)庫。 這適合開發(fā),并且對(duì)于教程是有意義的,因?yàn)樗?安裝"操作系統(tǒng)獨(dú)立(數(shù)據(jù)庫即服務(wù)也是您可能很好地用于生產(chǎn)數(shù)據(jù)庫的一種方法)。
Mongoose 需要連接到MongoDB數(shù)據(jù)庫。 您可以 require()
并使用 mongoose.connect()
連接到本地托管數(shù)據(jù)庫,如下所示。
//Import the mongoose module var mongoose = require('mongoose'); //Set up default mongoose connection var mongoDB = 'mongodb://127.0.0.1/my_database'; mongoose.connect(mongoDB); //Get the default connection var db = mongoose.connection; //Bind connection to error event (to get notification of connection errors) db.on('error', console.error.bind(console, 'MongoDB connection error:'));
您可以使用 mongoose.connection
獲取默認(rèn)的 Connection
對(duì)象。 連接后,將在 Connection
實(shí)例上觸發(fā)open事件。
提示:如果您需要?jiǎng)?chuàng)建其他連接,可以使用 mongoose.createConnection()
。 這采用與 connect()
相同的形式的數(shù)據(jù)庫URI(具有主機(jī),數(shù)據(jù)庫,端口,選項(xiàng)等)并返回 Connection
對(duì)象)。
使用 Schema
界面 定義模型。 模式允許您定義每個(gè)文檔中存儲(chǔ)的字段及其驗(yàn)證要求和默認(rèn)值。 此外,您可以定義靜態(tài)和實(shí)例幫助程序方法,以便更輕松地使用您的數(shù)據(jù)類型,以及虛擬屬性,您可以像任何其他字段一樣使用,但實(shí)際上并不存儲(chǔ)在數(shù)據(jù)庫中(我們將討論 下面一點(diǎn))。
然后使用 mongoose.model()
方法將模式"編譯"成模型。 一旦你有一個(gè)模型,你可以使用它來查找,創(chuàng)建,更新和刪除給定類型的對(duì)象。
注意:每個(gè)模型都會(huì)映射到MongoDB數(shù)據(jù)庫中文檔的集合。 文檔將包含在模型 Schema
中定義的字段/模式類型。
下面的代碼片段顯示了如何定義一個(gè)簡(jiǎn)單的模式。 首先你 require()
mongoose,然后使用Schema構(gòu)造函數(shù)創(chuàng)建一個(gè)新的模式實(shí)例,在構(gòu)造函數(shù)的對(duì)象參數(shù)中定義其中的各個(gè)字段。
//Require Mongoose var mongoose = require('mongoose'); //Define a schema var Schema = mongoose.Schema; var SomeModelSchema = new Schema({ a_string : String, a_date : Date, });
在上面的例子中,我們只有兩個(gè)字段,一個(gè)字符串和一個(gè)日期。 在接下來的部分中,我們將展示一些其他字段類型,驗(yàn)證和其他方法。
使用 mongoose.model()
方法從模式創(chuàng)建模型:
// Define schema var SomeModelSchema = new mongoose.Schema({ name: 'string', }); // Compile model from schema var SomeModel = mongoose.model('SomeModel', SomeModelSchema );
第一個(gè)參數(shù)是將為您的模型創(chuàng)建的集合的單一名稱(Mongoose將為上述模型 SomeModel 創(chuàng)建數(shù)據(jù)庫集合),第二個(gè)參數(shù)是您要使用的模式 在創(chuàng)建模型。
注意:定義模型類后,可以使用它們創(chuàng)建,更新或刪除記錄,并運(yùn)行查詢以獲取記錄或特定記錄子集。 我們將在使用模型部分以及創(chuàng)建視圖時(shí)向您展示如何執(zhí)行此操作。
模式可以具有任意數(shù)量的字段 - 每個(gè)字段表示存儲(chǔ)在 MongoDB 中的文檔中的字段。 顯示許多常見字段類型及其聲明方式的示例模式如下所示。
var schema = new Schema( { name: String, binary: Buffer, living: Boolean, updated: { type: Date, default: Date.now }, age: { type: Number, min: 18, max: 65, required: true }, mixed: Schema.Types.Mixed, _someId: Schema.Types.ObjectId, array: [], ofString: [String], // You can also have an array of each of the other types too. nested: { stuff: { type: String, lowercase: true, trim: true } } })
SchemaTypes ("type:"后或"字段名稱"后的描述符)大部分都是自我說明。 例外情況是:
ObjectId
: Represents specific instances of a model in the database. For example, a book might use this to represent its author object. This will actually contain the unique ID (_id
) for the specified object. We can use the populate()
method to pull in the associated information when needed.String
objects, but you can have an array of any type of object.代碼還顯示了聲明字段的兩種方法:
name
, binary
and living
).type
, and any other options for the field. Options include things like:String
fields should automatically be set to lowercase, uppercase, or trimmed (e.g. { type: String, lowercase: true, trim: true }
)有關(guān)選項(xiàng)的詳情,請(qǐng)參閱: SchemaTypes (Mongoose文檔)。
Mongoose提供內(nèi)置和自定義驗(yàn)證器,以及同步和異步驗(yàn)證器。 它允許您在所有情況下指定可接受的范圍或值以及驗(yàn)證失敗的錯(cuò)誤消息。
內(nèi)置驗(yàn)證器包括:
下面的示例(略微修改了Mongoose文檔)顯示了如何指定一些驗(yàn)證器類型和錯(cuò)誤消息:
var breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, 'Too few eggs'],
max: 12
required: [true, 'Why no bacon?']
},
drink: {
type: String,
enum: ['Coffee', 'Tea', 'Water',]
}
});
有關(guān)字段驗(yàn)證的完整信息,請(qǐng)參閱驗(yàn)證(Mongoose文檔)。
虛擬屬性是您可以獲取和設(shè)置但不會(huì)持久保存到MongoDB的文檔屬性。 getter對(duì)于格式化或組合字段很有用,而setter對(duì)于將單個(gè)值解組成多個(gè)值以進(jìn)行存儲(chǔ)是有用的。 文檔中的示例構(gòu)造(并解構(gòu))來自名字和姓氏字段的全名虛擬屬性,這比在模板中使用時(shí)構(gòu)建全名更簡(jiǎn)單和更清楚。
注意:我們將使用庫中的虛擬屬性為每個(gè)模型記錄使用路徑和記錄的 _id
值定義唯一的網(wǎng)址。
有關(guān)詳情,請(qǐng)參閱虛擬(Mongoose文檔)。
模式還可以具有實(shí)例方法, ://mongoosejs.com/docs/guide.html#statics">靜態(tài)方法和 幫助">查詢助手。 實(shí)例和靜態(tài)方法是相似的,但有一個(gè)明顯的區(qū)別,即實(shí)例方法與特定記錄相關(guān)聯(lián),并且可以訪問當(dāng)前對(duì)象。 查詢助手允許您擴(kuò)展mongoose的可鏈接查詢構(gòu)建器API (例如,允許您添加查詢 "byName"除了 find()
, findOne()
和 findById()
方法)。
一旦創(chuàng)建了模式,就可以使用它來創(chuàng)建模型。 模型表示數(shù)據(jù)庫中您可以搜索的文檔集合,而模型的實(shí)例表示可以保存和檢索的單個(gè)文檔。
我們?cè)谙旅嫣峁┮粋€(gè)簡(jiǎn)要概述。 有關(guān)詳情,請(qǐng)參閱:模型(Mongoose文檔)。
要?jiǎng)?chuàng)建記錄,您可以定義模型的實(shí)例,然后調(diào)用 save()
。 下面的示例假設(shè)SomeModel是一個(gè)模型(具有單個(gè)字段"名稱"),我們已從我們的模式創(chuàng)建。
// Create an instance of model SomeModel var awesome_instance = new
SomeModel({ name: 'awesome' }); // Save the new model instance, passing a callback awesome_instance.save(function (err) { if (err) return handleError(err); // saved! });
請(qǐng)注意,創(chuàng)建記錄(以及更新,刪除和查詢)是異步操作 - 您提供在操作完成時(shí)調(diào)用的回調(diào)。 API使用錯(cuò)誤第一個(gè)參數(shù)約定,因此回調(diào)的第一個(gè)參數(shù)將始終為錯(cuò)誤值(或null)。 如果API返回一些結(jié)果,這將作為第二個(gè)參數(shù)提供。
您還可以使用 create()
在保存模型實(shí)例的同時(shí)定義模型實(shí)例。 回調(diào)將返回第一個(gè)參數(shù)的錯(cuò)誤,并為第二個(gè)參數(shù)的新創(chuàng)建的模型實(shí)例返回錯(cuò)誤。
SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
if (err) return handleError(err);
// saved!
});
每個(gè)模型都有一個(gè)關(guān)聯(lián)的連接(這將是使用 mongoose.model()
時(shí)的默認(rèn)連接)。 您創(chuàng)建一個(gè)新的連接并調(diào)用 .model()
就可以在不同的數(shù)據(jù)庫上創(chuàng)建文檔。
您可以使用點(diǎn)語法訪問此新記錄中的字段,并更改值。 您必須調(diào)用 save()
或 update()
將修改的值存回?cái)?shù)據(jù)庫。
// Access model field values using dot notation console.log(awesome_instance.name
); //should log 'also_awesome
' // Change record by modifying the fields, then calling save().awesome_instance
.name="New cool name";awesome_instance.save(function (err) { ? if (err) return handleError(err); // saved! ? });
您可以使用查詢方法搜索記錄,將查詢條件指定為JSON文檔。 下面的代碼片段顯示了如何在網(wǎng)球數(shù)據(jù)庫中找到所有運(yùn)動(dòng)員,僅返回運(yùn)動(dòng)員姓名 的字段。 這里我們只指定一個(gè)匹配字段(運(yùn)動(dòng)),但您可以添加更多的條件,指定正則表達(dá)式條件,或刪除條件,以返回所有運(yùn)動(dòng)員。
var Athlete = mongoose.model('Athlete', yourSchema);
// find all athletes who play tennis, selecting the 'name' and 'age' fields
Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) {
if (err) return handleError(err);
// 'athletes' contains the list of athletes that match the criteria.
})
如果指定回調(diào),如上所示,查詢將立即執(zhí)行。 回調(diào)將在搜索完成時(shí)調(diào)用。
注意:Mongoose中的所有回調(diào)都使用回調(diào)(error,result)
模式。 如果在執(zhí)行查詢時(shí)發(fā)生錯(cuò)誤,則 error
參數(shù)將包含錯(cuò)誤文檔, result
將為null。 如果查詢成功,則 error
參數(shù)將為null,并且 result
將填充查詢結(jié)果。
如果您未指定回調(diào),API將返回類型為 Query >。 您可以使用此查詢對(duì)象構(gòu)建您的查詢,然后使用 exec()
方法執(zhí)行(使用回調(diào))。
// find all athletes that play tennis
var query = Athlete.find({ 'sport': 'Tennis' });
// selecting the 'name' and 'age' fields
query.select('name age');
// limit our results to 5 items
query.limit(5);
// sort by age
query.sort({ age: -1 });
// execute the query at a later time
query.exec(function (err, athletes) {
if (err) return handleError(err);
// athletes contains an ordered list of 5 athletes who play Tennis
})
上面我們?cè)?code> find()方法中定義了查詢條件。 我們也可以使用 where()
函數(shù)來做到這一點(diǎn),我們可以使用點(diǎn)運(yùn)算符(。)將查詢的所有部分鏈接在一起,而不是單獨(dú)添加它們。 下面的代碼片段與上面的查詢相同,有一個(gè)額外的年齡條件。
Athlete.
find().
where('sport').equals('Tennis').
where('age').gt(17).lt(50). //Additional where query
limit(5).
sort({ age: -1 }).
select('name age').
exec(callback); // where callback is the name of our callback function.
find()方法會(huì)獲取所有匹配的記錄,但通常您只想獲取 一個(gè)匹配。 以下方法查詢單個(gè)記錄:
請(qǐng)注意:還有 count()方法 您可以使用它來獲取符合條件的項(xiàng)目數(shù)。 如果要在不實(shí)際獲取記錄的情況下執(zhí)行計(jì)數(shù),這將非常有用。
有很多你可以做的查詢。 有關(guān)詳細(xì)信息,請(qǐng)參閱:查詢(Mongoose文檔)。
您可以使用 ObjectId
模式字段,或者從一個(gè)文檔到許多使用 ObjectIds
數(shù)組的一個(gè)文檔/模型實(shí)例到另一個(gè)文檔/模型實(shí)例創(chuàng)建引用。 該字段存儲(chǔ)相關(guān)模型的ID。 如果您需要相關(guān)文檔的實(shí)際內(nèi)容,可以使用填充() >方法在查詢中用實(shí)際數(shù)據(jù)替換id。
例如,以下模式定義了作者和故事。 每個(gè)作者可以有多個(gè)故事,我們表示為 ObjectId
的數(shù)組。 每個(gè)故事可以有一個(gè)作者。 "ref"(下面以粗體突出顯示)告訴模式哪個(gè)模型可以分配給此字段。
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var authorSchema = Schema({
name : String,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
author : { type: Schema.Types.ObjectId, ref: 'Author' },
title : String,
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Author', authorSchema);
我們可以通過分配 _id
值來保存對(duì)相關(guān)文檔的引用。 下面我們創(chuàng)建一個(gè)作者,然后一本書,并將作者id分配給我們的故事作者字段。
var bob = new Author({ name: 'Bob Smith' });
bob.save(function (err) {
if (err) return handleError(err);
//Bob now exists, so lets create a story
var story = new Story({
title: "Bob goes sledding",
author: bob._id // assign the _id from the our author Bob. This ID is created by default!
});
story.save(function (err) {
if (err) return handleError(err);
// Bob now has his story
});
});
我們的故事文檔現(xiàn)在有一個(gè)由作者文檔ID引用的作者。 為了在我們的故事結(jié)果中獲得作者信息,我們使用 populate()
,如下所示。
Story
.findOne({ title: 'Bob goes sledding' })
.populate('author') //This populates the author id with actual author information!
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Bob Smith"
});
注意:精明的讀者會(huì)注意到我們?yōu)槲覀兊墓适绿砑恿艘晃蛔髡?,但我們沒有做任何事情將我們的故事添加到作者的 stories
數(shù)組中。 那么我們?nèi)绾文艿玫揭粋€(gè)特定作者的所有故事? 一種方法是將我們的作者添加到故事數(shù)組中,但這將導(dǎo)致我們有兩個(gè)地方需要維護(hù)有關(guān)作者和故事的信息。
更好的方法是獲取作者的 _id
,然后使用 find()
在所有故事的作者字段中搜索。
Story
.find({ author : bob._id })
.exec(function (err, stories) {
if (err) return handleError(err);
// returns all stories that have Bob's id as their author.
});
這幾乎是您在使用本教學(xué)課程相關(guān)項(xiàng)目時(shí)需要了解的一切 。 有關(guān)詳細(xì)信息,請(qǐng)參閱人口(Mongoose文檔)。
雖然可以使用任何您喜歡的文件結(jié)構(gòu)創(chuàng)建模式和模型,但我們強(qiáng)烈建議在其自己的模塊(文件)中定義每個(gè)模型模式,導(dǎo)出方法以創(chuàng)建模型。 如下所示:
// File: ./models/somemodel.js
//Require Mongoose
var mongoose = require('mongoose');
//Define a schema
var Schema = mongoose.Schema;
var SomeModelSchema = new Schema({
a_string : String,
a_date : Date,
});
//Export function to create "SomeModel" model class
module.exports = mongoose.model('SomeModel', SomeModelSchema );
然后,您可以在其他文件中立即要求并使用模型。 下面我們展示如何使用它來獲取模型的所有實(shí)例。
//Create a SomeModel model just by requiring the module
var SomeModel = require('../models/somemodel')
// Use the SomeModel object (model) to find all SomeModel records
SomeModel.find(callback_function);
現(xiàn)在我們了解了Mongoose可以做什么,以及我們?nèi)绾卧O(shè)計(jì)我們的模型,現(xiàn)在是開始在 LocalLibrary 網(wǎng)站上工作的時(shí)候了。 我們首先要做的是設(shè)置一個(gè)MongoDb數(shù)據(jù)庫,我們可以使用它來存儲(chǔ)我們的庫數(shù)據(jù)。
在本教程中,我們將使用 mLab 的免費(fèi)云托管的" > sandbox "數(shù)據(jù)庫。 此數(shù)據(jù)庫層不適合生產(chǎn)網(wǎng)站,因?yàn)樗鼪]有冗余,但它是偉大的開發(fā)和原型。 我們?cè)谶@里使用它,因?yàn)樗敲赓M(fèi)和容易設(shè)置,并且因?yàn)閙Lab是流行的數(shù)據(jù)庫作為服務(wù)供應(yīng)商,你可以合理地選擇為您的生產(chǎn)數(shù)據(jù)庫(當(dāng)時(shí)其他流行的選擇 的寫作包括撰寫, .html"> ScaleGrid 和 MongoDB Atlas )。
注意:如果您愿意,可以在本地設(shè)置MongoDB數(shù)據(jù)庫,方法是下載并安裝 適合您系統(tǒng)的二進(jìn)制文件。 本文中的其余說明將類似,但連接時(shí)要指定的數(shù)據(jù)庫URL除外。
您首先需要使用mLab 創(chuàng)建帳戶(這是免費(fèi)的,只需輸入基本的聯(lián)系方式 并確認(rèn)其服務(wù)條款)。
登錄后,您將轉(zhuǎn)到首頁屏幕:
創(chuàng)建數(shù)據(jù)庫后,您將返回查看您的MongoDb部署的概述。height:266px; width:800px;">
單擊新數(shù)據(jù)庫(如上面突出顯示)以打開包含數(shù)據(jù)庫的更多詳細(xì)信息的視圖。 您可以看到數(shù)據(jù)庫沒有集合(數(shù)據(jù))。
width:800px;">
您需要用于訪問數(shù)據(jù)庫的URL顯示在上面的表單上(針對(duì)上面圈出的此數(shù)據(jù)顯示)。 為了使用這個(gè),你需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫用戶,你可以在URL中指定。
您現(xiàn)在已經(jīng)創(chuàng)建了數(shù)據(jù)庫,并且具有可用于訪問它的URL(具有用戶名和密碼)。 這將看起來像: mongodb:// your_user_namer:your_password@ds119748.mlab.com:19748 / local_library
。
打開命令提示符,并導(dǎo)航到您創(chuàng)建骨架本地圖書館網(wǎng)站的目錄。 輸入以下命令以安裝Mongoose(及其依賴項(xiàng))并將其添加到您的 package.json 文件中,除非您在閱讀 Mongoose Primer / a>。
npm install mongoose --save
打開 /app.js (在項(xiàng)目的根目錄下),并在下面復(fù)制以下文本,您在此聲明 Express應(yīng)用程序?qū)ο?/em>( express(); )。 將數(shù)據(jù)庫url字符串(\' insert_your_database_url_here \')替換為您自己的數(shù)據(jù)庫的位置(即使用來自從mLab 的信息)。
//Set up mongoose connection var mongoose = require('mongoose'); var mongoDB = 'insert_your_database_url_here'; mongoose.connect(mongoDB); var db = mongoose.connection; db.on('error', console.error.bind(console, 'MongoDB connection error:'));
如上所述,在上面的Mongoose引導(dǎo)中,此代碼創(chuàng)建與數(shù)據(jù)庫的默認(rèn)連接,并綁定到錯(cuò)誤事件(以便錯(cuò)誤將打印到控制臺(tái))。
我們將為每個(gè)模型定義一個(gè)單獨(dú)的模塊,如上所述的 。 首先在項(xiàng)目根目錄中為我們的模型創(chuàng)建一個(gè)文件夾( / models ),然后為每個(gè)模型創(chuàng)建單獨(dú)的文件:
/express-locallibrary-tutorial //the project root /models author.js book.js bookinstance.js genre.js
復(fù)制下面顯示的作者
模式代碼,并將其粘貼到您的 ./ models / author.js 文件中。 該方案定義了作者對(duì)于第一個(gè)和家族名稱具有 String
SchemaTypes,這是必需的并且最多有100個(gè)字符,以及 Date
死亡。
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var AuthorSchema = Schema( { first_name: {type: String, required: true, max: 100}, family_name: {type: String, required: true, max: 100}, date_of_birth: {type: Date}, date_of_death: {type: Date}, } ); // Virtual for author's full name AuthorSchema .virtual('name') .get(function () { return this.family_name + ', ' + this.first_name; }); // Virtual for author's URL AuthorSchema .virtual('url') .get(function () { return '/catalog/author/' + this._id; }); //Export model module.exports = mongoose.model('Author', AuthorSchema);
我們還針對(duì)名為"url"的類型聲明了一個(gè)虛擬,它返回獲取模型的特定實(shí)例所需的絕對(duì)網(wǎng)址 - 我們將使用 每當(dāng)我們需要獲得到特定作者的鏈接時(shí)。
請(qǐng)注意:在模式中將我們的網(wǎng)址聲明為虛擬網(wǎng)址是一個(gè)好主意,因?yàn)檫@樣一來,只需要在一個(gè)地方更改項(xiàng)目的網(wǎng)址。
此時(shí),使用此網(wǎng)址的鏈接 將不工作,因?yàn)槲覀儧]有任何路由處理代碼為個(gè)別模型實(shí)例。 我們將在后面的文章中設(shè)置它們!
在模塊的最后,我們導(dǎo)出模型。
復(fù)制下面顯示的 Book
模式代碼,并將其粘貼到您的 ./ models / book.js 文件中。 其中大部分類似于作者模型 - 我們已經(jīng)聲明了一個(gè)帶有多個(gè)字符串字段的模式和一個(gè)用于獲取特定書記錄的URL的模式,并且我們已經(jīng)導(dǎo)出了模型。
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var BookSchema = Schema({ title: {type: String, required: true}, author: {type: Schema.ObjectId, ref: 'Author', required: true}, summary: {type: String, required: true}, isbn: {type: String, required: true}, genre: [{type: Schema.ObjectId, ref: 'Genre'}] }); // Virtual for book's URL BookSchema .virtual('url') .get(function () { return '/catalog/book/' + this._id; }); //Export model module.exports = mongoose.model('Book', BookSchema);
這里的主要區(qū)別是我們已經(jīng)創(chuàng)建了對(duì)其他模型的兩個(gè)引用(以粗體突出顯示):
Author
model object, and is required.Genre
model objects. We haven't declared this object yet!最后,復(fù)制下面顯示的 BookInstance
模式代碼,并將其粘貼到 ./ models / bookinstance.js 文件中。 BookInstance
表示某人可能借用的書的特定副本,并包括有關(guān)副本是否可用或其預(yù)期的日期,"印記"或版本詳細(xì)信息的信息。
var mongoose = require('mongoose'); var Schema = mongoose.Schema; var BookInstanceSchema = Schema({ book: { type: Schema.ObjectId, ref: 'Book', required: true }, //reference to the associated book imprint: {type: String, required: true}, status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'}, due_back: {type: Date, default: Date.now}, }); // Virtual for bookinstance's URL BookInstanceSchema .virtual('url') .get(function () { return '/catalog/bookinstance/' + this._id; }); //Export model module.exports = mongoose.model('BookInstance', BookInstanceSchema);
我們?cè)谶@里顯示的新東西是字段選項(xiàng):
enum
: This allows us to set the allowed values of a string. In this case we use it to specify the availability status of our books (using an enum means that we can prevent mis-spellings and arbitrary values for our status)default
: We use default to set the default status for newly created bookinstances to maintenance and the default due_back
date to now
(note how you can call the Date function when setting the date!)其他一切都應(yīng)該從我們以前的模式熟悉。
打開您的 ./ models / genre.js 文件,并創(chuàng)建存儲(chǔ)類型(圖書類別,例如它是小說還是非小說,浪漫或軍事歷史等)的架構(gòu)。
其定義將與其他模型非常相似:
String
SchemaType called name
to describe the genre.url
.而已。 我們現(xiàn)在有所有型號(hào)的網(wǎng)站設(shè)置!
為了測(cè)試模型(并創(chuàng)建一些示例書和其他項(xiàng)目,我們可以在下一篇文章中使用),我們現(xiàn)在將運(yùn)行一個(gè)獨(dú)立腳本來創(chuàng)建每種類型的項(xiàng)目:
注意:您不需要知道 "> populatedb.js 在這一點(diǎn)上工作。 我們將在后面的文章中解釋相關(guān)的部分。
npm install async --save
node populatedb <your mongodb url>????
提示:在 mLab 上轉(zhuǎn)到您的數(shù)據(jù)庫。 現(xiàn)在,您應(yīng)該可以深入查看圖書,作者,流派和BookInstances的各個(gè)集合,并查看單個(gè)文檔。
在本文中,我們已經(jīng)了解了一些關(guān)于Node / Express上的數(shù)據(jù)庫和ORM,以及有關(guān)如何定義Mongoose模式和模型的內(nèi)容。 然后我們使用這些信息來設(shè)計(jì)和實(shí)現(xiàn) Book
, BookInstance
, Author
和 Genre
LocalLibrary 網(wǎng)站。
最后,我們通過創(chuàng)建多個(gè)實(shí)例(使用獨(dú)立腳本)來測(cè)試我們的模型。 在下一篇文章中,我們將介紹創(chuàng)建一些頁面來顯示這些對(duì)象。
更多建議: