暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL:使用 Node .js 和 Sequelize 批量加载数据

原创 Ellison 2023-02-14
997

应用程序开发通常需要在数据库中播种数据以进行测试和开发。以下文章将概述如何使用 Node.js 和 Sequelize 来处理这个问题。


无论您是在零用户的情况下从头开始构建应用程序,还是向现有应用程序添加功能,在开发过程中处理数据都是必要的。这可以采用不同的形式,从读取开发中的数据文件的模拟数据 API 到紧密反映预期生产环境的种子数据库部署。

我更喜欢后者,因为我发现与生产工具集的偏差较少,导致错误更少。

谦逊的开始

为了进行此讨论,让我们假设我们正在构建一个提供各种编码课程的在线学习平台。在最简单的形式中,我们的 Node.js API 层可能如下所示。

JavaScript

// server.js
const express = require("express");
const App = express();
 
const courses = [
   {title: "CSS Fundamentals", "thumbnail": "https://fake-url.com/css"}],
   {title: "JavaScript Basics", "thumbnail": "https://fake-url.com/js-basics"}],
   {title: "Intermediate JavaScript", "thumbnail": "https://fake-url.com/intermediate-js"}
];
 
App.get("/courses", (req, res) => {
   res.json({data: courses});
});
 
App.listen(3000);


如果您只需要几个项目即可开始构建 UI,那么这足以开始。调用端点将返回此文件中定义的所有课程。但是,如果我们想从更能代表成熟的数据库支持应用程序的数据集开始测试呢?/courses

使用 JSON

假设我们继承了一个脚本,导出一个包含数千个课程的 JSON 数组。我们可以像这样导入数据。

JavaScript

// courses.js
 
module.exports = [
   {title: "CSS Fundamentals", "thumbnail": "https://fake-url.com/css"}],
   {title: "JavaScript Basics", "thumbnail": "https://fake-url.com/js-basics"}],
   {title: "Intermediate JavaScript", "thumbnail": "https://fake-url.com/intermediate-js"},
   ...
];
 
// server.js
 
...
const courses = require("/path/to/courses.js");
...


这消除了在服务器文件中定义模拟数据的需要,现在我们有大量的数据可以使用。我们可以通过添加参数来对结果进行分页并设置返回记录数的限制来增强端点。但是,允许用户发布自己的课程呢?编辑课程怎么样?

当您开始添加功能时,此解决方案很快就会失控。我们必须编写额外的代码来模拟关系数据库的功能。毕竟,创建数据库是为了存储数据。所以,让我们这样做。

使用续集批量加载 JSON

对于这种性质的应用程序,PostgreSQL是一个合适的数据库选择。我们可以选择在本地运行PostgreSQL,也可以连接到与PostgreSQL兼容的云原生数据库,如YugabyteDB Managed。除了是一个高性能的分布式SQL数据库之外,使用YugabyteDB的开发人员还可以从可以由多个用户共享的集群中受益。随着应用程序的增长,我们的数据层可以横向扩展到多个节点和区域。

在创建YugabyteDB托管帐户并启动免费数据库集群后,我们准备使用Sequelize为数据库设定种子并重构代码。续集ORM允许我们对数据进行建模以创建数据库表并执行命令。这是它的工作原理。

首先,我们从终端安装 Sequelize。

// terminal
> npm i sequelize


接下来,我们使用 Sequelize 建立与数据库的连接,创建一个表,并用数据为我们的表播种。

JavaScript

// database.js
 
// JSON-array of courses
const courses = require("/path/to/courses.js");
 
// Certificate file downloaded from YugabyteDB Managed
const cert = fs.readFileSync(CERTIFICATE_PATH).toString();
 
// Create a Sequelize instance with our database connection details
const Sequelize = require("sequelize");
const sequelize = new Sequelize("yugabyte", "admin", DB_PASSWORD, {
   host: DB_HOST,
   port: "5433",
   dialect: "postgres",
   dialectOptions: {
   ssl: {
       require: true,
       rejectUnauthorized: true,
       ca: cert,
   },
   },
   pool: {
   max: 5,
   min: 1,
   acquire: 30000,
   idle: 10000,
   }
});
 
// Defining our Course model
export const Course = sequelize.define(
   "course",
   {
       id: {
           type: DataTypes.INTEGER,
           autoIncrement: true,
           primaryKey: true,
       },
       title: {
           type: DataTypes.STRING,
       },
 
       thumbnail: {
           type: DataTypes.STRING,
       },
   }
);
 
 
async function seedDatabase() {
   try {
       // Verify that database connection is valid
       await sequelize.authenticate();
 
       // Create database tables based on the models we've defined
       // Drops existing tables if there are any
       await sequelize.sync({ force: true });
 
       // Creates course records in bulk from our JSON-array
       await Course.bulkCreate(courses);
 
       console.log("Courses created successfully!");
   } catch(e) {
       console.log(`Error in seeding database with courses: ${e}`);
   }
}
 
// Running our seeding function
seedDatabase();


通过利用Sequelize的bulkCreate方法,我们能够在一个语句中插入多个记录。这比像这样一次插入一个请求的性能更高。

JavaScript

. . .
// JSON-array of courses
const courses = require("/path/to/courses.js");
 
async function insertCourses(){
	for(let i = 0; i < courses.length; i++) {
		await Course.create(courses[i]); 
	}
}
 
insertCourses();


单个插入带有一次性连接、发送请求、解析请求、索引、关闭连接等的开销。当然,连接池可以缓解其中一些问题,但一般来说,批量插入的性能优势是巨大的,更不用说更方便了。bulkCreate 方法甚至带有一个基准测试选项,用于将查询执行时间传递给日志记录函数(如果性能是主要考虑因素)。

现在我们的数据库已经播种了记录,我们的 API 层可以使用这个 Sequelize 模型来查询数据库并返回课程。

JavaScript

// server.js
 
const express = require("express");
const App = express();
 
// Course model exported from database.js
const { Course } = require("/path/to/database.js")
 
App.get("/courses", async (req, res) => {
   try {
       const courses = await Course.findAll();
       res.json({data: courses});
   } catch(e) {
       console.log(`Error in courses endpoint: ${e}`);
   }
});
App.listen(3000);


嗯,这很容易!我们很快就从静态数据结构转变为功能齐全的数据库。

如果我们以另一种数据格式(例如从Microsoft Excel导出的CSV文件)提供数据集,该怎么办?我们如何使用它来为我们的数据库播种?

使用 CSV

有许多 NPM 包可以将 CSV 文件转换为 JSON,但没有一个像 csvtojson 那样易于使用。首先安装软件包。

// terminal

> npm i csvtojson

接下来,我们使用此包将我们的 CSV 文件转换为 JSON 数组,Sequelize 可以使用该文件。


// courses.csv

title,thumbnail

CSS Fundamentals,https://fake-url.com/css

JavaScript Basics,https://fake-url.com/js-basics

Intermediate JavaScript,https://fake-url.com/intermediate-js


JavaScript

// database.js

...

const csv = require('csvtojson');

const csvFilePath = "/path/to/courses.csv";

 

// JSON-array of courses from CSV

const courses = await csv().fromFile(csvFilePath);

...

await Course.bulkCreate(courses);

...


就像我们格式良好的文件一样,我们能够通过 Sequelize 轻松地将文件转换为批量插入记录。courses.jscourses.csv

结论

使用硬编码数据开发应用程序只能带我们走这么远。我发现在开发过程的早期投资工具会让我走上无错误编码的道路(或者我希望如此!

通过批量加载记录,我们能够在具有代表性的应用程序环境中使用具有代表性的数据集。我相信很多人都同意,这通常是应用程序开发过程中的主要瓶颈。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论