web-dev-qa-db-fra.com

Pourquoi Mongoose a-t-il à la fois des schémas et des modèles?

Les deux types d'objets semblent si proches l'un de l'autre que le fait d'avoir les deux semble redondant. Quel est l'intérêt d'avoir les deux schémas et modèles?

85
Randomblue

Souvent, la façon la plus simple de répondre à ce type de question est avec un exemple. Dans ce cas, quelqu'un l'a déjà fait pour moi :)

Jetez un œil ici:

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

EDIT: Le message d'origine (comme mentionné dans les commentaires) ne semble plus exister, donc je le reproduis ci-dessous. Si jamais il revient, ou s'il vient de déménager, faites-le moi savoir.

Il donne une description décente de l'utilisation des schémas dans les modèles dans mangouste et pourquoi vous souhaitez le faire, et vous montre également comment pousser des tâches via le modèle alors que le schéma concerne la structure, etc.

Message d'origine:

Commençons par un exemple simple d'intégration d'un schéma dans un modèle.

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

J'ai créé un nouvel objet TaskSchema avec les informations de base qu'une tâche peut avoir. Un Mongoose attribut virtuel est configuré pour combiner facilement le nom et la priorité de la tâche. J'ai seulement spécifié un getter ici, mais les setters virtuels sont également pris en charge.

J'ai également défini une méthode de tâche simple appelée isHighPriority pour montrer comment les méthodes fonctionnent avec cette configuration.

Dans la définition ListSchema, vous remarquerez comment la clé de tâches est configurée pour contenir un tableau d'objets TaskSchema. La clé de tâche deviendra une instance de DocumentArray qui fournit des méthodes spéciales pour traiter les documents Mongo intégrés.

Pour l'instant, je n'ai passé que l'objet ListSchema dans mongoose.model et laissé le TaskSchema à l'extérieur. Techniquement, il n'est pas nécessaire de transformer le TaskSchema en un modèle formel car nous ne l'enregistrerons pas dans sa propre collection. Plus tard, je vais vous montrer comment cela ne fait rien de mal si vous le faites et cela peut aider à organiser tous vos modèles de la même manière, surtout lorsqu'ils commencent à s'étendre sur plusieurs fichiers.

Avec la configuration du modèle List, ajoutons-y quelques tâches et les enregistrons dans 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'); 
    }
});

L'attribut de tâches sur l'instance de notre modèle List (simpleList) fonctionne comme un tableau JavaScript standard et nous pouvons y ajouter de nouvelles tâches à l'aide de Push. La chose importante à noter est que les tâches sont ajoutées en tant qu'objets JavaScript normaux. C'est une distinction subtile qui peut ne pas être immédiatement intuitive.

Vous pouvez vérifier à partir du shell Mongo que la nouvelle liste et les nouvelles tâches ont été enregistrées dans 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" }

Maintenant, nous pouvons utiliser le ObjectId pour tirer le Sample List et parcourir ses tâches.

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

Si vous exécutez ce dernier morceau de code, vous obtiendrez une erreur indiquant que le document incorporé n'a pas de méthode isHighPriority. Dans la version actuelle de Mongoose, vous ne pouvez pas accéder directement aux méthodes sur les schémas intégrés. Il existe un ticket ouvert pour le corriger et après avoir posé la question au groupe Google Mongoose, manimal45 a publié une solution de contournement utile à utiliser pour le moment.

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

Si vous exécutez ce code, vous devriez voir la sortie suivante sur la ligne de commande.

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

Avec cette solution à l'esprit, transformons le TaskSchema en un modèle 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');

La définition TaskSchema est la même qu'avant, donc je l'ai laissée de côté. Une fois transformé en modèle, nous pouvons toujours accéder à son objet Schema sous-jacent en utilisant la notation par points.

Créons une nouvelle liste et incorporons-y deux instances de modèle de tâche.

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

Alors que nous intégrons les instances du modèle de tâche dans la liste, nous appelons toObject sur elles pour convertir leurs données en objets JavaScript simples que le List.tasksDocumentArray attend. Lorsque vous enregistrez des instances de modèle de cette manière, vos documents incorporés contiennent ObjectIds.

L'exemple de code complet est disponible sous forme de Gist . Espérons que ces solutions de contournement aideront à faciliter les choses à mesure que Mongoose continue de se développer. Je suis encore assez nouveau sur Mongoose et MongoDB, alors n'hésitez pas à partager de meilleures solutions et astuces dans les commentaires. Bonne modélisation des données!

57
Adam Comerford

Le schéma est un objet qui définit la structure de tous les documents qui seront stockés dans votre collection MongoDB; il vous permet de définir des types et des validateurs pour tous vos éléments de données.

Le modèle est un objet qui vous donne un accès facile à une collection nommée, vous permettant d'interroger la collection et d'utiliser le schéma pour valider tous les documents que vous y enregistrez. collection. Il est créé en combinant un schéma, une connexion et un nom de collection.

Formulé à l'origine par Valeri Karpov, Blog MongoDB

47

Je ne pense pas que la réponse acceptée réponde réellement à la question qui a été posée. La réponse n'explique pas pourquoi Mongoose a décidé d'exiger qu'un développeur fournisse à la fois un schéma et une variable de modèle. Un exemple de framework où ils ont éliminé la nécessité pour le développeur de définir le schéma de données est Django - un développeur écrit leurs modèles dans le fichier models.py, et laisse le framework à gérer le schéma. La première raison qui me vient à l'esprit pour cela, étant donné mon expérience avec Django, est la facilité d'utilisation. Peut-être plus important encore est le principe DRY (ne vous répétez pas)) - vous n'avez pas à vous rappeler de mettre à jour le schéma lorsque vous changez de modèle - Django le fera pour vous! Rails gère également le schéma des données pour vous - un développeur ne modifie pas le schéma directement, mais le modifie en définissant des migrations qui manipulent le schéma.

L'une des raisons pour lesquelles je pouvais comprendre que Mongoose séparerait le schéma et le modèle est les instances où vous souhaitez créer un modèle à partir de deux schémas. Un tel scénario pourrait introduire plus de complexité que cela ne vaut la peine d'être géré - si vous avez deux schémas gérés par un modèle, pourquoi ne sont-ils pas un seul schéma?

Peut-être que la question d'origine est plutôt une relique du système de base de données relationnelle traditionnel. Dans le monde NoSQL/Mongo, le schéma est peut-être un peu plus flexible que MySQL/PostgreSQL, et donc changer le schéma est une pratique plus courante.

4
johnklawlor

En termes simples,

Un modèle est un modèle d'objet de données, comme vous le trouverez dans un modèle de conception MVC. Il définit Structure quel type de données doit être stocké dans une base de données et quel type de relation avec les données.

Un schéma est comme un database schema, la définition de ce qui sera stocké dans une base de données.

1
Sajeetharan - MSFT