Stories

Detail Return Return

MongoDB & Mongoose - Stories Detail

MongoDB 和 Mongoose

mongoose

建立一個 MongoDB Atlas 數據庫並導入連接到它所需的軟件包。將 mongodb@~3.6.0mongoose@~5.4.0 添加到項目的 package.json 中。 然後,在 myApp.js 文件中請求 mongoose。 創建一個 .env 文件,給它添加一個 MONGO_URI 變量。 變量的值為 MongoDB Atlas 數據庫 URI。 應用單引號或雙引號包裹 URI。請記住,環境變量 = 兩邊不能有空格。 例如應該這樣寫:MONGO_URI='VALUE'。 完成後,使用下面的代碼來連接數據庫。

mongoose.connect(<Your URI>, { useNewUrlParser: true, useUnifiedTopology: true });
const mongoose = require('mongoose');
mongoose.connect("mongodb+srv://<username>:<password>@cluster0.zbwns.mongodb.net/myFirstDatabase?retryWrites=true&w=majority", { useNewUrlParser: true, useUnifiedTopology: true });

創建模型 Model

C RUD 第一小節——CREATE

首先,需要一個 Schema, 每一個 Schema 都對應一個 MongoDB 的 collection, 並且在相應的 collection 裏定義 documents 的“樣子”。 Schema 用於組成模型(Model), 可以通過嵌套 Schema 來創建複雜的模型。可以根據模型創建實例,模型實例化後的對象稱為 documents。

handler 函數會在特定事件(比如調用服務器 API)發生時執行。 done() 是一個回調函數,它的作用是在一個異步操作(比如對數據庫進行插入、查詢、更新或刪除)執行完成時,告知可以繼續執行後續的其它代碼。 這與 Node.js 中的處理方式十分類似,在 Node.js 中,在(異步操作)成功時調用 done(null, data),在失敗時調用 done(err)

注意:與遠程服務器進行交互時,需要考慮到發生錯誤的可能!

/* Example */
const someFunc = function(done) {
  //... do something (risky) ...
  if (error) return done(error);
  done(null, result);
};

按下面的原型信息創建一個名為 personSchema 的 schema:

- Person Prototype -
--------------------
name : string [required]
age :  number
favoriteFoods : array of strings (*)

採用 Mongoose 基礎 schema 類型。 如果還想添加更多的鍵,就請使用 required 或 unique 等簡單的驗證器(validators),並設置默認值。 詳情請參考 Mongoose 文檔。

請從 personSchema 創建一個名為 Person 的 model。

const Schema = mongoose.Schema;
const personSchema = new Schema({
  name: { type: String, required: true },
  age: Number,
  favoriteFoods: [String]
});
const Person = mongoose.model("Person", personSchema);

創建並保存一條 Model 記錄

createAndSavePerson 函數中,使用在上一個挑戰中寫好的 Person 構造函數創建 document 實例, 將包含 nameagefavoriteFoods 的對象傳給構造函數, 這些屬性的數據類型必須符合在 personSchema 中定義的類型。 然後在返回的 document 實例上調用方法 document.save()。 同時,按 Node.js 的方式為它傳一個回調函數。 這是一種常見模式,以下所有 CRUD 方法都將這樣的回調函數作為最後一個參數。

/* Example */
// ...
person.save(function(err, data) {
  //   ...do your stuff here...
});
let createAndSavePerson = function(done) {
  let janeFonda = new Person({name: "Jane Fonda", age: 84, favoriteFoods: ["eggs", "fish", "fresh fruit"]});

  janeFonda.save(function(err, data) {
    if (err) return                                             console.error(err);
    done(null, data)
  });
};

model.create() 創建多條記錄

在一些情況下,比如進行數據庫初始化,會需要創建很多 model 實例來用作初始數據。 Model.create() 接受一組像 [{name: 'John', ...}, {...}, ...] 的數組作為第一個參數,並將其保存到數據庫。

let arrayOfPeople = [
  {name: "Frankie", age: 74, favoriteFoods: ["Del Taco"]},
  {name: "Sol", age: 76, favoriteFoods: ["roast chicken"]},
  {name: "Robert", age: 78, favoriteFoods: ["wine"]}
];

let createManyPeople = function(arrayOfPeople, done) {
  Person.create(arrayOfPeople, function (err, people) {
    if (err) return console.log(err);
    done(null, people);
  });
};

model.find() 查詢數據庫

C R UD 第二小節—— Read 查詢

嘗試一種最簡單的用法,Model.find() 接收一個查詢 document(一個 JSON 對象)作為第一個參數,一個回調函數作為第二個參數, 它會返回由匹配到的數據組成的數組。 這個方法支持很多搜索選項, 詳情請參閲文檔。

let findPeopleByName = function(personName, done) {
  Person.find({name: personName}, function (err, personFound) {
    if (err) return console.log(err);
    done(null, personFound);
  });
};

model.findOne() 返回一個單一匹配

Model.findOne()Model.find() 十分類似,但就算數據庫中有很多條數據可以匹配查詢條件,它也只返回一個 document,而不會返回一個數組, 如果查詢條件是聲明為唯一值的屬性,它會更加適用。

let findOneByFood = function(food, done) {
  Person.findOne({favoriteFoods: food}, function (err, data) {
    if (err) return console.log(err);
    done(null, data);
  });
};

model.findById() 根據 _id 搜索

在保存 document 的時候,MongoDB 會自動為它添加 _id 字段,並給該字段設置一個唯一的僅包含數字和字母的值。 通過 _id 搜索是一個十分常見的操作,為此,Mongoose 提供了一個專門的方法。

var findPersonById = function(personId, done) {
  Person.findById(personId, function (err, data) {
    if (err) return console.log(err);
    done(null, data);
  });
};

通過執行查詢、編輯、保存來執行經典更新流程

CR U D 第三小節—— Update 更新

在過去,如果想要編輯 document 並以某種方式使用它(比如放到服務器的返回數據中),就必須執行查找、編輯和保存。 Mongoose 有一個專用的更新方法 Model.update(), 它與底層的 mongo 驅動綁定。 通過這個方法,可以批量編輯符合特定條件的多個 document。但問題在於,這個方法不會返回更新後的 document,而是返回狀態信息。 此外,它直接調用 mongo 的底層驅動,讓處理 model 的驗證變得更加棘手。

const findEditThenSave = (personId, done) => {
  const foodToAdd = 'hamburger';
  Person.findById(personId, (err, person) => {
    if(err) return console.log(err);   person.favoriteFoods.push(foodToAdd);
    person.save((err, updatedPerson) => {
      if(err) return console.log(err);
      done(null, updatedPerson)
    })
  })
};

在 document 中執行新的更新方式——使用 model.findOneAndUpdate()

最近發佈的 mongoose 版本簡化了 document 的更新方式, 但同時,一些高級功能(如 pre/post hook, 驗證)的使用方式也變得和以前不同。因此,在很多情景下,上一個挑戰中提到的老方法其實更常用。 新方法的加入,可以讓我們使用 findByIdAndUpdate() 來進行基於 id 的搜索。

const findAndUpdate = (personName, done) => {
  const ageToSet = 20;

  Person.findOneAndUpdate({name: personName}, {age: ageToSet}, {new: true}, (err, updatedDoc) => {
    if(err) return console.log(err);
    done(null, updatedDoc);
  })
};

提示: 需要返回更新後的 document。 可以把 findOneAndUpdate() 的第三個參數設置為 { new: true } 。 默認情況下,這個方法會返回修改前的數據。

model.findByIdAndRemove 刪除一個 document

CRU D 第四小節—— Delete 刪除

findByIdAndRemovefindOneAndRemove 類似於之前的更新方法, 它們將被刪除的 document 傳給數據庫。 和之前一樣,使用函數參數 personId 作為查詢關鍵字。

修改 removeById 函數,通過 _id 刪除一個人的數據, 可以使用 findByIdAndRemove()findOneAndRemove() 方法。

let removeById = function(personId, done) {
  Person.findByIdAndRemove(
    personId,
    (err, removedDoc) => {
      if(err) return console.log(err);
      done(null, removedDoc);
    }
  ); 
};

model.remove() 刪除多個 document

Model.remove() 可以用於刪除符合給定匹配條件的所有 document。

修改 removeManyPeople 函數,使用 nameToRemove 刪除所有姓名是變量 Model.remove() 的人。 給它傳入一個帶有 name 字段的查詢 document 和一個回調函數。

注意: Model.remove() 不會返回被刪除的 document,而是會返回一個包含操作結果以及受影響的數據數量的 JSON 對象。 不要忘記將它傳入 done() 回調函數,因為我們需要在挑戰的測試中調用它。

const removeManyPeople = (done) => {
  const nameToRemove = "Mary";
  Person.remove({name: nameToRemove}, (err, response) => {
    if(err) return console.log(err);
    done(null, response);
  })
};

鏈式調用輔助查詢函數來縮小搜索結果

如果不給 Model.find()(或者別的搜索方法)的最後一個參數傳入回調函數, 查詢將不會執行。 可以將查詢條件存儲在變量中供以後使用, 也可以通過鏈式調用這類變量來構建新的查詢字段。 實際的數據庫操作會在最後調用 .exec() 方法時執行。 必須把回調函數傳給最後一個方法。 Mongoose 提供了許多輔助查詢函數, 這裏使用最常見的一種。

修改 queryChain 函數來查詢喜歡 foodToSearch 食物的人。 同時,需要將查詢結果按 name 屬性排序, 查詢結果應限制在兩個 document 內,並隱藏 age 屬性。 請鏈式調用 .find().sort().limit().select(),並在最後調用 .exec(), 並將 done(err, data) 回調函數傳入 exec()

let queryChain = function(done) {
  var foodToSearch = "burrito";
  var jsonObject = {favoriteFoods : foodToSearch};
  Person.find(jsonObject).sort({name: 1}).limit(2).select({age: 0}).exec((err, data) => {
    (err) ? done(err) : done(null, data); 
  })
};

Add a new Comments

Some HTML is okay.