План
1) Введение
2) Алгоритм бекендной части, проблемы, с которыми я столкнулся
3) Алгоритм фронтовой части, проблемы, с которыми я столкнулся
4) Выводы
Введение
Всем привет, меня зовут Александр, я являюсь фронтенд разработчиком более 4-х лет. В этой статье хочу поделится с вами, как я писал функционал для поиска пользователей в своем проекте на вебсокетах. Также в этой статье будет мои рассуждения об отличии между реляционной и реляционной базой данных, работа с тегами в rtk query.
Алгоритм бекендной части, проблемы, с которыми я столкнулся
Теперь давайте окунемся в то, как лучше вести разработку. В своей рабочей практике я стараюсь сейчас перед тем, как выполнить задачу представить ее выполнение от начала до конечной или промежуточной точки, когда можно будет остановится. Точно также я поступил и в этот раз.
Экшн в контроллере и обработка параметров запроса
Казалось простая задача — запросить данные по поисковому запросу, а давайте разберем подробно, что нужно сделать. Для начала необходимо создать экшен в контроллере, который примет на себя обработку данного запроса, прикрепить обработчики запроса, через которые будет получен доступ к параметрам запроса, добавить обработку гвардов для авторизованных пользователей. И это только начало. У меня это уже ранее было прописано, поэтому по рабочему примеру вышеописанная логика была оперативно добавлена.
Далее перейдем к продумыванию самой логики, прежде чем воплотим ее в жизнь. Мне необходимо прогнать поисковую фразу по всем полям, в рамках которых будет производится поиск, далее необходимо получить всех искомых собеседников, дополнить их последним сообщением в переписке.
Теперь у меня возник технический вопрос в плане реализации поиска:
- по айди пользователя запросить все необходимые данные и потом производить по ним поиск;
- занести все данные в таблицу собеседников и потом работать только сугубо с ней.
С одной стороны правильно запросить все данные из бд и потом работать с массивами. Сначала необходимо запросить данные из коллекции собеседников, а потом — из коллекции сообщений и пользователей.
С другой стороны если положить все в одну коллекцию, то эти данные будет тяжело поддерживать и актуализировать Одно дело в одном месте по необходимости занести данные из другой коллекции, а другое когда это происходит повсеместно. В скором будущем это приведет к аду и большим проблемам с данными, а здесь недалеко и до велосипедов.
По хорошему нужно использовать второй вариант, потому что так задумывалось использование mongodb. Здесь необходимо немного раскрыть тему различия между sql и mongodb базами данных и подходе их работы. В sql данные хранят между таблицами и если эти данные необходимо связать, то для этого используют связи между таблицами. Таких связей существует два вида: один к одному и один ко многим. Один к одному — это связь одной записи к другой записи в таблице по определенному полю. Один ко многим — это связь одной записи ко многим записям из другой таблицы. В mongo таковых связей нет, здесь сразу помещают данные в внутрь объекта, а не выносим в отдельную коллекцию.
Казалось бы, что я не правильно делаю, применяя подход sql базы к mongodb. Возможно, оно так и есть и я не правильно выбрал базу данных для своего проекта. Но мне не нужны четко прописанные структуры, я не хочу тратить время на миграции и тому подобное, то есть для меня взять базу mongodb и применить к ней логику проектирования sql — это нормально, главное, что все работает и это можно поддерживать. Поэтому исхожу из своих потребностей и того, что имеем.
Теперь хочу перейти к самому алгоритму и его реализации.
Алгоритм поиска собеседников по поисковой фразе
Как видно из картинки выше, мой алгоритм состоит из следующих шагов:
- Найти пользователя через заголовок авторизации;
- получить всех существующих собеседников пользователя;
- получить все данных о собеседниках и отфильтровать по поисковой фразе;
- найти сообщения всех отфильтрованных пользователей;
- соединить пользователей с их сообщениями;
- отправить пользователю в нужном виде.
В результате вышеописанного списка у меня получилось для себя построить представление, что мне нужно сделать для реализации списка и куда нужно копать, чтобы закрыть вопрос.
Из таких особенностей, с которыми я столкнулся и переосмысливаю сейчас — это разбиение методов на мелкие функции, которые выполняют одну или две функции. Ранее я делал запрос на получение собеседников без фильтрации и я написал всю логику в одном сервисе. После написания этой функции я заметил, что сервис начал общаться со всеми остальными сервисами и от этого метод класса очень сильно разросся. Поэтому я решил вынести эту логику на уровень контроллера. Здесь она мне больше нравится, потому что контроллер выступает в качестве головного решения, который собирает все нужные данные для работы и потом преобразует их в готовый вид.
Алгоритм фронтовой части, проблемы, с которыми я столкнулся
В алгоритме фронтовой части лежит два шага: получение информации, ее обработка перед отрисовкой и сама отрисовка. Давайте сначала разберем получение и обработку информации. Получение информации происходит через rtk query, все его плюсы и минусы я разбирал ранее, поэтому здесь подробно останавливаться не буду. После того, как я получил информацию, то поставил перед собой задачу, что ее необходимо обработать в одном месте таким образом, чтобы наружу отдать две или три функции, которые будут управлять всем состоянием внутри. Для себя я добился этого следующим способом:
- вынес в санку два вызова для получения списка и для списка, который генерируется по поисковому слову, после запроса готовый результат заносится в глобальный стор;
- создал несколько управляющих хуков: в моем случае хук для управления пагинацией, хук для отдачи готового списка собеседников, и два вспомогательных хука для управления поиском;
- все вышеперечисленные хуки импортируются в компонент, который отрисует содержимое на странице.
Санка для запросов собеседников
Давайте теперь разберем это более подробно. Как ранее упоминал в списке, у меня для запроса используется два запроса: для простого списка и для списка по поисковому слову. Для получения простого списка используется функция fetchInterlocutor и для второго списка — fetchSearchInterlocutor.
Реализация бесконечной загрузки для собеседников
Из интересного в хуках хочу разобрать хук для управления пагинацией и запросов для нее. Сама логика строится на переменной pagination и ее изменении в колбеках. Когда она меняется, то запускается сайд эффект, который кидает запрос на получение/обновление данных, выше разбирали. То есть хук рассчитан, что пользователь при входе всегда получает список собеседников, потом, после ввода данных в текстовое поле, происходит запрос списка, который формируется по поисковому запросу. Из интересного я несколько раз перебирал хук для понимания, как лучше организовать переход между простым списком и списком, который формируется по поисковому запросу. Изначально хотел сделать два отдельных хука для каждого сценария, но после реализации увидел, что эти хуки являются копиями друг друга и было решено их соединить, а запросы определять в отдельной переменной по наличию поискового слова — если слово есть — запрос на поиск, если нет — запрос на простой список.
Получение информации в основном компоненте
На картинке выше видно, что из хука пагинации получаем все необходимые колбеки, которые управляют переходами между запросами данных, а вся логика осталась в хуке, также здесь подключается хук для получения списка собеседников. Дальнейшая реализация — это уже просто технические нюансы, в плане корректного размещения колбека по нужным местам.
Выводы
В этой статье я затронул вопрос логику формирования структуры проекта, что подход сначала все понять про задачу и после этого ее выполнить. В этом случае у нас есть полный контроль над выполнением задачи, понимание, что получили на входе и что необходимо отдать на выходе. В рамках этой статьи хочу еще донести мысль, которая исходит из понимания задачи. Если задача понятна, то в этом случае проще расширять свой продукт, который в дальнейшем будет проще поддерживать. Это очень хорошо здесь видно на примере фронтенд части, где получилось разделить логику сущности и перенести ее в компонент отрисовки.
Всем спасибо, за то что дочитали статью, надеюсь вам все понравилось и вы для себя узнали что-то новое. До встречи в следующих статьях.