翻译| 《 JavaScript无处不在》第5章 数据库(^_^)

翻译| 《 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"}])

如今咱们已经向数据库中写入了一些文档,让咱们对其进行检索。为此,咱们将使用MongoDBfind方法。要查看集合中的全部文档,请运行查找带有空参数的命令:

$ 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") })

咱们不只但愿可以找到文档,并且可以对其进行更新也颇有用。咱们可使用Mongoupdate方法来作到这一点,该方法接受要更改的文档的第一个参数和第二个参数。让咱们将蔬菜比萨更新为蘑菇比萨:

$ 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" }

与更新文档同样,咱们也可使用Mongoremove方法删除一个文档。让咱们从数据库中删除蘑菇披萨:

$ 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服务器的默认URLmongodb://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": []
  }
}

img

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

img

5-2。修改会在数据库中建立新笔记

若是如今从新运行笔记查询,则应该看到从数据库中检索到的笔记!(请参阅图5-3

img

5-3。咱们的笔记查询返回数据库中的数据。

最后一步是使用MongoDB分配给每一个条目的惟一ID重写note的查询,用于从数据库中提取特定的笔记。为此,咱们将使用MongoosefindbyId方法:

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

img

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使用MongoDBMongoose库。数据库(例如MongoDB)使咱们可以安全地存储和检索应用程序的数据。对象建模库(例如Mongoose)经过提供用于数据库查询和数据验证的工具来简化数据库的工做。在下一章中,咱们将更新API以使数据库内容具备完整的CRUD(建立,读取,更新和删除)功能。

若是有理解不到位的地方,欢迎你们纠错。若是以为还能够,麻烦您点赞收藏或者分享一下,但愿能够帮到更多人。:slightly_smiling_fac

相关文章
相关标签/搜索