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

92

Les deux types d'objets semblent être si proches l'un de l'autre que les deux semblent redondants. Quel est l'intérêt d'avoir à la fois des schémas et des modèles?

Randomblue
la source

Réponses:

61

Souvent, le moyen le plus simple de répondre à ce type de question est de donner 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 original (comme mentionné dans les commentaires) semble ne plus exister, donc je le reproduis ci-dessous. Si jamais il revient, ou s'il vient de déménager, merci de me le faire savoir.

Il donne une description décente de l'utilisation des schémas dans les modèles en mangouste et pourquoi vous voudriez 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 original:

Commençons par un exemple simple d'incorporation 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 TaskSchemaobjet avec des informations de base qu'une tâche pourrait avoir. Un attribut virtuel Mongoose est configuré pour combiner de manière pratique le nom et la priorité de la tâche. Je n'ai spécifié qu'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 isHighPrioritypour démontrer comment les méthodes fonctionnent avec cette configuration.

Dans la ListSchemadéfinition, vous remarquerez comment la clé de tâches est configurée pour contenir un tableau d' TaskSchemaobjets. La clé de tâche deviendra une instance de DocumentArraylaquelle fournit des méthodes spéciales pour traiter les documents Mongo incorporés.

Pour l'instant, je n'ai passé l' ListSchemaobjet que dans mongoose.model et j'ai laissé le TaskSchema. Techniquement, il n'est pas nécessaire de transformer le TaskSchemaen un modèle formel puisque nous ne l'enregistrerons pas dans sa propre collection. Plus tard, je vous montrerai comment cela ne nuit à rien si vous le faites et cela peut vous aider à organiser tous vos modèles de la même manière, en particulier lorsqu'ils commencent à couvrir plusieurs fichiers.

Avec la Listconfiguration du modèle, ajoutons-y quelques tâches et sauvegardons-les 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 Listmodèle ( simpleList) fonctionne comme un tableau JavaScript normal et nous pouvons y ajouter de nouvelles tâches en utilisant 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" }

Nous pouvons maintenant utiliser le ObjectIdpour extraire le Sample Listet 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 bit 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 des schémas intégrés. Il y a un ticket ouvert pour le résoudre 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 TaskSchemaen 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 TaskSchemadéfinition est la même qu'avant, donc je l'ai laissée de côté. Une fois qu'il est 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 intégrons-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'); 
    }
});

Lorsque nous intégrons les instances de modèle Task dans la liste, nous leur demandons toObjectde convertir leurs données en objets JavaScript simples que leList.tasks DocumentArray attendus par le. Lorsque vous enregistrez des instances de modèle de cette façon, vos documents incorporés contiendront ObjectIds.

L'exemple de code complet est disponible sous forme de résumé . Espérons que ces solutions de contournement aideront à faciliter les choses alors 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!

Adam Comerford
la source
3
Il est généralement recommandé de ne pas soumettre de liens simples comme réponse aux questions publiées dans SO car le lien pourrait cesser de fonctionner (comme c'est le cas dans ce cas). Au moins copier / coller et citer les sections pertinentes des articles vers lesquels vous créez un lien.
Behrang Saeedzadeh
1
fait - il était toujours dans le cache de Google, donc relativement simple
Adam Comerford
1
Pour mémoire, le problème de la méthode de document intégré a été résolu: github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Dakota
5
Je n'essaie pas de pleuvoir sur le défilé de qui que ce soit, mais cette réponse se lit plus comme un tutoriel: répondre au comment, mais pas au pourquoi. Malgré le nombre de votes moins élevé, j'ai trouvé la réponse suivante beaucoup plus utile: stackoverflow.com/a/22950402/26331
aaaidan
2
J'ai vu cette réponse (et je l'ai votée), celle-ci a été répondue et acceptée plus de 2 ans auparavant. Je suis heureux qu'il y ait une meilleure réponse à trouver, il n'y a pas de pluie sur le défilé de personne et il y a un lien vers la réponse à laquelle vous avez fait référence dans les commentaires de la question depuis février 2015, donc je n'ai pas ressenti le besoin de le lier moi-même
Adam Comerford
54

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 enregistrez dans cette collection. Il est créé en combinant un schéma, une connexion et un nom de collection.

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

Zeeshan Hassan Memon
la source
5

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é de demander à un développeur de fournir à la fois un schéma et une variable de modèle. Un exemple de cadre où ils ont éliminé le besoin de développeur définir le schéma de données est django - un développeur écrit ses modèles dans le fichier models.py et laisse au framework le soin de gérer le schéma. La première raison qui me vient à l'esprit pour expliquer pourquoi ils font cela, compte tenu de mon expérience avec django, est la facilité d'utilisation. Le principe DRY (ne vous répétez pas) est peut-être plus important - 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.

Une des raisons pour lesquelles je pourrais comprendre que Mongoose séparerait le schéma et le modèle est les instances où vous voudriez créer un modèle à partir de deux schémas. Un tel scénario peut introduire plus de complexité qu'il n'en vaut la peine de gérer - si vous avez deux schémas gérés par un modèle, pourquoi ne sont-ils pas un schéma?

La question originale est peut-être plus 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.

johnklawlor
la source
Comme si schéma vs modèle ne suffisait pas à vous répéter, vous rencontrez plus de duplication lorsque vous essayez de maintenir une interface TypeScript correspondante , et encore plus lors de la création d'un schéma GraphQL.
Dan Dascalescu le
0

Pour comprendre pourquoi? vous devez comprendre ce qu'est réellement la mangouste?

Eh bien, la mangouste est une bibliothèque de modélisation de données d'objets pour MongoDB et Node JS, offrant un niveau d'abstraction plus élevé. C'est donc un peu comme la relation entre Express et Node, donc Express est une couche d'abstraction sur Node normal, tandis que Mongoose est une couche d'abstraction sur le pilote MongoDB régulier.

Une bibliothèque de modélisation de données d'objets n'est qu'un moyen pour nous d'écrire du code Javascript qui interagira ensuite avec une base de données. Nous pourrions donc simplement utiliser un pilote MongoDB standard pour accéder à notre base de données, cela fonctionnerait très bien.

Mais à la place, nous utilisons Mongoose car il nous donne beaucoup plus de fonctionnalités prêtes à l'emploi, permettant un développement plus rapide et plus simple de nos applications.

Ainsi, certaines des fonctionnalités de Mongoose nous offrent des schémas pour modéliser nos données et nos relations, une validation facile des données, une API de requête simple, un middleware et bien plus encore.

Dans Mongoose, un schéma est l'endroit où nous modélisons nos données, où nous décrivons la structure des données, les valeurs par défaut et la validation, puis nous prenons ce schéma et en créons un modèle, un modèle est essentiellement un wrapper autour du schéma, ce qui nous permet d'interfacer réellement avec la base de données afin de créer, supprimer, mettre à jour et lire des documents.

entrez la description de l'image ici

Créons un modèle à partir d'un schéma.

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

Selon la convétion, la première lettre d'un nom de modèle doit être en majuscule.

Créons une instance de notre modèle que nous avons créée à l'aide de la mangouste et du schéma. aussi, interagissez avec notre base de données.

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

Donc, avoir à la fois schama et modle mangouste nous facilite la vie.

Seigneur
la source