Рубрики
Без рубрики

Mongoose с Node.js – моделирование данных объектов

В этом руководстве мы будем делать демонстрацию, которое объясняет, как работает Mongoose с Node.js. Мы будем выполнять моделирование объектов и реализовать функциональность Crud.

Автор оригинала: Leandro Cofre.

Вступление

NoSQL принес гибкость в табличный мир баз данных. В частности, MongoDB стал отличным вариантом для хранения неструктурированных документов JSON. Данные начинаются как JSON в UI и проходят очень мало преобразований, которые должны быть сохранены, поэтому мы получаем выгоды от повышенной производительности и снижению времени обработки.

Но NoSQL не означает полное отсутствие структуры. Нам все еще нужно подтвердить и бросать наши данные, прежде чем хранить его, и нам все равно может понадобиться применить какую-то деловую логику. Это место Мангуст заполняет.

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

Мы напишем модель для приложения Genealogy, человек с несколькими личными свойствами, в том числе, в том числе их родители. Мы также посмотрим, как мы можем использовать эту модель для создания и изменения людей и сохранять их в MongoDB.

Что такое мангуст?

Как работает Mongodb

Чтобы понять, что мангуст, мы сначала должны понимать в общих чертах, как работает Mongodb. Основная единица данных, которые мы можем сэкономить в MongoDB, это документ. Хотя сохраняется как двоичный, когда мы запрашиваем базу данных, мы получаем его представление в качестве объекта JSON.

Связанные документы могут быть сохранены в коллекциях, аналогичны таблицам в реляционных базах данных. Именно здесь аналогия заканчивается, потому что мы определяем, что рассмотреть «связанные документы».

MongoDB не будет соблюдать структуру на документах. Например, мы могли бы сохранить этот документ для Человек коллекция:

{
  "name": "Alice"
}

А затем в той же коллекции мы могли бы сохранить, казалось бы, не связанный документ без общих свойств или сооружения:

{
  "latitude": 53.3498,
  "longitude": 6.2603
}

Здесь лежит новинка NoSQL баз данных. Мы создаем значение для наших данных и храните так, как мы считаем лучшим. База данных не навязывает никаких ограничений.

Цель мангусты

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

Mongoose – это пакет NPM для приложений NODEJS. Это позволяет определить схемы для наших данных, чтобы вписаться, а также абстрагируя доступ к MongoDB. Таким образом, мы можем обеспечить все сохраненные документы, которые разделяют структуру и содержат требуемые свойства.

Давайте теперь посмотрим, как определить Схема Отказ

Установка мангуста и создания схемы человека

Давайте запустим проект узла с свойствами по умолчанию и человек схема :

$ npm init -y

С инициализированным проектом, давайте пойдем вперед и установите мангуст Использование NPM :

$ npm install --save mongoose

мангуст будет автоматически включать Монгодб NPM модуль также. Вы не будете использовать это непосредственно самостоятельно. Это будет обрабатываться мангустом.

Работать с мангустом, мы захочем импортировать его в наши сценарии:

let mongoose = require('mongoose');

А затем подключиться к базе данных с:

mongoose.connect('mongodb://localhost:27017/genealogy', {useNewUrlParser: true, useUnifiedTopology: true});

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

Mongoose.connect () Предполагает, что сервер MongoDB работает на месте на порту по умолчанию и без учетных данных. Одним из простых способов иметь MongoDB, так как это Докер :

$ docker run -p 27017:27017 mongo

Созданный контейнер будет достаточно для нас, чтобы попробовать мангусты, хотя данные, сохраненные в MongoDB, не будут постоянными.

Схема и модель человека

После предыдущих необходимых объяснений мы можем сосредоточиться на написании наших человек схема и составление модели от нее.

Схема в Mongoose Maps в коллекции MongoDB и определяет формат для всех документов на эту коллекцию. Все свойства внутри схемы должны иметь назначенные Схемы Отказ Например, имя нашего Человек можно определить таким образом:

const PersonSchema = new mongoose.Schema({
    name:  { type: String},
});

Или даже проще, как это:

const PersonSchema = new mongoose.Schema({
    name: String,
});

Строка один из нескольких Схемы Определяется мангустом. Вы можете найти остальные в Монгусная документация Отказ

Ссылка на другие схемы

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

В нашем примере для представления семейного дерева нам нужно добавить два атрибута нашей схеме:

const PersonSchema = new mongoose.Schema({
    // ...
    mother: { type: mongoose.Schema.Types.ObjectId, ref: 'Person' },
    father: { type: mongoose.Schema.Types.ObjectId, ref: 'Person' },
});

У человека может быть Мать и а Отец Отказ Способ представления этого в мангусе – сохранение идентификатора ссылочного документа, Mongoose.schema.Types.object , а не сам объект.

Ref Свойство должно быть название модели, которую мы ссылаемся. Подробнее мы увидим больше о моделях, но на данный момент достаточно знать, что схема относится только к одной модели, а «Человек» это модель Люди Отказ

Наш случай немного особенный, потому что оба Мать и Отец Будет также содержать лиц, но способ определить эти отношения одинаково во всех случаях.

Встроенная проверка

Все Схемы S поставляется с встроенной проверкой по умолчанию. Мы можем определить пределы и другие требования в зависимости от выбранного Схемы Отказ Чтобы увидеть некоторые примеры, давайте добавим Фамилия , Годррон и Примечания нашему Человек :

const PersonSchema = new mongoose.Schema({
    name: { type: String, index: true, required: true },
    surname: { type: String, index: true },
    yearBorn: { type: Number, min: -5000, max: (new Date).getFullYear() },
    notes: { type: String, minlength: 5 },
});

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

индекс Свойство сделает Mongoose создать индекс в базе данных. Это облегчает эффективное выполнение запросов. Выше, мы определили человека Имя и Фамилия быть указателями. Мы всегда будем искать людей по их имена.

Пользовательская проверка

Встроенный Схемы s Разрешить для настройки. Это специально полезно, когда у нас есть свойство, которое может удерживать только определенные значения. Давайте добавим Фотографии недвижимость нашему Человек , массив URL их фотографии:

const PersonSchema = new mongoose.Schema({
    // ...
    photosURLs: [
      {
        type: String,
        validate: {
          validator: function(value) {
            const urlPattern = /(http|https):\/\/(\w+:{0,1}\w*#)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%#!\-/]))?/;
            const urlRegExp = new RegExp(urlPattern);
            return value.match(urlRegExp);
          },
          message: props => `${props.value} is not a valid URL`
        }
      }
    ],
});

Фотографии это просто массив строк, Фотографии: [строка] Отказ Что делает это особенным свойством, что нам нужна настраиваемая проверка для подтверждения добавленных значений имеют формат URL-адреса Интернета.

валидатор () Функция выше использует регулярное выражение, которое соответствует типичным интернет-URL-адресам, которые должны начинаться с http (s):// Отказ

Если нам нужен более сложный Схемы Мы можем создать Наша собственная , но мы все хорошо ищу, если это уже доступно.

Например, Mongoose-type-url Пакет добавляет пользовательский Схемы что мы могли бы использовать, mongoose.schematypes.url Отказ

Виртуальные свойства

Виртуальные свойства документа, которые не сохраняются в базе данных. Это результат расчета. В нашем примере было бы полезно установить полное имя человека в одну строку, а не разделение в Имя и Фамилия Отказ

Давайте посмотрим, как выполнить это после нашего первоначального определения схемы:

PersonSchema.virtual('fullName').
    get(function() { 
      if(this.surname)
        return this.name + ' ' + this.surname; 
      return this.name;
    }).
    set(function(fullName) {
      fullName = fullName.split(' ');
      this.name = fullName[0];
      this.surname = fullName[1];
    });

Виртуальная собственность Funnname Выше делает некоторые предположения ради простоты: у каждого человека есть как минимум имя или имя и фамилия. Мы бы столкнулись с проблемами, если у человека есть второе имя или сочиненное имя или фамилия. Все эти ограничения могут быть зафиксированы внутри Получить () и Установить () Функции, определенные выше.

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

Промежуточное программное обеспечение

Промежуточное программное обеспечение являются функциями или крючками, которые могут быть выполнены до или после стандартных методов мангусты, как Сохранить () или Найти () Например.

У человека может быть Мать и а Отец Отказ Как мы уже говорили, мы сохраняем эти отношения, сохраняя идентификатор объекта как свойства человека, а не сами объекты. Было бы неплохо заполнить оба свойства с самими объектами вместо только идентификаторов.

Это может быть достигнуто как Pre () Функция, связанная с findone () Метод Mongoose:

PersonSchema.pre('findOne', function(next) {
    this.populate('mother').populate('father');
    next();
});

Вышеуказанная функция должна вызвать функцию, полученную в качестве параметра, Далее () Для того, чтобы поддерживать обработку других крючков.

заполнить () Это метод Mongoose для замены идентификаторов с объектами, которые они представляют, и мы используем его, чтобы получить родителей при поиске только одного человека.

Мы могли бы добавить этот крючок в другие функции поиска, например Найти () Отказ Мы могли даже найти родителей рекурсивно, если мы хотим. Но мы должны обращаться заполнить () С осторожностью, так как каждый звонок – это выборка из базы данных.

Создать модель для схемы

Чтобы начать создание документов на основе схемы нашего человека, последний шаг состоит в том, чтобы составить модель на основе схемы:

const Person = mongoose.model('Person', PersonSchema);

Первый аргумент будет единственное название коллекции, к которой мы имеем в виду. Это значение, которое мы дали Ref Собственность Мать и Отец свойства нашего человека. Второй аргумент – Схема Мы определили ранее.

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

Модель – единственное, на что нам нужно. Мы могли бы даже использовать Module.exports, чтобы сделать лицо доступным в других модулях нашего приложения:

module.exports.Person = mongoose.model('Person', PersonSchema);
module.exports.db = mongoose;

Мы также экспортировали мангуст модуль. Нам понадобится отключение от базы данных до завершения приложения.

Мы можем импортировать модуль таким образом:

const {db, Person} = require('./persistence');

Как использовать модель

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

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

Создавать лиц

Мы можем создать человека, просто делая:

let alice = new Person({name: 'Alice'});

Имя это единственное требуемое свойство. Давайте создадим другого человека, но используя виртуальную собственность на этот раз:

let bob = new Person({fullName: 'Bob Brown'});

Теперь, когда у нас есть наши первые два человека, мы можем создать новый со всеми недвижимостью, включая родителей:

let charles = new Person({
  fullName: 'Charles Brown',
  photosURLs: ['https://bit.ly/34Kvbsh'],
  yearBorn: 1922,
  notes: 'Famous blues singer and pianist. Parents not real.',
  mother: alice._id,
  father: bob._id,
});

Все значения для этого последнего человека устанавливаются на действительные, поскольку проверка будет выстрелить ошибку, как только эта строка выполнена. Например, если мы установили первый URL-адрес фотографий на что-то кроме ссылки, мы получим ошибку:

ValidationError: Person validation failed: photosURLs.0: wrong_url is not a valid URL

Как объясняется ранее, родители были укомплектованы идентификаторами первых двух человек вместо объектов.

Мы создали три человека, но они еще не хранятся в базе данных. Давайте сделаем это следующим:

alice.save();
bob.save();

Операции, которые включают в себя базу данных, являются асинхронными. Если мы хотим дождаться завершения, мы можем использовать Async/a ждать:

await charles.save();

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

Получить один или несколько человек

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

let dbCharles = await Person.findOne({name: 'Charles', surname: 'Brown'}).exec();

findone () Возвращает запрос, поэтому для того, чтобы получить результат, нам нужно выполнить его с помощью EXEC () а затем подождите результат с ждать Отказ

Потому что мы приложили крючок к findone () Метод заполнить родителей человека, мы теперь могли бы получить доступ к ним напрямую:

console.log(dbCharles.mother.fullName);

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

Мы можем получить более одного результата, если мы используем Найти () Метод:

let all = await Person.find({}).exec();

Мы вернемся на массив, который мы можем повторить.

Обновлять людей

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

alice.surname = 'Adams';
charles.photosURLs.push('https://bit.ly/2QJCnMV');
await alice.save();
await charles.save();

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

Удалить людей

Как поиск, удаление может быть сделано для одного или многих людей. Давайте сделаем это следующим:

await Person.deleteOne({name: 'Alice'});
await Person.deleteMany({}).exec();

После выполнения этих двух команд коллекция будет пустой.

Заключение

В этой статье мы видели, как Mongoose может быть очень полезен в наших проектах Nodejs и MongoDB.

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

Полный образец проекта можно найти на Github Отказ