概述

根据_Meik Wiking_(《记忆的艺术》的作者)的说法,快乐的记忆对我们的心理健康至关重要。它们增强了我们的认同感和目的感,并加强了我们的关系。快乐的回忆是当下幸福的重要组成部分。因此,这催生了我的项目,Memories App,它允许每个人在任何时间点记录他们的记忆。

事不宜迟,让我们导航到项目详细信息。

序言 Redis 是一个 NoSQL 数据库,因其简单性和速度而备受喜爱。在这篇博文中,我们将使用 Redis 数据库构建一个全栈应用程序来存储数据。我们将使用允许创建、检索、更新和执行全文搜索的 Redis-OM 功能构建我们的模型。如果您不熟悉 Redis 数据库,那完全可以。

首先:

什么是 Redis? 嗯,Redis 是一种内存键值存储,通常用作缓存,以使传统数据库更快。然而,它已经发展成为一个多模型数据库,能够进行全文搜索、图形关系、人工智能工作负载等。

什么是 RedisJSON? RedisJSON 是一个 Redis 模块,它在 Redis 中提供 JSON 支持。 RedisJSON 允许您在 Redis 中存储、更新和检索 JSON 值,就像使用任何其他 Redis 数据类型一样。 RedisJSON 还可以与 RediSearch 无缝协作,让您可以索引和查询 JSON 文档。这是使用 Redis 数据库的最佳方式。

什么是 Redis-OM Redis OM(发音为 REDiss OHM)是一个为 Redis 提供对象映射的库——这就是 OM 的意思......对象映射。它将 Redis 数据类型(特别是哈希和 JSON 文档)映射到 JavaScript 对象。它允许您搜索这些哈希和 JSON 文档。它使用 RedisJSON 和 RediSearch 来做到这一点。

RedisJSON 添加了 JSON 文档数据类型和操作它的命令,RediSearch 添加了各种搜索命令来索引 JSON 文档和哈希的内容。

让我们使用 Redis 构建并利用这些 Redis-OM 功能!

设置

启动 Redis 首先,我们将设置我们的环境。然后,出于开发目的,我们将使用 Docker 来运行 Redis Stack:

docker run -p 10001:6379 -p 13333:8001 redis/redis-stack:latest

这将启动您的 Redis 服务器,一旦启动,继续构建后端服务器

初始化您的应用程序

  • 在您的项目目录中,导航到终端并运行以下命令:
mkdir server
cd server
npm run init -y
  • 这会在你的服务器文件夹中创建一个 package.json 和 package-lock.json 文件,现在你可以开始安装你的依赖了。我们将安装以下软件包:redis-omcorsexpressnodemon 以及其他依赖项

  • 运行以下命令安装依赖

npm i cors express nodemon redis-om bcrypt body-parser colors dotenv express express-validator jsonwebtoken nodemon redis redis-om
  • 在进行应用程序的最后一部分之前,让我们创建我们的 env 文件,在服务器文件夹中创建 .env 并添加
PORT=3005
REDIS_HOST="redis://localhost:6379"

JWT_TOKEN_SECRET= "add your preferred jwt secret"
JWT_EXPIRE="add the expiry"
  • 将 dotenv 作为依赖项的一部分安装后,我们可以开始在应用程序中使用环境变量数据。

创建并连接 Redis 客户端

  • 要创建和连接 Redis 客户端,在 server 文件夹中创建 config 文件夹并创建一个名为 connectToRedis.js 的文件
config/connectToRedis.js

import { Client } from "redis-om";
import dotenv from "dotenv";
const url = process.env.REDIS_HOST;
let client;
try {
  client = await new Client().open(url);
console.log("##########################################################");
  console.log("#####            REDIS STORE CONNECTED               #####");
  console.log("##########################################################\n");
} catch (err) {
  console.log(`Redis error: ${err}`.red.bold);
}
export default client;

创建模型/存储库

  • 在server文件夹中创建一个名为model的文件夹,分别添加post.js和user.js这两个文件。
model/post.js 

import { Entity, Schema } from "redis-om";
import client from "../config/connectToRedis.js";
class Post extends Entity {}

const postSchema = new Schema(
  Post,
  {
    title: { type: "text" },
    message: { type: "text" },
    name: { type: "string" },
    creator: { type: "string" },
    tags: { type: "text" },
    selectedFile: { type: "string" },
    likes: { type: "string[]" },
    comments: { type: "string[]" },
    createdAt: { type: "date", sortable: true },
  },
  {
    dataStructure: "JSON",
  }
);

export const postRepository = client.fetchRepository(postSchema);
await postRepository.createIndex();
model/user.js

import { Entity, Schema } from "redis-om";
import client from "../config/connectToRedis.js";

class User extends Entity {}

const userSchema = new Schema(
  User,
  {
    name: {
      type: "string",
    },
    email: {
      type: "string",
    },
    password: {
      type: "string",
    },
  },
  {
    dataStructure: "JSON",
  }
);

export const userRepository = client.fetchRepository(userSchema);
await userRepository.createIndex();
  • 对于这两种模式,我们定义了一个实体。实体是在您使用它时保存您的数据的类——被映射到的东西。它是您创建、阅读、更新和删除的内容。任何扩展 Entity 的类都是实体。

  • 我们还为两者定义了模式;架构定义实体上的字段、它们的类型以及它们如何在内部映射到 Redis。默认情况下,实体映射到 JSON 文档。

注意:在字段类型中,文本字段很像字符串。不同之处在于,字符串字段只能在其整个值上匹配——不能部分匹配——并且最适合键,而文本字段启用了全文搜索并针对人类可读的文本进行了优化。

  • 我们还为每个创建了一个存储库。 Repository 是 Redis OM 的主要接口。它为我们提供了读取、写入和删除特定实体的方法

最后...

  • 我们创建了一个索引,以便我们能够在每个存储库中搜索数据。我们通过调用 .createIndex() 来做到这一点。如果一个索引已经存在并且它是相同的,这个函数不会做任何事情。如果它不同,它将删除它并创建一个新的

设置路线

  • 创建一个路由文件夹并添加包含我们应用程序路由的posts.js 和users.js 文件。它应该像我们下面的内容:
routes/posts.js 

import express from "express";
const router = express.Router();
import { getPosts, getPost, getPostsBySearch, createPost, updatePost, deletePost, likePost, commentPost, getMyPosts, getSuggestedPosts, } from "../controller/posts.js";
import validator from "../middleware/validator.js";
import auth from "../middleware/auth.js";
import schema from "../validation/post.js";
const { postSchema } = schema;

router.route("/").get(auth, getMyPosts).post(auth, validator(postSchema), createPost);
router.route("/all").get(getPosts);
router.get("/search", getPostsBySearch);
router.get("/suggestion", getSuggestedPosts);
router.route("/:id").patch(auth, updatePost).delete(auth, deletePost);
router.get("/:id", getPost);
router.patch("/:id/comment", commentPost);
router.patch("/:id/like", auth, likePost);

export default router;
routes/users.js 

import express from "express";

const router = express.Router();

import { signin, signup } from "../controller/user.js";
import validator from "../middleware/validator.js";
import schema from "../validation/user.js";
const { userSchema } = schema;

router.route("/signin").post(signin);
router.route("/signup").post(validator(userSchema), signup);

export default router;

请注意,验证器是使用 express 验证器完成的,因此我们导入了帖子和用户验证模式,并确保在将请求发送到端点时首先验证我们的数据。您可以在此处访问必要的验证文件验证文件和验证器中间件

设置 Index.js 文件

在服务器文件夹中创建 Index.js 文件

index.js

import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
import dotenv from "dotenv";
import colors from "colors";
import client from "./config/connectToRedis.js";
import postRoutes from "./routes/posts.js";
import { errorHandler, notFound } from "./middleware/error.js";
import userRoutes from "./routes/users.js";

const app = express();

dotenv.config();

//Body Parser
app.use(bodyParser.json({ limit: "30mb", extended: true }));
app.use(bodyParser.urlencoded({ limit: "30mb", extended: true }));

app.use(cors());

app.get("/", (req, res) => {
  res.json({ message: "Hello to Memories API" });
});
app.use("/posts", postRoutes);
app.use("/user", userRoutes);

const PORT = process.env.PORT || 5000;

const server = app.listen(PORT, () => {
  console.log("##########################################################");
  console.log("#####               STARTING SERVER                  #####");
  console.log("##########################################################\n");
  console.log(`server running on → PORT ${server.address().port}`.yellow.bold);
});

process.on("uncaughtException", (error) => {
  console.log(`uncaught exception: ${error.message}`.red.bold);
  process.exit(1);
});

process.on("unhandledRejection", (err, promise) => {
  console.log(`Error ${err.message}`.red.bold);
  server.close(() => process.exit(1));
});

app.use(notFound);

app.use(errorHandler);

在这里,我们使用了不同的中间件,并在文件中导入了我们连接的 Redis 客户端以及创建的路由。

哎呀,同志,我们到了那里!,进入我们应用程序的最后一部分!,

设置控制器

这是您的应用程序逻辑所在的位置。对于用户注册,我们将使用 JWT 对凭据进行签名,并使用 bcrypt 对密码进行加密,然后再将其存储到我们的数据库中。

  • 从 /register 路由,我们将:

  • 获取用户输入。

  • 验证用户是否已经存在。

  • 加密用户密码。

  • 在我们的数据库中创建一个用户。

  • 最后,创建一个签名的 JWT 令牌。

controller/user.js

export const signin = async (req, res) => {
  const { email } = req.body;
  const existingUser = await userRepository.search().where("email").is.equalTo(email).return.first();
  //check if user exists
  if (!existingUser) {
    return res.status(404).json({ message: "User not found." });
  }
  //check for correct password
  const isPasswordCorrect = await bcrypt.compare(req.body.password, existingUser.password);
  if (!isPasswordCorrect) {
    return res.status(404).json({ message: "invalid Credentials" });
  }
  //create auth token
  const token = jwt.sign({ email: existingUser.email, id: existingUser.entityId }, process.env.JWT_TOKEN_SECRET, {
    expiresIn: process.env.JWT_EXPIRE,
  });
  const { entityId, password, ...rest } = existingUser.toJSON();
  const data = { id: existingUser.entityId, ...rest };
  res.status(200).json({ result: data, token });
};
  • 使用 Postman 测试端点,注册成功后会得到如下响应。

注册响应

在整个应用程序中,请注意 id 如何替换 Redis-OM 为我们提供的 entityId 以匹配传统响应。

 const { entityId, password, ...rest } = user.toJSON();
 const data = { id: user.entityId, ...rest };
  • 用于登录路由

  • 获取用户输入。

  • 验证用户。

  • 最后,创建并发送一个签名的 JWT 令牌。

controller/user.js

export const signin = async (req, res) => {
  const { email } = req.body;
  const existingUser = await userRepository.search().where("email").is.equalTo(email).return.first();
  //check if user exists
  if (!existingUser) {
    return res.status(404).json({ message: "User not found." });
  }
  //check for correct password
  const isPasswordCorrect = await bcrypt.compare(req.body.password, existingUser.password);
  if (!isPasswordCorrect) {
    return res.status(404).json({ message: "invalid Credentials" });
  }
  //create auth token
  const token = jwt.sign({ email: existingUser.email, id: existingUser.entityId }, process.env.JWT_TOKEN_SECRET, {
    expiresIn: process.env.JWT_EXPIRE,
  });
  const { entityId, password, ...rest } = existingUser.toJSON();
  const data = { id: existingUser.entityId, ...rest };
  res.status(200).json({ result: data, token });
};
  • 使用 Postman 测试端点,登录成功后会得到如下响应。

登录响应

😊 并且用户可以开始创建他或她的记忆

好吧,让我们深入了解后控制器...

创建帖子

controller/post.js

export const createPost = async (req, res) => {
  const post = req.body;
  const newPost = await postRepository.createAndSave({
    ...post,
    creator: req.userId,
    createdAt: new Date().toISOString(),
  });

  const { entityId, ...rest } = newPost.toJSON();
  res.status(201).json({ data: { id: newPost.entityId, ...rest } });
};

创建后响应

获取分页帖子

controller/post.js

export const getPosts = async (req, res) => {
  const { page } = req.query;
  const limit = 8;
  const offset = (page - 1) * limit;
  if (!page) {
    res.status(400).json({ message: "Enter count and offset" });
  }
  const posts = await postRepository.search().sortDescending("createdAt").return.page(offset, limit);
  const postsCount = await postRepository.search().sortDescending("createdAt").return.count();
  const newPosts = posts.map((item) => {
    const { entityId, ...rest } = item.toJSON();
    return { id: item.entityId, ...rest };
  });

  res.status(200).json({ data: newPosts, currentPage: Number(page), numberOfPages: Math.ceil(postsCount / limit) });
};

获取分页帖子

获取帖子

controller/post.js

export const getPost = async (req, res) => {
  const data = await postRepository.fetch(req.params.id);
  if (!data.title) {
    return res.status(404).json({ message: "No post with that id" });
  }
  const { entityId, ...rest } = data.toJSON();
  res.status(200).json({ data: { id: data.entityId, ...rest } });
};

得到一个帖子

删除帖子

controller/post.js

export const deletePost = async (req, res) => {
  const post = await postRepository.fetch(req.params.id);
  if (!post.title) {
    return res.status(404).json({ message: "No post with that id" });
  }
  if (req.userId !== post.creator) {
    return res.status(400).json({ message: "Unauthorized, only creator of post can delete" });
  }
  await postRepository.remove(req.params.id);
  res.status(200).json({ message: "post deleted successfully" });
};

删除帖子回复

就像一个帖子

controller/post.js

export const likePost = async (req, res) => {
  let post = await postRepository.fetch(req.params.id);
  if (!post.title) {
    return res.status(400).json({ message: "No post with that id" });
  }
  if (!post.likes) {
    post.likes = [];
  }
  const index = post.likes.findIndex((id) => id === String(req.userId));
  if (index === -1) {
    post.likes = [...post.likes, req.userId];
  } else {
    post.likes = post?.likes?.filter((id) => id !== String(req.userId));
  }
  await postRepository.save(post);
  const { entityId, ...rest } = post.toJSON();
  res.status(200).json({ data: { id: post.entityId, ...rest } });
};

点赞回复

  • 我们将在这里停止对我们的 post 端点的请求。

Redis 云平台

  • 最后一点,让我们浏览一下 Redis 云平台

  • 在Redis上创建一个账户并登录。

  • 导航到仪表板以创建新订阅。您可以从免费套餐开始,稍后根据您的应用使用情况进行升级。

  • 创建一个名为Memories App的新数据库创建数据库

仪表板

注意端点在与该项目一起准备的前端应用程序中使用

投稿类别:

  • MEAN/MERN Maverick:Redis 作为主数据库而不是 MongoDB(即将 MEAN/MERN 中的“M”替换为 Redis 的“R”)。

项目短视频说明:

  • Youtube

使用的技术

  • JS/Node.js

  • 快递js

  • JSON Web 令牌

  • ReactJs, Redux

  • 材质界面

代码链接

  • Github

部署

要使部署工作,您需要在Redis Cloud上创建免费帐户

Heroku

部署的后端链接

网络化

前端记忆应用

其他资源/信息

  • Redis-OM Node Github Repo

  • Redis OM for Node.js

  • 观看此视频,了解 Redis Cloud 相对于其他 Redis 提供商的优势

  • Redis Developer Hub - 关于 Redis 的工具、指南和教程

截图

Image1

Image2Image3Image4Image5

  • 查看Redis OM,用于将 Redis 用作多模型数据库的客户端库。

  • 使用RedisInsight在 Redis 中可视化您的数据。

  • 注册一个免费的 Redis 数据库.

结论

在这里,我们来到了应用程序的结尾,我们学习了如何使用 NodeJS 和 Redis OM 创建执行 crud 操作的 API,并通过存储 JSON 文档和检索它们来利用 RedisJson 功能和 RedisSearch 功能来执行查询和完整\ _文本_搜索。

Logo

Redis社区为您提供最前沿的新闻资讯和知识内容

更多推荐