Использование RedisJSON и операции RediSearch в Redis

В жизненном цикле разработки программного обеспечения есть определенный момент времени, когда необходимо сделать выбор базы данных. В этой статье мы прольем свет на 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

Оцените статью
devanswers.ru
Добавить комментарий