Почему у Mongoose есть и схемы, и модели?

94

Эти два типа объектов кажутся настолько близкими друг к другу, что наличие обоих кажется излишним. Какой смысл иметь и схемы, и модели?

Randomblue
источник

Ответы:

61

Часто самый простой способ ответить на этот вопрос - это привести пример. В данном случае кто-то уже сделал это за меня :)

Взгляните сюда:

http://rawberg.com/blog/nodejs/mongoose-orm-nested-models/

РЕДАКТИРОВАТЬ: исходный пост (как упоминалось в комментариях), похоже, больше не существует, поэтому я воспроизводю его ниже. Если он когда-нибудь вернется или только что переехал, пожалуйста, дайте мне знать.

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

Исходное сообщение:

Начнем с простого примера встраивания схемы в модель.

var TaskSchema = new Schema({
    name: String,
    priority: Number
});

TaskSchema.virtual('nameandpriority')
    .get( function () {
        return this.name + '(' + this.priority + ')';
    });

TaskSchema.method('isHighPriority', function() {
    if(this.priority === 1) {
        return true;
    } else {
        return false;
    }
}); 

var ListSchema = new Schema({
    name: String,
    tasks: [TaskSchema]
});

mongoose.model('List', ListSchema);

var List = mongoose.model('List');

var sampleList = new List({name:'Sample List'});

Я создал новый TaskSchemaобъект с базовой информацией, которую может иметь задача. Виртуальный атрибут Mongoose настраивается для удобного сочетания имени и приоритета Задачи. Я указал здесь только геттер, но также поддерживаются виртуальные сеттеры.

Я также определил простой метод задачи, названный, isHighPriorityчтобы продемонстрировать, как методы работают с этой настройкой.

В ListSchemaопределении вы заметите, как ключ задач настроен для хранения массива TaskSchemaобъектов. Ключ задачи станет экземпляром, DocumentArrayкоторый предоставляет специальные методы для работы со встроенными документами Mongo.

На данный момент я только передал ListSchemaобъект в mongoose.model и не включил TaskSchema. Технически нет необходимости превращать модель TaskSchemaв формальную, поскольку мы не будем сохранять ее в собственной коллекции. Позже я покажу вам, как это ничему не повредит, если вы сделаете это, и это может помочь организовать все ваши модели одинаково, особенно когда они начинают охватывать несколько файлов.

После Listнастройки модели добавим к ней пару задач и сохраним их в Mongo.

var List = mongoose.model('List');
var sampleList = new List({name:'Sample List'});

sampleList.tasks.push(
    {name:'task one', priority:1}, 
    {name:'task two', priority:5}
);

sampleList.save(function(err) {
    if (err) {
        console.log('error adding new list');
        console.log(err);
    } else {
        console.log('new list successfully saved'); 
    }
});

Атрибут tasks в экземпляре нашей Listмодели ( simpleList) работает как обычный массив JavaScript, и мы можем добавлять в него новые задачи с помощью push. Важно отметить, что задачи добавляются как обычные объекты JavaScript. Это тонкое различие, которое может быть не сразу интуитивно понятным.

Вы можете проверить из оболочки Mongo, что новый список и задачи были сохранены в mongo.

db.lists.find()
{ "tasks" : [
    {
        "_id" : ObjectId("4dd1cbeed77909f507000002"),
        "priority" : 1,
        "name" : "task one"
    },
    {
        "_id" : ObjectId("4dd1cbeed77909f507000003"),
        "priority" : 5,
        "name" : "task two"
    }
], "_id" : ObjectId("4dd1cbeed77909f507000001"), "name" : "Sample List" }

Теперь мы можем использовать, ObjectIdчтобы открыть Sample Listи перебрать его задачи.

List.findById('4dd1cbeed77909f507000001', function(err, list) {
    console.log(list.name + ' retrieved');
    list.tasks.forEach(function(task, index, array) {
        console.log(task.name);
        console.log(task.nameandpriority);
        console.log(task.isHighPriority());
    });
});

Если вы запустите этот последний фрагмент кода, вы получите сообщение об ошибке, что встроенный документ не имеет метода isHighPriority. В текущей версии Mongoose вы не можете напрямую обращаться к методам встроенных схем. Есть открытый билет, чтобы исправить это, и, задав вопрос группе Mongoose Google, manimal45 опубликовал полезный обходной путь, который можно использовать на данный момент.

List.findById('4dd1cbeed77909f507000001', function(err, list) {
    console.log(list.name + ' retrieved');
    list.tasks.forEach(function(task, index, array) {
        console.log(task.name);
        console.log(task.nameandpriority);
        console.log(task._schema.methods.isHighPriority.apply(task));
    });
});

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

Sample List retrieved
task one
task one (1)
true
task two
task two (5)
false

Помня об этом, давайте превратим модель TaskSchemaв модель Mongoose.

mongoose.model('Task', TaskSchema);

var Task = mongoose.model('Task');

var ListSchema = new Schema({
    name: String,
    tasks: [Task.schema]
});

mongoose.model('List', ListSchema);

var List = mongoose.model('List');

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

Создадим новый список и встроим в него два экземпляра модели Task.

var demoList = new List({name:'Demo List'});

var taskThree = new Task({name:'task three', priority:10});
var taskFour = new Task({name:'task four', priority:11});

demoList.tasks.push(taskThree.toObject(), taskFour.toObject());

demoList.save(function(err) {
    if (err) {
        console.log('error adding new list');
        console.log(err);
    } else {
        console.log('new list successfully saved'); 
    }
});

Когда мы встраиваем экземпляры модели Task в List, мы призываем toObjectих преобразовать их данные в простые объекты JavaScript, которые List.tasks DocumentArrayожидает. Когда вы сохраняете экземпляры модели таким образом, ваши встроенные документы будут содержать ObjectIds.

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

Адам Комерфорд
источник
3
Обычно рекомендуется не отправлять голые ссылки в качестве ответа на вопросы, размещенные в SO, поскольку ссылка может перестать работать (как в этом случае). По крайней мере, скопируйте / вставьте и процитируйте соответствующие разделы статей, на которые вы ссылаетесь.
Behrang Saeedzadeh
1
готово - оно все еще было в кеше Google, так что относительно просто
Адам Комерфорд
1
Для записи, проблема с методом встроенного документа была исправлена: github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Дакота
5
Я не пытаюсь критиковать чей-то парад, но этот ответ больше похож на учебное пособие: отвечая на вопрос как, но не на вопрос почему. Несмотря на меньшее количество голосов «за», я нашел следующий ответ гораздо более полезным: stackoverflow.com/a/22950402/26331
aaaidan
2
Я видел этот ответ (и проголосовал за него), на этот ответ был дан и принят за 2 года до этого. Я рад, что можно найти ответ получше, на чьем-то параде нет дождя, и с февраля 2015 года была ссылка на ответ, на который вы ссылались в комментариях к вопросу, поэтому я не чувствовал необходимости связывать его сам
Адам Комерфорд,
54

Схема - это объект, который определяет структуру любых документов, которые будут храниться в вашей коллекции MongoDB; он позволяет вам определять типы и валидаторы для всех ваших элементов данных.

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

Первоначально сформулировано Валерием Карповым, блог MongoDB

Зишан Хасан Мемон
источник
5

Я не думаю, что принятый ответ действительно отвечает на поставленный вопрос. Ответ не объясняет, почему Mongoose решил потребовать от разработчика предоставить как схему, так и переменную модели. Пример фреймворка, где отпала необходимость в разработчикеДля определения схемы данных используется django - разработчик записывает свои модели в файл models.py и оставляет его фреймворку для управления схемой. Первая причина, которая приходит в голову, почему они это делают, учитывая мой опыт работы с django, - это простота использования. Возможно, более важным является принцип DRY (не повторяйтесь) - вам не нужно помнить об обновлении схемы при изменении модели - django сделает это за вас! Rails также управляет схемой данных за вас - разработчик не редактирует схему напрямую, а изменяет ее, определяя миграции, которые управляют схемой.

Одна из причин, по которой я мог понять, что Mongoose разделяет схему и модель, - это экземпляры, в которых вы хотите построить модель из двух схем. Такой сценарий может внести больше сложности, чем стоит управлять - если у вас есть две схемы, которые управляются одной моделью, почему они не одна схема?

Возможно, исходный вопрос - это скорее пережиток традиционной системы реляционных баз данных. В мире NoSQL / Mongo схема, возможно, немного более гибкая, чем MySQL / PostgreSQL, и поэтому изменение схемы является более распространенной практикой.

Johnklawlor
источник
Как будто схемы и модели недостаточно. Повторение себя приводит к большему дублированию при попытке сохранить соответствующий интерфейс TypeScript , и даже больше при создании схемы GraphQL.
Дэн Даскалеску
0

Чтобы понять почему? вы должны понять, что на самом деле мангуст?

Что ж, мангуст - это библиотека моделирования объектных данных для MongoDB и Node JS, обеспечивающая более высокий уровень абстракции. Это немного похоже на отношения между Express и Node, поэтому Express - это уровень абстракции над обычным Node, а Mongoose - это уровень абстракции над обычным драйвером MongoDB.

Библиотека моделирования объектных данных - это просто способ написать код Javascript, который затем будет взаимодействовать с базой данных. Таким образом, мы могли бы просто использовать обычный драйвер MongoDB для доступа к нашей базе данных, он работал бы нормально.

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

Итак, некоторые из функций Mongoose предоставляют нам схемы для моделирования наших данных и отношений, простую проверку данных, простой API запросов, промежуточное ПО и многое другое.

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

введите описание изображения здесь

Создадим модель из схемы.

const tourSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'A tour must have a name'],
    unique: true,
  },
  rating: {
    type: Number,
    default: 4.5,
  },
  price: {
    type: Number,
    required: [true, 'A tour must have a price'],
  },
});
//tour model
const Tour = mongoose.model('Tour', tourSchema);

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

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

const testTour = new Tour({ // instance of our model
  name: 'The Forest Hiker',
  rating: 4.7,
  price: 497,
});
 // saving testTour document into database
testTour
  .save()
  .then((doc) => {
    console.log(doc);
  })
  .catch((err) => {
    console.log(err);
  });

Таким образом, наличие шама и мангуста делает нашу жизнь проще.

Лорд
источник