В жизненном цикле разработки программного обеспечения есть определенный момент времени, когда необходимо сделать выбор базы данных. В этой статье мы прольем свет на Redis и обсудим, почему это отличная база данных. И почему вы должны выбрать ее для реализации в своем следующем проекте.
Что такое Redis?
Redis расшифровывается как REmote DIctionary Server. Это база данных с открытым исходным кодом. Она использует структуру данных in-memory для хранения своих данных. В основном он используется для кэширования и брокеров сообщений. Это определенно хороший вариант для нашего приложения в качестве базы данных.
Redis — это база данных NoSQL, которая хранит данные в парах ключ/значение. В отличие от многих других широко используемых баз данных Redis использует память для хранения своих данных, а не постоянное хранение данных на диске.
Создание экземпляра Redis
Мы будем использовать облачную платформу Redis Enterprise, где мы можем получить щедрый бесплатный план для начала работы с ней. Но мы все еще можем взаимодействовать с базой данных Redis с помощью локальной установки через командную строку. Вы можете создать новую учетную запись в Redis Enterprise и начать внедрение по ссылке здесь.
В процессе подписки убедитесь, что вы добавили модули RediSearch и RedisJSON во время первоначальной настройки. Именно эти два модуля делают Redis мощной базой данных in-memory, которую хочет видеть каждый разработчик: RedisJSON и RediSearch.
После настройки базы данных вы можете получить строку подключения и пароль, которые мы добавим в наш проект через минуту.
Создание проекта
Для удобства мы создадим пустой проект Next.js с нуля с помощью команды и назовем его redis-next-app
npx create-next-app redis-next-app
Для взаимодействия с базой данных Redis нам необходимо установить клиент Redis. В качестве клиентской библиотеки мы будем использовать redis-om. Все еще можно написать необработанный командный API Redis, но проще использовать клиентскую библиотеку, абстрагирующую его.
RedisOM: Redis Object Mapping — это инструментарий высокоуровневой абстракции, который упрощает работу с данными Redis в среде программирования. Это довольно новая библиотека, и она проста в использовании. Библиотеки Redis OM позволяют нам прозрачно сохранять наши доменные объекты в Redis и запрашивать их с помощью свободного, ориентированного на язык API. Она также поддерживает отображение объектов для Redis в Node.js.
Мы создадим файл, который будет состоять из переменных окружения под названием
CONNECTION_URL=redis://default:PASSWORD@HOST:PORT
Значения здесь взяты из бесплатного экземпляра или базы данных Redis, которую мы создали с помощью Redis Enterprise. Более того, я бы настоятельно рекомендовал использовать RedisInsight. Это бесплатное приложение, которое можно скачать и использовать для визуализации данных Redis с помощью таких моделей, как JSON и временные ряды. Вы можете найти его на сайте https://redis.com/redis-enterprise/redis-insight/.
Впоследствии, если нам понадобится просмотреть данные, хранящиеся внутри экземпляра Redis, этот инструмент станет очень удобным.
Создайте новую папку, назовем ее utils и внутри нее назовем newJob. Внутри нее мы напишем скрипт для подключения к нашей базе данных.
import { Client } from 'redis-om';
const client = new Client();
async function connect() {
if (client.isOpen()) return;
await client.open(process.env.CONNECTION_URL);
if (!client.isOpen()) {
// handle issue here
}
}
async function disconnect() {
await client.close();
}
Здесь мы подключились к Redis с помощью клиента. Класс Client имеет методы для открытия, закрытия и выполнения необработанных команд для Redis. Мы создали две функции, которые в основном подключаются к базе данных (только если нет соединения) и отсоединяются от нее. Мы будем использовать эти удобные функции по ходу работы.
Пополнение данных
Чтобы работать с Redis, мы должны получить некоторые данные, сохраненные в нашей базе данных. Для этого мы объединим RedisOM и наш облачный экземпляр Redis и сохраним данные Jobs в нашей базе данных.
Для начала нам нужно сделать некоторое отображение объектов. Мы начнем с создания сущности и схемы. Для нашей схемы у нас будет просто список вакансий.
class Job extends Entity {}
let jobSchema = new Schema(
Job,
{
company: { type: 'string' },
experience: { type: 'string' },
website: { type: 'string' },
title: { type: 'text', textSearch: true },
},
{
dataStructure: 'JSON',
}
);
Сущности — это классы, с которыми мы работаем. Сущности создаются, читаются, обновляются и удаляются. Любой класс, который расширяет Entity, является сущностью.
Схемы определяют поля нашей сущности, их типы и то, как они отображаются внутри Redis. По умолчанию сущности отображаются на документы JSON с помощью RedisJSON, но при необходимости мы можем изменить это на использование хэшей.
Примечание: Мы добавили булево свойство text search true к заголовку, потому что позже мы будем использовать это значение для поиска и запроса наших данных.
После того как мы подготовили нашу сущность и схему, нам нужно создать хранилище. Репозиторий предоставляет средства для добавления CRUD-функций, таких как создание, чтение, запись и удаление сущностей.
export async function postNewJob(param) {
await connect();
const repository = client.fetchRepository(jobSchema);
const newJob = repository.createEntity(param);
const id = await repository.save(newJob);
await disconnect();
return id;
}
Эта функция принимает параметр со значениями, которые мы передадим позже через простую форму пользовательского интерфейса. Сущности, созданные с помощью .createEntity, не сохраняются в Redis, но нам нужно получить все свойства сущности и вызвать метод .save().
Мы также можем создать и сохранить в одном вызове, чтобы сократить время, с помощью метода createAndSave(). Позже мы можем получить доступ к этому объекту с помощью .fetch(), передав ему идентификатор, по которому мы хотим получить доступ.
const jobVacancy = await jobSchema.fetch('**object ID**');
Для выполнения этой задачи нам понадобится API-маршрут, по которому мы можем сделать запрос. Мы создадим новый файл, который сам по себе будет действовать как отдельный маршрут.
import { postNewJob } from '../../utils/newJob';
export default async function handler(req, res) {
const id = await postNewJob(req.body);
if (!id) return res.status(500).send('Error posting a job');
res.status(200).json({ id });
}
Наконец, нам нужно подключить форму для отправки данных формы. Мы воспользуемся ChakraUI, которая является библиотекой react, создающей доступные интерфейсы.
import { useRef } from 'react';
export default function Jobform() {
const formElement = useRef();
const toast = useToast();
const handleSubmit = async (e) => {
e.preventDefault();
const form = new FormData(e.target);
try {
const formData = Object.fromEntries(form.entries());
const res = await fetch('/api/jobs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
if (res.ok) {
await res.json().then(
toast({
title: 'Your new job is added.',
description: "We've added your job to public to see.",
status: 'success',
duration: 6000,
isClosable: true,
})
);
}
formElement.current.reset();
} catch (err) {
console.log(err);
}
};
return (
<>
//all form with fields goes here
</>
);
}
Здесь мы создали обработчик onSubmit, который принимает все данные, заполненные пользователем в полях формы, с соответствующей схемой, которую мы определили. Затем он выполняет POST-запрос к созданной нами конечной точке и сохраняет все данные в базе данных с уведомлением об успехе, окончательно очищая поля формы.
Выполнение операции запроса
После того как мы сохранили приличное количество данных, мы можем выполнить запрос к нашей базе данных. Для выполнения операции запроса нам необходимо использовать другой модуль Redis — RedisSearch (до сих пор мы использовали только модуль RedisJSON).
Использование RedisSearch с RedisOM — это мощная комбинация. Убедитесь, что у нас установлен модуль RedisSearch, чтобы использовать эту возможность. Чтобы начать использовать эту функцию, нам сначала нужно создать индекс. Чтобы создать индекс, просто вызовите .createIndex() в нашем репозитории.
export async function createIndex() {
await connect();
const repository = client.fetchRepository(jobSchema);
await repository.createIndex();
}
Если вы измените схему в любое время, RedisOM автоматически перестроит индекс для вас. Нам понадобится еще одна конечная точка для вызова экспортированной нами функции. Для этого мы создадим новый API-маршрут и назовем его createindex.js
import { createIndex } from '../../utils/newJob';
export default async function handler(_req, res) {
await createIndex();
res.status(200).send('Index is created');
}
Как только мы создадим индекс, вызвав эту конечную точку из браузера по адресу
http://localhost:3000/api/createindex
Мы можем начать поиск и запрос к нашей базе данных. Базовая операция поиска выглядит следующим образом
const allJobs = await jobSchema.search().return.all();
console.log(allJobs);
Это вернет все задания, которые мы сохранили в нашей базе данных до этого момента. Мы можем реализовать строковый поиск, при котором поиск осуществляется по всей строке. Но мы хотим использовать полнотекстовый поиск, который может искать слова, частичные слова и точные фразы в тексте. Например, если мы ищем «яблоко», он будет искать «яблоко», «обезьяна», «приложение» и «яблоко», игнорируя пунктуацию.
Чтобы увидеть его в действии, мы можем создать функцию, которая будет возвращать все совпадения
//Adding full Text search on the job title
export async function getJobs(query) {
await connect();
const repository = client.fetchRepository(jobSchema);
return await repository
.search()
.where('title')
.does.match(query)
.sortBy('title', 'DESC')
.return.all();
}
Нам также нужно создать конечную точку, которая будет вызывать эту функцию. Мы передадим нашей функции объект запроса.
import { getJobs } from '../../utils/newJob';
export default async function handler(req, res) {
const jobs = await getJobs(req.query);
res.status(200).json({ jobs });
}
Реализация Time To Live(TTL)
Одной из замечательных особенностей Redis является хранение хэша или пар ключ/значение в течение ограниченного промежутка времени, которое автоматически удаляется и истекает. TTL обычно указывается в секундах или отрицательном значении, чтобы сигнализировать об ошибке. Мы можем выбрать объект для истечения срока действия в определенный момент времени с помощью метода .expiry().
Чтобы он сработал, нам нужно передать ему идентификатор сущности и время в качестве второго параметра. Это будет выглядеть примерно так
const ttlInSeconds = 12 * 60 * 60 // 12 hours
await studioRepository.expire('***entity ID*', ttlInSeconds)
Это приведет к истечению срока действия сущности с заданным ID через 12 часов временного интервала. Это пригодится в сценарии, когда мы хотим, чтобы какая-то сущность истекала сама по себе через определенный промежуток времени.
До этого момента мы изучили и реализовали два основных модуля Redis — RedisJSON и RedisSearch. Вы можете найти код в репозитории на Github здесь.
Заключение
Это был обзор использования RedisOM для Node.js и изучение того, как он может пригодиться в наших проектах. Мы также можем обратиться к README и документации по API на GitHub, чтобы получить более глубокое понимание библиотеки.
Это относительно новая библиотека с большим количеством полезных функций. Она использует классы для создания сущностей, позволяя нам создавать схемы и легко взаимодействовать с базой данных. Она устраняет необходимость в использовании более надоедливых низкоуровневых команд с помощью удобного интерфейса.
Узнайте больше о Redis
Попробуйте Redis Cloud бесплатно
Посмотрите это видео о преимуществах Redis Cloud перед другими поставщиками Redis.
Redis Developer Hub — инструменты, руководства и учебники по Redis
Графический интерфейс RedisInsight Desktop
Этот пост подготовлен в сотрудничестве с Redis