MongoDB 和 Mongoose
mongoose
建立一个 MongoDB Atlas 数据库并导入连接到它所需的软件包。将 mongodb@~3.6.0 和 mongoose@~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 实例, 将包含 name、age 和 favoriteFoods 的对象传给构造函数, 这些属性的数据类型必须符合在 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 删除
findByIdAndRemove 和 findOneAndRemove 类似于之前的更新方法, 它们将被删除的 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);
})
};









