Шрифт
Введение в Mongoose для MongoDB и Node.js
Play

Введение в Mongoose для MongoDB и Node.js

Mongoose - это библиотека JavaScript, часто используемая в приложении Node.js с базой данных MongoDB. В данной статье я собираюсь познакомить вас с Mongoose и MongoDB и, что более важно, показать, где их уместно использовать в вашем приложении.

Что такое MongoDB?

Для начала рассмотрим MongoDB. MongoDB - это база данных, которая хранит ваши данные в виде документов. Как правило, эти документы имеют JSON (* JavaScript Object Notation - текстовый формат обмена данными, основанный на JavaScript. Здесь и далее примеч. пер.) - подобную структуру:

Затем документ помещается внутрь коллекции. Например, в вышеуказанном примере документа определяется объект user . Далее этот объект user стал бы, скорее всего, частью коллекции под названием users .

Одна из основных особенностей MongoDB - гибкость структуры её данных. Несмотря на то, что в первом примере объект user имел свойства firstName и lastName , эти свойства могут отсутствовать в других документах user коллекции users . Именно это отличает MongoDB от баз данных SQL (* structured query language — язык структурированных запросов), например, MySQL или Microsoft SQL Server, в которых для каждого объекта, хранящегося в базе данных, необходима фиксированная схема.

За счет способности создавать динамические объекты, которые сохраняются в виде документов в базе данных, в игру вступает Mongoose.

Что такое Mongoose?

Mongoose - это ODM (* Object Document Mapper - объектно-документный отобразитель). Это означает, что Mongoose позволяет вам определять объекты со строго-типизированной схемой, соответствующей документу MongoDB.

Mongoose предоставляет огромный набор функциональных возможностей для создания и работы со схемами. На данный момент Mongoose содержит восемь SchemaTypes (* типы данных схемы), которые может иметь свойство, сохраняемое в MongoDB. Эти типы следующие:

  1. String
  2. Number
  3. Date
  4. Buffer
  5. Boolean
  6. Mixed
  7. ObjectId (* уникальный идентификатор объекта, первичный ключ, _id)
  8. Array

Для каждого типа данных можно:

  • задать значение по умолчанию
  • задать пользовательскую функцию проверки данных
  • указать, что поле необходимо заполнить
  • задать get-функцию (геттер), которая позволяет вам проводить манипуляции с данными до их возвращения в виде объекта
  • задать set-функцию (* сеттер), которая позволяет вам проводить манипуляции с данными до их сохранения в базу данных
  • определить индексы для более быстрого получения данных

Кроме этих общих возможностей для некоторых типов данных также можно настроить особенности сохранения и получения данных из базы данных. Например, для типа данных String можно указать следующие дополнительные опции:

  • конвертация данных в нижний регистр
  • конвертация данных в верхний регистр
  • обрезка данных перед сохранением
  • определение регулярного выражения, которое позволяет в процессе проверки данных ограничить разрешенные для сохранения варианты данны
  • определение перечня, который позволяет установить список допустимых строк

Для свойств типа Number и Date можно задать минимально и максимально допустимое значение.

Большинство из восьми допустимых типов данных должны быть вам хорошо знакомы. Однако, некоторые ( Buffer , Mixed , ObjectId и Array ) могут вызвать затруднения.

Тип данных Buffer позволяет вам сохранять двоичные данные. Типичным примером двоичных данных может послужить изображение или закодированный файл, например, документ в PDF-формате (* формат переносимого документа).

Тип данных Mixed используется для превращения свойства в "неразборчивое" поле (поле, в котором допустимы данные любого типа). Подобно тому, как многие разработчики используют MongoDB для различных целей, в этом поле можно хранить данные различного типа, поскольку отсутствует определенная структура. С осторожностью используйте этот тип данных, поскольку он ограничивает возможности, предоставляемые Mongoose, например, проверку данных и отслеживание изменений сущности для автоматического обновления свойства при сохранении.

Тип данных ObjectId используется обычно для определения ссылки на другой документ в вашей базе данных. Например, если бы у вас имелась коллекция книг и авторов, документ книги мог бы содержать свойство ObjectId , ссылающееся на определенного автора документа.

Тип данных Array позволяет вам сохранять JavaScript-подобные массивы. Благодаря этому типу данных вы можете выполнять над данными типичные JavaScript операции над массивами, например, push, pop, shift, slice и т.д.

Краткое повторение

Перед тем, как двинуться далее и писать код, мне хотелось бы подвести итог того, что мы только что выучили. MongoDB - это база данных, которая позволяет вам сохранять документы с динамической структурой. Эти документы сохраняются внутри коллекции.

Mongoose - это библиотека JavaScript, позволяющая вам определять схемы со строго-типизированными данными. Сразу после определения схемы Mongoose дает вам возможность создать Model (модель), основанную на определенной схеме. Затем модель синхронизируется с документом MongoDB с помощью определения схемы модели.

Сразу после определения схем и моделей вы можете пользоваться различными функциями Mongoose для проверки, сохранения, удаления и запроса ваших данных, используя обычные функции MongoDB. Мы еще рассмотрим это более подробно на конкретных примерах.

Установка MongoDB

До того, как начать создавать схемы и модели Mongoose, нам необходимо установить и настроить MongoDB. Я бы порекомендовал вам зайти на страницу загрузки MongoDB. Имеется несколько различных вариантов установки. Я выбрал Community Server. Данный вариант позволяет вам установить версию, предназначенную именно для вашей операционной системы. Также MongoDB предлагает вариант Enterprise Server и вариант облачной установки. Поскольку целые книги можно было бы написать об установке, настройке и мониторинге MongoDB, я остановился на варианте Community Server.

Как только вы загрузили и установили MongoDB для выбранной вами операционной системы, вам необходимо будет запустить базу данных. Вместо того, чтобы заново изобретать колесо, я хотел бы предложить вам почитать документацию MongoDB об установке MongoDB версии Community.

Я подожду вас, пока вы настроите MongoDB. Как только вы справились с вышесказанным, мы можем перейти к инсталляции Mongoose для соединения с вашей только что установленной базой данных MongoDB.

Установка Mongoose

Mongoose - это библиотека JavaScript. Я собираюсь использовать её в приложении Node.js. Если у вас уже установлен Node.js, то вы можете перейти к следующему разделу. Если же не установлен, я рекомендую вам начать с посещения страницы загрузки Node.js и выбора установщика для вашей операционной системы.

Как только Node.js установлен и настроен, я собираюсь создать новое приложение и затем установить npm (* диспетчер пакетов Node) модуль Mongoose.

После перехода в консоли в папку, куда бы вы хотели установить ваше приложение, вы можете выполнить следующие команды:

При инициализации моего приложения я оставил значения всех запрашиваемых параметров по умолчанию. Теперь я установлю модуль mongoose следующим образом:

После выполнения всех необходимых предварительных условий, давайте подключимся к базе данных MongoDB. Я разместил следующий код в файле index.js, поскольку я выбрал его как стартовую точку моего приложения:

В первой строке кода мы подключаем библиотеку mongoose . Далее я открываю соединение с базой данных, которую я назвал mongoose_basics , используя функцию connect .

Функция connect принимает еще два других необязательных параметра. Второй параметр - объект опций, где вы можете указать, при необходимости, например, username (имя пользователя) и password (пароль). Третий параметр, который также может быть и вторым, если у вас не определены опции, - это функция обратного вызова, которая будет вызвана после попытки соединения с базой данных. Функцию обратного вызова можно использовать двумя способами:

Чтобы избежать потенциальной необходимости введения в JavaScript Promises, я буду использовать первый способ. Ниже приводится обновленный index.js::

В случае ошибки при подключении к базе данных выбрасывается исключение, и все дальнейшее исполнение функции прерывается. При отсутствии ошибки в консоль выводится сообщение об успешном соединении.

Теперь Mongoose установлена и подключена к базе данных под названием mongoose_basics . Мое соединение с MongoDB не использует ни username, ни password, ни пользовательского порта. Если вам необходимо указать эти опции или любую другую при подключении, я рекомендую вам просмотреть документацию Mongoose по подключению. В документации дается объяснение как многих доступных опций, так и процесса создания нескольких соединений, объединения соединений, реплик и т.д.

После удачного соединения давайте перейдем к определению схемы.

Определение Mongoose Schema (* схемы)

В начале статьи я показал вам объект user , который имел два свойства: firstName и lastName . В следующем примере я переделал этот документ в схему:

Это очень простая схема, которая содержит всего лишь два свойства без атрибутов, связанных с ней. Давайте распространим наш пример, сделав свойства first и last name дочерними объектами свойства name . Свойство name будет содержать свойства first и last name. Также я добавлю свойство created типа Date .

Как вы видите, Mongoose позволяет мне создавать очень гибкие схемы со множеством возможных комбинаций организации данных.

В следующем примере я собираюсь создать две новые схемы ( author и book ) и показать вам, как создать связь с другой схемой. Схема book будет содержать ссылку на схему author .

Выше приводится схема author , которая распространяет схему user , что я создал в предыдущем примере. Чтобы связать Author и Book, в схеме author первым свойством указываем _id типа ObjectId . _id - это стандартный синтаксис для обозначения первичного ключа в Mongoose и MongoDB. Далее, как и в схеме user , я определил свойство name , содержащее first и last name автора.

Распространяя схему user , схема author содержит несколько дополнительных свойств типа String . Также я добавил свойство типа Buffer , в котором можно было бы расположить изображение профиля автора. Последнее свойство содержит дату создания автора; однако, вы можете обратить внимание, что оно создано немного по-иному, так как в нем указано значение по умолчанию "сейчас". При сохранении автора в базу данных, данному свойству будет присвоено значение текущей даты/времени.

Чтобы завершить примеры схем, давайте создадим схему book , которая содержит ссылку на автора, за счет использования свойства типа ObjectId .

Схема book содержит несколько свойств типа String . Как было упомянуто ранее, эта схема содержит ссылку на схему author . Схема book также содержит свойство ratings типа Array , чтобы продемонстрировать вам возможности определения схем. Каждый элемент этого массива содержит свойства summary , detail , numberOfStars и created date.

Mongoose дает вам возможность создавать схемы со ссылками на другие схемы или, как в примере выше со свойством ratings , позволяет создавать Array дочерних свойств, который может содержаться в привязанной схеме (author в нашем примере) или же в текущей схеме, как в примере выше (схема book со свойством ratings типа Array ).

Создание и сохранение Mongoose Models (* моделей)

Поскольку на примере схем author и book мы увидели гибкость схемы Mongoose, я собираюсь продолжить использовать их и создать на их основе модели Author и Book .

После сохранения модели в MongoDB создается Document (* документ) с теми же свойствами, что определены в схеме, на основе которой была создана модель.

Чтобы продемонстрировать создание и сохранение объекта, в следующем примере я собираюсь создать несколько объектов: одну модель Author и несколько моделей Book . Сразу после создания эти объекты будут сохранены в MongoDB при помощи метода модели save .

В примере выше я самым бессовестным образом разместил ссылки на две мои новые книги. В начале примера мы создаем и сохраняем jamieObject , созданный при помощи модели Author . В случае ошибки внутри функции save объекта jamieObject приложение выбросит исключение. В случае же отсутствия ошибки внутри функции save будут созданы и сохранены два объекта book. Подобно объекту jamieObject , в этих объектах в случае возникновения ошибки при сохранении выбрасывается исключение. В ином случае в консоль выводится сообщение об успешном сохранении.

Для создания ссылки на Author, оба объекта book ссылаются на первичный ключ _id схемы author в свойстве author схемы book .

Проверка данных перед сохранением

Общепринято наполнение данных для создания модели в форме на веб-странице. По этой причине, хорошо бы проверить эти данные перед сохранением модели в MongoDB.

В следующем примере я обновил предыдущую схему author, добавив проверку данных следующих свойств: firstName , twitter , facebook и linkedin .

Для свойства firstName был задан атрибут required . Теперь при вызове функции save , Mongoose вернет ошибку с сообщением о необходимости указания значения свойства firstName . Я решил сделать свойство lastName без необходимости указания его значения на случай, если авторами в моей базе данных были бы Cher или Madonna (* отсутствует фамилия).

Для свойств twitter , facebook и linkedin используются подобные пользовательские валидаторы. Они проверяются на соответствие начала их значений соответствующему доменному имени социальных сетей. Поскольку это необязательные для заполнения поля, валидатор применяется только в случае поступления данных для этого свойства.

Поиск и обновление данных

Введение в Mongoose не было бы завершенным без примера поиска записи и обновления одного или более свойств этого объекта.

Mongoose предоставляет несколько различных функций для поиска данных определенной модели. Эти функции следующие: find , findOne и findById .

Функции find и findOne получают в качестве аргумента объект, позволяющий осуществлять сложные запросы. Функция же findById получает только одно значение функции обратного вызова (скоро будет пример). В следующем примере я продемонстрирую вам, как можно сделать выборку книг, содержащих в своем названии строку 'mvc'.

Внутри функции find я осуществляю поиск нечувствительной к регистру строки 'mvc' по свойству title . Это осуществляется с помощью того же синтаксиса, что используется для поиска строки в JavaScript.

Функцию find таккже можно "прицепить" к другим методам запроса, например, where , and , or , limit , sort , any и т.д.

Давайте распространим наш предыдущий пример, ограничив количество результатов до пяти первых книг и отсортировав их по дате создания по убыванию. Результатом будут первые пять наиболее новых книг, содержащих в названии строку 'mvc'.

После применения функции find порядок последующих функций не имеет значения, поскольку из всех сцепленных функций формируется единый запрос и функции не выполняются до вызова функции exec .

Как я упоминал ранее, функция findById выполняется немного по-другому. Она выполняется сразу же и принимает в качестве одного из аргументов функцию обратного вызова, и не позволяет сцепливание функций. В следующем примере я запрашиваю необходимого автора по его _id .

У вас значение _id может быть немного другим. Я скопировал значение _id из предыдущего console.log , когда осуществляли поиск книг, содержащих в названии строку 'mvc'.

Сразу после возвращения объекта вы можете изменить любое из его свойств и обновить его. Как только вы внесли необходимые изменения, вы вызываете метод save также, как вы делали и при создании объекта. В следующем примере я распространю пример с функцией findbyId и обновлю свойство linkedin автора.

После успешного получения автора устанавливается значение свойства linkedin и вызывается функция save . Mongoose способна заметить изменение свойства linkedin и передать состояние, обновленное только по модифицированным свойствам, в MongoDB. В случае возникновения ошибки при сохранении будет выброшено исключение и приложение прекратит работу. При отсутствии ошибок в консоль будет выведено сообщение об успешном изменении.

Также Mongoose предоставляет возможность найти объект и сразу обновить его при помощи функций с соответствующими названиями: findByIdAndUpdate и findOneAndUpdate . Давайте обновим предыдущий пример, чтобы показать функцию findByIdAndUpdate в действии.

В предыдущем примере свойства, что мы хотим обновить, передаются в функцию findByIdAndUpdate как объект вторым параметром. При этом функция обратного вызова является третьим параметром. После удачного обновления возвращенный объект author содержит обновленную информацию. Он выводиться в консоль, чтобы мы увидели обновленные свойства автора.

Полный код примера

По ходу статьи мы рассматривали кусочки кода, описывающие работу отдельных действий, например, создание схемы, создание модели и т.д. Давайте теперь воссоединим все воедино в одном полном примере.

Для начала я создал два дополнительных файла: author.js и book.js . Данные файлы содержат соответствующие оределения схем и создание моделей. Последняя строка кода делает модель доступной для использования в файле index.js .

Давайте начнем с файла author.js:

Далее переходим к файлу book.js :

И, наконец, обновленнй файл index.js :

В вышеуказанном примере все действия Mongoose содержатся внутри функции connect . Файлы author и book подключаются при помощи функции require после подключения mongoose .

Если MongoDB запущена, вы теперь можете запустить полное приложение Node.js при помощи следующей команды:

После сохранения некоторых данных в базу я обновил файл index.js , добавив функции поиска, следующим образом:

Опять-таки, вы можете запустить приложение при помощи следующей команды: node index.js .

Резюме

После прочтения данной статьи вы должны быть в состоянии создавать чрезвычайно гибкие схемы и модели Mongoose, осуществлять простую или сложную проверку данных, создавать и обновлять документы и, наконец, осуществлять поиск созданных документов.

Надеюсь, теперь вы чувствуете себя уверенным пользователем Mongoose. Если вы хотите узнать больше о Mongoose, я бы рекомендовал вам изучить Mongoose Guides, в котором объясняются более продвинутые темы, например, population, middleware, promises и т.д.

Удачной охоты (да простят меня мангусты)!

Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.

📎📎📎📎📎📎📎📎📎📎