翻译| 《 JavaScript无处不在》第5章数据库(^_^) 前端
写在最前面
你们好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。mongodb
为了提升你们的阅读体验,对语句的结构和内容略有调整。若是发现本文中有存在瑕疵的地方,或者你有任何意见或者建议,能够在评论区留言,或者加个人微信:code
_maomao
,欢迎相互沟通交流学习。shell
(σ゚∀゚)σ..:
*☆哎哟不错哦数据库
翻译 | 《JavaScript Everywhere
》第5
章 数据库(^_^)express
第5章 数据库
当我仍是个孩子时,我痴迷地收集各种运动卡。收集卡片的过程当中很大一部分时间是在整理卡片。我将明星球员放在一个盒子里,有一整个盒子专门放篮球巨星迈克尔·乔丹(Michael Jordan
)的纸牌,其他的纸牌则按运动分类,或者按团队分类。这种组织方法使我可以安全地存储卡牌,并在任何给定时间内轻松找到我要找的卡。我自我感受所知甚少,可是像这样的存储系统实际上就等同于数据库。数据库的核心是容许咱们存储信息并在之后检索它。npm
刚开始进行Web
开发时,我发现数据库使人生畏。我看到不少有关运行数据库和输入晦涩的SQL
命令的说明,这感受就像是一个附加的抽象概念,我没法搞定。值得庆幸的是,我最终跨越了障碍,而且再也不被SQL
表联接所吓倒,所以,若是你仍在踯躅不前,我但愿你知道你是能够浏览数据库的世界的。api
在本书中,咱们将使用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