共计 9875 个字符,预计需要花费 25 分钟才能阅读完成。
翻译 |《JavaScript 无处不在》第 5 章数据库(^_^)
写在最后面
大家好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。
为了进步大家的浏览体验,对语句的构造和内容略有调整。如果发现本文中有存在瑕疵的中央,或者你有任何意见或者倡议,能够在评论区留言,或者加我的微信:code
_maomao
,欢送互相沟通交流学习。
(σ゚∀゚)σ..:
*☆哎哟不错哦
翻译 |《JavaScript Everywhere
》第 5
章 数据库(^_^)
第 5 章 数据库
当我还是个孩子时,我痴迷地收集各类静止卡。收集卡片的过程中很大一部分工夫是在整顿卡片。我将明星球员放在一个盒子里,有一整个盒子专门放篮球巨星迈克尔·乔丹(Michael Jordan
)的纸牌,其余的纸牌则按静止分类,或者按团队分类。这种组织办法使我可能平安地存储卡牌,并在任何给定工夫内轻松找到我要找的卡。我自我感觉所知甚少,然而像这样的存储系统实际上就等同于数据库。数据库的外围是容许咱们存储信息并在当前检索它。
刚开始进行 Web
开发时,我发现数据库令人生畏。我看到很多无关运行数据库和输出艰涩的 SQL
命令的阐明,这感觉就像是一个附加的抽象概念,我无奈搞定。值得庆幸的是,我最终逾越了阻碍,并且不再被 SQL
表联接所吓倒,因而,如果你仍在踯躅不前,我心愿你晓得你是能够浏览数据库的世界的。
在本书中,咱们将应用 MongoDB
作为咱们的首选数据库。我之所以抉择 Mongo
,是因为它是Node.js
生态系统中的一种风行抉择,并且是适宜任何人入门的杰出数据库。Mongo
将咱们的数据存储在相似于 JavaScript
对象的“文档”中。这意味着咱们将可能以任何 JavaScript
开发人员相熟的格局编写和检索信息。然而,如果你领有一个十分喜爱的数据库(例如PostgreSQL
),那么本书中波及的主题只需做很少的工作即可转移到任何类型的零碎上。
在应用 Mongo
之前,咱们须要确保 MongoDB
服务器在本地运行。这是整个开发过程中必须的。为此,请依照第 1
章中无关零碎的阐明进行操作。
MongoDB 入门
运行 Mongo
,让咱们摸索如何应用Mongo Shell
从终端间接与 Mongo
交互。
首先通过键入以下内容关上MongoDB shell
mongo
命令:
$ mongo
运行此命令后,你应该看到无关 MongoDB Shell
本地服务器连贯的信息,以及打印到终端上的一些其余信息。当初,咱们能够从终端应用程序中间接与 MongoDB
进行交互。咱们能够应用应用命令创立一个数据库。
让咱们创立一个名为 learning
的数据库:
$ use learning
在本章结尾介绍的卡片珍藏中,我将卡片整顿在不同的盒子中。MongoDB
带来了雷同的概念,称为汇合。
汇合是咱们将类似文档组合在一起的形式。例如,博客应用程序可能有一个文章汇合,一个用户汇合和第三个评论汇合。如果将汇合与 JavaScript
对象进行比拟,它将是一个顶级对象,而文档是其中的单个对象。咱们能够像这样将它可视化:
collection: {document: {},
document: {},
document: {}.
...
}
把握了这些信息后,让咱们在学习数据库的汇合中创立一个文档。咱们将创立一个披萨汇合,在其中存储披萨类型的文档。在 MongoDB shell
中输出以下内容:
$ db.pizza.save({type: "Cheese"})
如果胜利,咱们应该看到一个返回的后果:
WriteResult({"nInserted" : 1})
咱们还能够一次将多个条目写入数据库:
$ db.pizza.save([{type: "Veggie"}, {type: "Olive"}])
当初咱们曾经向数据库中写入了一些文档,让咱们对其进行检索。为此,咱们将应用 MongoDB
的find
办法。要查看汇合中的所有文档,请运行查找带有空参数的命令:
$ db.pizza.find()
当初,咱们应该在数据库中看到所有三个条目。除了存储数据外,MongoDB
还主动为每个条目调配一个惟一的ID
。后果应如下所示:
{"_id" : ObjectId("5c7528b223ab40938c7dc536"), "type" : "Cheese" }
{"_id" : ObjectId("5c7529fa23ab40938c7dc53e"), "type" : "Veggie" }
{"_id" : ObjectId("5c7529fa23ab40938c7dc53f"), "type" : "Olive" }
咱们还能够通过属性值以及 Mongo
调配的 ID
查找单个文档:
$ db.pizza.find({type: "Cheese"})
$ db.pizza.find({_id: ObjectId("A DOCUMENT ID HERE") })
咱们不仅心愿可能找到文档,而且可能对其进行更新也很有用。咱们能够应用 Mongo
的update
办法来做到这一点,该办法承受要更改的文档的第一个参数和第二个参数。让咱们将蔬菜比萨更新为蘑菇比萨:
$ db.pizza.update({type: "Veggie"}, {type: "Mushroom"})
当初,如果咱们运行db.pizza.find
(),咱们应该看到你的文档曾经更新:
{"_id" : ObjectId("5c7528b223ab40938c7dc536"), "type" : "Cheese" }
{"_id" : ObjectId("5c7529fa23ab40938c7dc53e"), "type" : "Mushroom" }
{"_id" : ObjectId("5c7529fa23ab40938c7dc53f"), "type" : "Olive" }
与更新文档一样,咱们也能够应用 Mongo
的remove
办法删除一个文档。让咱们从数据库中删除蘑菇披萨:
$ db.pizza.remove({type: "Mushroom"})
当初,如果咱们执行数据库:db.pizza.find
() 查问,咱们只会在汇合中看到两个条目。
如果咱们决定不再心愿蕴含任何数据,则能够在没有空对象参数的状况下运行 remove
办法,这将革除整个汇合:
$ db.pizza.remove({})
当初,咱们曾经胜利地应用 MongoDB Shell
创立数据库,将文档增加到汇合中,更新这些文档并删除它们。当咱们将数据库集成到我的项目中时,这些根本的数据库操作将提供松软的根底。在开发中,咱们还能够应用 MongoDB Shell
拜访数据库。这对于调试、手动删除或更新条目等工作很有帮忙。
将 MongoDB 连贯到咱们的应用程序
当初,你曾经从 shell
中学习了一些无关应用 MongoDB
的常识,让咱们将其连贯到咱们的 API
应用程序。为此,咱们将应用 Mongoose
对象文档映射器(ODM
)。Mongoose
是一个库,它通过应用基于模式的建模解决方案来缩小和简化款式代码,从而简化了在 Node.js
应用程序中应用 MongoDB
的工作量。是的,你没看错 - 另一种模式!如你所见,一旦定义了数据库模式,通过 Mongoose
应用 MongoDB
的形式与咱们在 Mongo Shell
中编写的命令的类型相似。
咱们首先须要更新在咱们本地数据库 URL
的.env
文件。这将使咱们可能在咱们正在应用的任何环境(例如本地开发和生产)中设置数据库URL
。
本地 MongoDB
服务器的默认 URL
为mongodb://localhost:27017
,咱们将在其中增加数据库名称。因而,在咱们.env
文件,咱们将应用 Mongo
数据库实例的 URL
设置一个DB
_HOST 变量,如下所示:
DB_HOST=mongodb://localhost:27017/notedly
在咱们的应用程序中应用数据库的下一步是连贯到该数据库。让咱们写一些代码,在启动时将咱们的应用程序连贯到咱们的数据库。为此,咱们将首先在 src
目录中创立一个名为 db.js
的新文件。在 db.js
中,咱们将编写数据库连贯代码。咱们还将包含一个敞开数据库连贯的性能,这将对测试应用程序很有用。
在 src/db.js
中,输出以下内容:
// Require the mongoose library
const mongoose = require('mongoose');
module.exports = {
connect: DB_HOST => {
// Use the Mongo driver's updated URL string parser
mongoose.set('useNewUrlParser', true);
// Use findOneAndUpdate() in place of findAndModify()
mongoose.set('useFindAndModify', false);
// Use createIndex() in place of ensureIndex()
mongoose.set('useCreateIndex', true);
// Use the new server discovery and monitoring engine
mongoose.set('useUnifiedTopology', true);
// Connect to the DB
mongoose.connect(DB_HOST);
// Log an error if we fail to connect
mongoose.connection.on('error', err => {console.error(err);
console.log('MongoDB connection error. Please make sure MongoDB is running.');
process.exit();});
},
close: () => {mongoose.connection.close();
}
};
当初,咱们将更新 src/index.js
来调用此连贯。为此,咱们将首先导入.env
配置以及 db.js
文件。在导入中,在文件顶部,增加以下导入:
require('dotenv').config();
const db = require('./db');
我喜爱在 env
文件中定义DB
_HOST 值作为一个变量。间接在上面的端口变量定义中增加此变量:
const DB_HOST = process.env.DB_HOST;
而后,通过将以下内容增加到 src/index.js
文件中,能够调用咱们的连贯:
db.connect(DB_HOST);
src/index.js
文件当初如下:
const express = require('express');
const {ApolloServer, gql} = require('apollo-server-express');
require('dotenv').config();
const db = require('./db');
// Run the server on a port specified in our .env file or port 4000
const port = process.env.PORT || 4000;
// Store the DB_HOST value as a variable
const DB_HOST = process.env.DB_HOST;
let notes = [
{
id: '1',
content: 'This is a note',
author: 'Adam Scott'
},
{
id: '2',
content: 'This is another note',
author: 'Harlow Everly'
},
{
id: '3',
content: 'Oh hey look, another note!',
author: 'Riley Harrison'
}
];
// Construct a schema, using GraphQL's schema language
const typeDefs = gql`
type Note {
id: ID
content: String
author: String
}
type Query {
hello: String
notes: [Note]
note(id: ID): Note
}
type Mutation {newNote(content: String!): Note
}
`;
// Provide resolver functions for our schema fields
const resolvers = {
Query: {hello: () => 'Hello world!',
notes: () => notes,
note: (parent, args) => {return notes.find(note => note.id === args.id);
}
},
Mutation: {newNote: (parent, args) => {
let noteValue = {
id: notes.length + 1,
content: args.content,
author: 'Adam Scott'
};
notes.push(noteValue);
return noteValue;
}
}
};
const app = express();
// Connect to the database
db.connect(DB_HOST);
// Apollo Server setup
const server = new ApolloServer({typeDefs, resolvers});
// Apply the Apollo GraphQL middleware and set the path to /api
server.applyMiddleware({app, path: '/api'});
app.listen({port}, () =>
console.log(`GraphQL Server running at http://localhost:${port}${server.graphqlPath}`
)
);
只管理论性能没有更改,然而如果你运行npm run dev
,则应用程序应该胜利连贯到数据库并且运行没有谬误。
从咱们的应用程序读取和写入数据
当初咱们能够连贯到数据库了,让咱们编写从应用程序外部读取数据和向其写入数据所需的代码。Mongoose
容许咱们定义如何将数据作为 JavaScript
对象存储在数据库中,而后咱们能够存储匹配该模型构造的数据并对其进行操作。思考到这一点,让咱们创立咱们的对象,称为 Mongoose
模式。
首先,在咱们的 src
目录中创立一个名为 models
的文件夹来寄存该模式文件。在此文件夹中,创立一个名为 note.js
的文件。在 src/models/note.js
中,咱们将从定义文件的根本设置开始:
// Require the mongoose library
const mongoose = require('mongoose');
// Define the note's database schema
const noteSchema = new mongoose.Schema();
// Define the 'Note' model with the schema
const Note = mongoose.model('Note', noteSchema);
// Export the module
module.exports = Note;
接下来,咱们将在 noteSchema
变量中定义咱们的模式。与内存数据示例相似,目前,咱们以后的构造中将包含笔记的内容以及代表作者的字符串。咱们还将包含为笔记增加工夫戳的选项,当创立或编辑笔记时,工夫戳将主动存储。咱们将持续在笔记构造中增加这些性能。
咱们的 Mongoose
模式的构造如下:
// Define the note's database schema
const noteSchema = new mongoose.Schema(
{
content: {
type: String,
required: true
},
author: {
type: String,
required: true
}
},
{
// Assigns createdAt and updatedAt fields with a Date type
timestamps: true
}
);
数据永久性
咱们将在整个开发过程中更新和更改数据模型,有时会从数据库中删除所有数据。因而,我不倡议应用此 API
存储重要的内容,例如课堂笔记、敌人的生日列表或返回你最喜爱的披萨店的地址导航信息。
当初,咱们的整体 src/models/note.js
文件应如下所示:
// Require the mongoose library
const mongoose = require('mongoose');
// Define the note's database schema
const noteSchema = new mongoose.Schema(
{
content: {
type: String,
required: true
},
author: {
type: String,
required: true
}
},
{
// Assigns createdAt and updatedAt fields with a Date type
timestamps: true
}
);
// Define the 'Note' model with the schema
const Note = mongoose.model('Note', noteSchema);
// Export the module
module.exports = Note;
为了简化将模型导入 Apollo Server Express
应用程序的过程,咱们将向 index.js
文件增加到 src/models
目录中。这会将咱们的模型合并到一个 JavaScript
模块中。只管这不是严格必要的,但随着应用程序和数据库模型的增长,我认为这是一个很好的策略。在 src/models/index.js
中,咱们将导入笔记模型并将其增加到要导出的模块对象中:
const Note = require('./note');
const models = {Note};
module.exports = models;
当初,通过将模块导入到 src/index.js
文件中,咱们能够将数据库模块合并到 Apollo Server Express
利用程序代码中:
const models = require('./models');
导入数据库模块代码后,咱们能够使解析器实现保留和读取数据库的需要,而不是通过寄存在内存中的变量。为此,咱们将重写 notes
查问,用来通过应用从 MongoDB
数据库中提取笔记:
notes: async () => {return await models.Note.find();
},
在服务器运行后,咱们能够在浏览器中拜访 GraphQL Playground
并运行笔记查问:
query {
notes {
content
id
author
}
}
预期后果将是一个空数组,因为咱们尚未向数据库中增加任何数据(图5-1
):
{
"data": {"notes": []
}
}
图5-1
。笔记查问。
更新咱们的 newNote
批改以向咱们的数据库中增加一个笔记,咱们将应用 MongoDB
模块的 create
办法来承受一个对象。
当初,咱们将持续对作者的姓名进行编写:
newNote: async (parent, args) => {
return await models.Note.create({
content: args.content,
author: 'Adam Scott'
});
}
当初,咱们能够拜访 GraphQL Playground
并编写一个批改,该批改将为咱们的数据库增加一个笔记:
mutation {newNote (content: "This is a note in our database!") {
content
author
id
}
}
咱们的批改将返回一个新笔记,其中蕴含咱们放入变量中的内容,作者的姓名以及 MongoDB
生成的ID
(图5-2
)。
图5-2
。批改会在数据库中创立新笔记
如果当初从新运行笔记查问,则应该看到从数据库中检索到的笔记!(请参阅图5-3
)
图5-3
。咱们的笔记查问返回数据库中的数据。
最初一步是应用 MongoDB
调配给每个条目标惟一 ID
重写 note
的查问,用于从数据库中提取特定的笔记。为此,咱们将应用 Mongoose
的findbyId
办法:
note: async (parent, args) => {return await models.Note.findById(args.id);
}
当初,咱们能够应用在笔记查问或 newNote
批改中看到的惟一 ID
查问了,能够从数据库中检索单个笔记。为此,咱们将编写一个带 id
参数的笔记查问(图5-4
):
query {note(id: "5c7bff794d66461e1e970ed3") {
id
content
author
}
}
你的笔记编号
上一个示例中应用的 ID
对于我的本地数据库是惟一的。确保从你本人的查问或批改后果中复制一个ID
。
图5-4
。查问单个笔记
咱们最终的 src/index.js
文件将如下所示:
const express = require('express');
const {ApolloServer, gql} = require('apollo-server-express');
require('dotenv').config();
const db = require('./db');
const models = require('./models');
// Run our server on a port specified in our .env file or port 4000
const port = process.env.PORT || 4000;
const DB_HOST = process.env.DB_HOST;
// Construct a schema, using GraphQL's schema language
const typeDefs = gql`
type Note {
id: ID
content: String
author: String
}
type Query {
hello: String
notes: [Note]
note(id: ID): Note
}
type Mutation {newNote(content: String!): Note
}
`;
// Provide resolver functions for our schema fields
const resolvers = {
Query: {hello: () => 'Hello world!',
notes: async () => {return await models.Note.find();
},
note: async (parent, args) => {return await models.Note.findById(args.id);
}
},
Mutation: {newNote: async (parent, args) => {
return await models.Note.create({
content: args.content,
author: 'Adam Scott'
});
}
}
};
const app = express();
db.connect(DB_HOST);
// Apollo Server setup
const server = new ApolloServer({typeDefs, resolvers});
// Apply the Apollo GraphQL middleware and set the path to /api
server.applyMiddleware({app, path: '/api'});
app.listen({port}, () =>
console.log(`GraphQL Server running at http://localhost:${port}${server.graphqlPath}`
)
);
当初,咱们能够应用 GraphQL API
从数据库读取和写入数据!尝试减少更多的笔记,浏览笔记查问所有的注意事项。并浏览单个笔记查问的信息内容。
论断
在本章中,你学习了如何通过咱们的 API
应用 MongoDB
和Mongoose
库。数据库(例如 MongoDB
)使咱们可能平安地存储和检索应用程序的数据。对象建模库(例如Mongoose
)通过提供用于数据库查问和数据验证的工具来简化数据库的工作。在下一章中,咱们将更新API
以使数据库内容具备残缺的CRUD
(创立,读取,更新和删除)性能。
如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦您点赞珍藏或者分享一下,心愿能够帮到更多人。:slightly_smiling_fac