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

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

Ответ 1

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

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

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 предназначен для удобного объединения имени и приоритета задачи. Я указал только на getter, но поддерживаются виртуальные сеттеры.

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

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

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

С помощью установки 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 вы не можете использовать методы доступа на встроенных схемах напрямую. Theres открыть билет, чтобы исправить его, и после того, как он задал вопрос группе Google Mongoose, 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 такое же, как и раньше, поэтому я его оставил. После того, как он превратился в модель, мы все равно можем получить доступ к его базовому объекту Schema с использованием точечной нотации.

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

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'); 
    }
});

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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