Express Tutorial Part 3: Using a Database (with Mongoose)

2018-05-15 17:26 更新
先決條件: Express Tutorial Part 2: Creating a skeleton website
目的: 能夠使用Mongoose設計和創(chuàng)建自己的模型。

概述

圖書館工作人員將使用當地圖書館網站存儲有關圖書和借閱者的信息,圖書館成員將使用它來瀏覽和搜索圖書,查看是否有任何副本,然后預訂或借閱。 為了有效地存儲和檢索信息,我們將其存儲在數據庫中。

Express應用可以使用許多不同的數據庫,您可以使用多種方法來執(zhí)行 C reate, R ead, U pdate和 > D 電子(CRUD)操作。 本教程簡要概述了一些可用選項,然后詳細顯示所選的特定機制。

我可以使用什么數據庫?

應用程序可以使用節(jié)點支持的任何數據( Express 本身沒有定義數據庫管理的任何特定附加行為/要求)。 許多常用選項,包括PostgreSQL,MySQL,Redis,SQLite和MongoDB 。

在選擇數據庫時,應考慮時間 - 生產力/學習曲線,性能,易于復制/備份,成本,社區(qū)支持等。雖然沒有單一的"最佳"數據庫,但幾乎任何流行的解決方案 對于像我們的本地圖書館這樣的中小型網站應該是可以接受的。

有關選項的詳細信息,請參閱:數據庫集成(Express docs)。

什么是與數據庫交互的最佳方式?

有兩種方法與數據庫交互:

  • Using the databases' native query language (e.g. SQL)
  • Using an Object Data Model ("ODM") / Object Relational Model ("ORM"). An ODM/ORM represents the website's data as JavaScript objects, which are then mapped to the underlying database. Some ORMs are tied to a specific database, while others provide a database-agnostic backend.

使用SQL或數據庫支持的任何查詢語言都可以獲得最佳的性能。 ODM通常較慢,因為它們使用翻譯代碼在對象和數據庫格式之間進行映射,這可能不會使用最高效的數據庫查詢(如果ODM支持不同的數據庫后端,則尤其如此,并且必須在什么數據庫 支持功能)。

使用ORM的好處是程序員可以繼續(xù)思考JavaScript對象而不是數據庫語義 - 如果你需要使用不同的數據庫(在相同或不同的網站上),這是特別真實的。 它們還提供了執(zhí)行數據驗證和檢查的顯而易見的地方。

提示:使用ODM / ORM通常會降低開發(fā)和維護成本! 除非您非常熟悉本機查詢語言或性能至關重要,否則應該強烈考慮使用ODM。

我應該使用什么ORM / ODM?

NPM軟件包管理器網站上提供了許多ODM / ORM解決方案(請查看 odm orm 標記的子集!)。

在寫作時流行的一些解決方案是:

  • Mongoose: Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
  • Waterline: An ORM extracted from the Express-based Sails web framework. It provides a uniform API for accessing numerous different databases, including Redis, mySQL, LDAP, MongoDB, and Postgres.
  • Bookshelf: Features both promise-based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. Works with PostgreSQL, MySQL, and SQLite3.
  • Objection: Makes it as easy as possible to use the full power of SQL and the underlying database engine (supports SQLite3, Postgres, and MySQL).

作為一般規(guī)則,在選擇解決方案時,應考慮提供的功能和"社區(qū)活動"(下載,貢獻,錯誤報告,文檔質量等)。 在編寫時Mongoose是目前最流行的ORM,如果你使用MongoDB為你的數據庫是一個合理的選擇。

使用Mongoose和MongoDb為LocalLibrary

對于 Local Library 示例(以及本主題的其余部分),我們將使用 "> Mongoose ODM 訪問我們的庫數據。 Mongoose充當 MongoDB 的前端,這是一個開源 > NoSQL 數據庫,它使用面向文檔的數據模型。 "文檔"的"集合",在MongoDB數據庫中,類似 到關系數據庫中的"行"的"表"。

這種ODM和數據庫組合在Node社區(qū)中非常受歡迎,部分原因是文檔存儲和查詢系統(tǒng)看起來非常像JSON,因此JavaScript開發(fā)人員很熟悉。

提示:您不需要知道MongoDB以使用Mongoose,但部分 "> Mongoose文檔 更容易使用,并了解您是否已經熟悉MongoDB。

本教程的其余部分將介紹如何定義和訪問 LocalLibrary網站示例的Mongoose模式和模型。

設計LocalLibrary模型

在你跳入并開始編碼模型之前,值得花幾分鐘時間考慮我們需要存儲什么數據以及不同對象之間的關系。

我們知道我們需要存儲關于圖書的信息(標題,摘要,作者,類型,ISBN),并且我們可能有多個副本可用(具有全球唯一的ID,可用性狀態(tài)等)。 我們可能需要存儲有關作者的更多信息,而不僅僅是他們的姓名,并且可能有多個作者使用相同或相似的名稱。 我們希望能夠根據書名,作者,類型和類別對信息進行排序。

在設計模型時,為每個"對象"(相關信息組)分別建立模型是有意義的。 在這種情況下,明顯的對象是書,書實例和作者。

您可能還想使用模型來表示選擇列表選項(例如,像下拉列表中的選項),而不是將選擇硬編碼到網站本身 - 這是建議當所有的選項都不知道前面或可能 更改。 這種類型的模型的明顯候選是書類型(例如科幻小說,法國詩歌等)

一旦我們決定了我們的模型和領域,我們需要考慮它們之間的關系。

考慮到這一點,下面的UML關聯(lián)圖顯示了我們在這種情況下定義的模型(如框)。 如上所述,我們已經創(chuàng)建了書的模型(書的通用細節(jié)),書實例(系統(tǒng)中可用的書的特定物理副本的狀態(tài))和作者。 我們還決定了一個類型模型,以便可以動態(tài)創(chuàng)建值。 我們決定不要為 BookInstance:status 建立一個模型 - 我們將硬編碼可接受的值,因為我們不希望這些值發(fā)生變化。 在每個框中,您可以看到模型名稱,字段名稱和類型,以及方法及其返回類型。

該圖還示出了模型之間的關系,包括它們的多重性。 多重性是圖上的數字,其示出了可能存在于關系中的每個模型的數量(最大和最小)。 例如,框之間的連接線示出 Book Genre 是相關的。 靠近 Book 模型的數字表明一本圖書必須有零個或多個 Genre (盡可能多),而下一行的另一端的數字 Genre 表示它可以有零個或多個關聯(lián)的圖書。

注意:如下面我們的 Mongoose入門中所述,通常最好是在一個模型中定義文檔/模型之間的關系的字段 (您仍然可以通過搜索其他模型中相關的 _id 找到相反的關系)。 下面我們選擇定義Book模式中的Book / Genre和Book / Author之間的關系,以及BookInstance模式中的Book / BookInstance之間的關系。 這個選擇有點武斷 - 我們可以同樣有在另一個模式中的字段。

注意:下一節(jié)提供了一個基本的說明,說明如何定義和使用模型。 當你閱讀它,考慮我們將如何構建上面的圖中的每個模型。

Mongoose引物

本節(jié)提供如何將Mongoose連接到MongoDB數據庫,如何定義模式和模型以及如何進行基本查詢的概述。

請注意:此底稿受 Mongoose快速入門的影響很大 npm 官方文檔。

安裝Mongoose和MongoDB

Mongoose安裝在您的項目中( app.json ),就像使用NPM的任何其他依賴項一樣。 要安裝它,請在項目文件夾中使用以下命令:

npm install mongoose --save

安裝 會添加所有的依賴項,包括MongoDB數據庫驅動程序,但它不會安裝MongoDB本身。 如果要安裝MongoDB服務器,則可以從此處下載安裝程序,以查看各種操作系統(tǒng)并安裝 它在本地。 您還可以使用基于云的MongoDB實例。

注意:在本教程中,我們將使用基于云端的mLab數據庫作為服務 / plans / pricing /"> sandbox tier 來提供數據庫。 這適合開發(fā),并且對于教程是有意義的,因為它使"安裝"操作系統(tǒng)獨立(數據庫即服務也是您可能很好地用于生產數據庫的一種方法)。

連接到MongoDB

Mongoose 需要連接到MongoDB數據庫。 您可以 require()并使用 mongoose.connect()連接到本地托管數據庫,如下所示。

//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 獲取默認的 Connection 對象。 連接后,將在 Connection 實例上觸發(fā)open事件。

提示:如果您需要創(chuàng)建其他連接,可以使用 mongoose.createConnection()。 這采用與 connect()相同的形式的數據庫URI(具有主機,數據庫,端口,選項等)并返回 Connection 對象)。

定義和創(chuàng)建模型

使用 Schema 界面 定義模型。 模式允許您定義每個文檔中存儲的字段及其驗證要求和默認值。 此外,您可以定義靜態(tài)和實例幫助程序方法,以便更輕松地使用您的數據類型,以及虛擬屬性,您可以像任何其他字段一樣使用,但實際上并不存儲在數據庫中(我們將討論 下面一點)。

然后使用 mongoose.model()方法將模式"編譯"成模型。 一旦你有一個模型,你可以使用它來查找,創(chuàng)建,更新和刪除給定類型的對象。

注意:每個模型都會映射到MongoDB數據庫中文檔集合。 文檔將包含在模型 Schema 中定義的字段/模式類型。

Defining schemas

下面的代碼片段顯示了如何定義一個簡單的模式。 首先你 require() mongoose,然后使用Schema構造函數創(chuàng)建一個新的模式實例,在構造函數的對象參數中定義其中的各個字段。

//Require Mongoose
var mongoose = require('mongoose');

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
    a_string          : String,
    a_date            : Date,
});

在上面的例子中,我們只有兩個字段,一個字符串和一個日期。 在接下來的部分中,我們將展示一些其他字段類型,驗證和其他方法。

Creating a model

使用 mongoose.model()方法從模式創(chuàng)建模型:

// Define schema
var SomeModelSchema = new mongoose.Schema({ name: 'string', });
// Compile model from schema
var SomeModel = mongoose.model('SomeModel', SomeModelSchema );

第一個參數是將為您的模型創(chuàng)建的集合的單一名稱(Mongoose將為上述模型 SomeModel 創(chuàng)建數據庫集合),第二個參數是您要使用的模式 在創(chuàng)建模型。

注意:定義模型類后,可以使用它們創(chuàng)建,更新或刪除記錄,并運行查詢以獲取記錄或特定記錄子集。 我們將在使用模型部分以及創(chuàng)建視圖時向您展示如何執(zhí)行此操作。

Schema types (fields)

模式可以具有任意數量的字段 - 每個字段表示存儲在 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.
  • Mixed: An arbitrary schema type.
  • []: An array of items. You can perform JavaScript array operations on these models (push, pop, unshift, etc.). The examples above show?an array of objects without a specified type and an array of?String objects, but you can have an array of any type of object.

代碼還顯示了聲明字段的兩種方法:

  • Field name and type as a key-value pair (i.e. as done with fields name, binary and living).
  • Field name followed by an object defining the type, and any other options for the field. Options include things like:
    • default values.
    • built-in validators (e.g. max/min values) and custom validation functions.
    • Whether the field is required
    • Whether String fields should automatically be set to lowercase, uppercase, or trimmed (e.g. { type: String, lowercase: true, trim: true })

有關選項的詳情,請參閱: SchemaTypes (Mongoose文檔)。

Validation

Mongoose提供內置和自定義驗證器,以及同步和異步驗證器。 它允許您在所有情況下指定可接受的范圍或值以及驗證失敗的錯誤消息。

內置驗證器包括:

  • All SchemaTypes have the built-in required validator. This is used to specify whether the field must be supplied in order to save a document.
  • Numbers have min and max validators.
  • Strings have:
    • enum: specifies the set of allowed values for the field.
    • match: specifies a regular expression that the string must match.
    • maxlength and minlength for the string.

下面的示例(略微修改了Mongoose文檔)顯示了如何指定一些驗證器類型和錯誤消息:


    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',]
      }
    });

有關字段驗證的完整信息,請參閱驗證(Mongoose文檔)。

Virtual properties

虛擬屬性是您可以獲取和設置但不會持久保存到MongoDB的文檔屬性。 getter對于格式化或組合字段很有用,而setter對于將單個值解組成多個值以進行存儲是有用的。 文檔中的示例構造(并解構)來自名字和姓氏字段的全名虛擬屬性,這比在模板中使用時構建全名更簡單和更清楚。

注意:我們將使用庫中的虛擬屬性為每個模型記錄使用路徑和記錄的 _id 值定義唯一的網址。

有關詳情,請參閱虛擬(Mongoose文檔)。

Methods and query helpers

模式還可以具有實例方法 ://mongoosejs.com/docs/guide.html#statics">靜態(tài)方法 幫助">查詢助手。 實例和靜態(tài)方法是相似的,但有一個明顯的區(qū)別,即實例方法與特定記錄相關聯(lián),并且可以訪問當前對象。 查詢助手允許您擴展mongoose的可鏈接查詢構建器API (例如,允許您添加查詢 "byName"除了 find() findOne() findById()方法)。

使用模型

一旦創(chuàng)建了模式,就可以使用它來創(chuàng)建模型。 模型表示數據庫中您可以搜索的文檔集合,而模型的實例表示可以保存和檢索的單個文檔。

我們在下面提供一個簡要概述。 有關詳情,請參閱:模型(Mongoose文檔)。

Creating and modifying documents

要創(chuàng)建記錄,您可以定義模型的實例,然后調用 save()。 下面的示例假設SomeModel是一個模型(具有單個字段"名稱"),我們已從我們的模式創(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!
});

請注意,創(chuàng)建記錄(以及更新,刪除和查詢)是異步操作 - 您提供在操作完成時調用的回調。 API使用錯誤第一個參數約定,因此回調的第一個參數將始終為錯誤值(或null)。 如果API返回一些結果,這將作為第二個參數提供。

您還可以使用 create()在保存模型實例的同時定義模型實例。 回調將返回第一個參數的錯誤,并為第二個參數的新創(chuàng)建的模型實例返回錯誤。

SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
  if (err) return handleError(err);
  // saved!
});

每個模型都有一個關聯(lián)的連接(這將是使用 mongoose.model()時的默認連接)。 您創(chuàng)建一個新的連接并調用 .model()就可以在不同的數據庫上創(chuàng)建文檔。

您可以使用點語法訪問此新記錄中的字段,并更改值。 您必須調用 save() update()將修改的值存回數據庫。

// 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!
?  });

Searching for records

您可以使用查詢方法搜索記錄,將查詢條件指定為JSON文檔。 下面的代碼片段顯示了如何在網球數據庫中找到所有運動員,僅返回運動員姓名 的字段。 這里我們只指定一個匹配字段(運動),但您可以添加更多的條件,指定正則表達式條件,或刪除條件,以返回所有運動員。

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.
})

如果指定回調,如上所示,查詢將立即執(zhí)行。 回調將在搜索完成時調用。

注意:Mongoose中的所有回調都使用回調(error,result)模式。 如果在執(zhí)行查詢時發(fā)生錯誤,則 error 參數將包含錯誤文檔, result 將為null。 如果查詢成功,則 error 參數將為null,并且 result 將填充查詢結果。

如果您未指定回調,API將返回類型為 Query >。 您可以使用此查詢對象構建您的查詢,然后使用 exec()方法執(zhí)行(使用回調)。

// 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
})

上面我們在 find()方法中定義了查詢條件。 我們也可以使用 where()函數來做到這一點,我們可以使用點運算符(。)將查詢的所有部分鏈接在一起,而不是單獨添加它們。 下面的代碼片段與上面的查詢相同,有一個額外的年齡條件。

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()方法會獲取所有匹配的記錄,但通常您只想獲取 一個匹配。 以下方法查詢單個記錄:

請注意:還有 count()方法 您可以使用它來獲取符合條件的項目數。 如果要在不實際獲取記錄的情況下執(zhí)行計數,這將非常有用。

有很多你可以做的查詢。 有關詳細信息,請參閱:查詢(Mongoose文檔)。

您可以使用 ObjectId 模式字段,或者從一個文檔到許多使用 ObjectIds 數組的一個文檔/模型實例到另一個文檔/模型實例創(chuàng)建引用。 該字段存儲相關模型的ID。 如果您需要相關文檔的實際內容,可以使用填充() >方法在查詢中用實際數據替換id。

例如,以下模式定義了作者和故事。 每個作者可以有多個故事,我們表示為 ObjectId 的數組。 每個故事可以有一個作者。 "ref"(下面以粗體突出顯示)告訴模式哪個模型可以分配給此字段。

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 值來保存對相關文檔的引用。 下面我們創(chuàng)建一個作者,然后一本書,并將作者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)在有一個由作者文檔ID引用的作者。 為了在我們的故事結果中獲得作者信息,我們使用 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"
});

注意:精明的讀者會注意到我們?yōu)槲覀兊墓适绿砑恿艘晃蛔髡?,但我們沒有做任何事情將我們的故事添加到作者的 stories 數組中。 那么我們如何能得到一個特定作者的所有故事? 一種方法是將我們的作者添加到故事數組中,但這將導致我們有兩個地方需要維護有關作者和故事的信息。

更好的方法是獲取作者 _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.
});

這幾乎是您在使用本教學課程相關項目時需要了解的一切 。 有關詳細信息,請參閱人口(Mongoose文檔)。

每個文件一個模式/模型

雖然可以使用任何您喜歡的文件結構創(chuàng)建模式和模型,但我們強烈建議在其自己的模塊(文件)中定義每個模型模式,導出方法以創(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 );

然后,您可以在其他文件中立即要求并使用模型。 下面我們展示如何使用它來獲取模型的所有實例。

//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);

設置MongoDB數據庫

現(xiàn)在我們了解了Mongoose可以做什么,以及我們如何設計我們的模型,現(xiàn)在是開始在 LocalLibrary 網站上工作的時候了。 我們首先要做的是設置一個MongoDb數據庫,我們可以使用它來存儲我們的庫數據。

在本教程中,我們將使用 mLab 的免費云托管的" > sandbox "數據庫。 此數據庫層不適合生產網站,因為它沒有冗余,但它是偉大的開發(fā)和原型。 我們在這里使用它,因為它是免費和容易設置,并且因為mLab是流行的數據庫作為服務供應商,你可以合理地選擇為您的生產數據庫(當時其他流行的選擇 的寫作包括撰寫, .html"> ScaleGrid MongoDB Atlas )。

注意:如果您愿意,可以在本地設置MongoDB數據庫,方法是下載并安裝 適合您系統(tǒng)的二進制文件。 本文中的其余說明將類似,但連接時要指定的數據庫URL除外。

您首先需要使用mLab 創(chuàng)建帳戶(這是免費的,只需輸入基本的聯(lián)系方式 并確認其服務條款)。

登錄后,您將轉到首頁屏幕:

  1. Click Create New in the MongoDB Deployments section.
  2. This will open the Create new deployment screen.
    • Select Single-node in the Plan section
    • Select the Sandbox radio button in the Standard Line section
    • Enter a Database Name - for example "local_library"
  3. Click the Create new MongoDB deployment button.

    創(chuàng)建數據庫后,您將返回查看您的MongoDb部署的概述。height:266px; width:800px;">

  4. 單擊新數據庫(如上面突出顯示)以打開包含數據庫的更多詳細信息的視圖。 您可以看到數據庫沒有集合(數據)。
    width:800px;">
    您需要用于訪問數據庫的URL顯示在上面的表單上(針對上面圈出的此數據顯示)。 為了使用這個,你需要創(chuàng)建一個數據庫用戶,你可以在URL中指定。

  5. Click the Users tab and select the Add database user button.
  6. Enter a username and password (twice), and then press Create. Do not select Make read only.

您現(xiàn)在已經創(chuàng)建了數據庫,并且具有可用于訪問它的URL(具有用戶名和密碼)。 這將看起來像: mongodb:// your_user_namer:your_password@ds119748.mlab.com:19748 / local_library 。

安裝Mongoose

打開命令提示符,并導航到您創(chuàng)建骨架本地圖書館網站的目錄。 輸入以下命令以安裝Mongoose(及其依賴項)并將其添加到您的 package.json 文件中,除非您在閱讀 Mongoose Primer / a>。

npm install mongoose --save

連接到MongoDB

打開 /app.js (在項目的根目錄下),并在下面復制以下文本,您在此聲明 Express應用程序對象( express(); )。 將數據庫url字符串(\' insert_your_database_url_here \')替換為您自己的數據庫的位置(即使用來自從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引導中,此代碼創(chuàng)建與數據庫的默認連接,并綁定到錯誤事件(以便錯誤將打印到控制臺)。

定義LocalLibrary模式

我們將為每個模型定義一個單獨的模塊,如上所述的 。 首先在項目根目錄中為我們的模型創(chuàng)建一個文件夾( / models ),然后為每個模型創(chuàng)建單獨的文件:

/express-locallibrary-tutorial  //the project root
  /models
    author.js
    book.js
    bookinstance.js
    genre.js

作者模型

復制下面顯示的作者模式代碼,并將其粘貼到您的 ./ models / author.js 文件中。 該方案定義了作者對于第一個和家族名稱具有 String SchemaTypes,這是必需的并且最多有100個字符,以及 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);

我們還針對名為"url"的類型聲明了一個虛擬,它返回獲取模型的特定實例所需的絕對網址 - 我們將使用 每當我們需要獲得到特定作者的鏈接時。

請注意:在模式中將我們的網址聲明為虛擬網址是一個好主意,因為這樣一來,只需要在一個地方更改項目的網址。
此時,使用此網址的鏈接 將不工作,因為我們沒有任何路由處理代碼為個別模型實例。 我們將在后面的文章中設置它們!

在模塊的最后,我們導出模型。

書模型

復制下面顯示的 Book 模式代碼,并將其粘貼到您的 ./ models / book.js 文件中。 其中大部分類似于作者模型 - 我們已經聲明了一個帶有多個字符串字段的模式和一個用于獲取特定書記錄的URL的模式,并且我們已經導出了模型。

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ū)別是我們已經創(chuàng)建了對其他模型的兩個引用(以粗體突出顯示):

  • author is a reference to a single Author model object, and is required.
  • genre is a reference to an array of Genre model objects. We haven't declared this object yet!

BookInstance模型

最后,復制下面顯示的 BookInstance 模式代碼,并將其粘貼到 ./ models / bookinstance.js 文件中。 BookInstance 表示某人可能借用的書的特定副本,并包括有關副本是否可用或其預期的日期,"印記"或版本詳細信息的信息。

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);

我們在這里顯示的新東西是字段選項:

  • 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!)

其他一切都應該從我們以前的模式熟悉。

類型模型 - 挑戰(zhàn)!

打開您的 ./ models / genre.js 文件,并創(chuàng)建存儲類型(圖書類別,例如它是小說還是非小說,浪漫或軍事歷史等)的架構。

其定義將與其他模型非常相似:

  • The model should have a String SchemaType called name to describe the genre.
  • This name should be required and have between 3 and 100 characters.
  • Declare a virtual for the genre's URL, named url.
  • Export the model.

測試 - 創(chuàng)建一些項目

而已。 我們現(xiàn)在有所有型號的網站設置!

為了測試模型(并創(chuàng)建一些示例書和其他項目,我們可以在下一篇文章中使用),我們現(xiàn)在將運行一個獨立腳本來創(chuàng)建每種類型的項目:

  1. Download (or otherwise create) the file populatedb.js in the root of your locallibrary website (in the level above your /models folder).

    注意:您不需要知道 "> populatedb.js 在這一點上工作。 我們將在后面的文章中解釋相關的部分。

  2. Enter the following commands in the project root to install the?async module?that is required by the script (we'll discuss this in later tutorials, )
    npm install async --save
  3. Run the script using node in your command prompt, passing in the URL of your MongoDB database:
    node populatedb <your mongodb url>????
  4. The script should run through to completion, displaying items as it creates them in the terminal.

提示: mLab 上轉到您的數據庫。 現(xiàn)在,您應該可以深入查看圖書,作者,流派和BookInstances的各個集合,并查看單個文檔。

概要

在本文中,我們已經了解了一些關于Node / Express上的數據庫和ORM,以及有關如何定義Mongoose模式和模型的內容。 然后我們使用這些信息來設計和實現(xiàn) Book , BookInstance Author Genre LocalLibrary 網站。

最后,我們通過創(chuàng)建多個實例(使用獨立腳本)來測試我們的模型。 在下一篇文章中,我們將介紹創(chuàng)建一些頁面來顯示這些對象。

也可以看看

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號