Perché Mongoose ha sia schemi che modelli?


92

I due tipi di oggetti sembrano essere così vicini l'uno all'altro che averli entrambi sembra ridondante. Qual è il punto di avere sia schemi che modelli?

Risposte:


61

Spesso il modo più semplice per rispondere a questo tipo di domanda è con un esempio. In questo caso qualcuno l'ha già fatto per me :)

Dai un'occhiata qui:

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

EDIT: Il post originale (come menzionato nei commenti) sembra non esistere più, quindi lo sto riproducendo di seguito. Se dovesse mai tornare, o se si è appena spostato, fammelo sapere.

Fornisce una descrizione decente dell'uso degli schemi all'interno dei modelli in mangusta e del motivo per cui vorresti farlo, e mostra anche come spingere le attività tramite il modello mentre lo schema riguarda la struttura ecc.

Post originale:

Cominciamo con un semplice esempio di incorporamento di uno schema all'interno di un modello.

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

Ho creato un nuovo TaskSchemaoggetto con informazioni di base che potrebbe avere un'attività. Un attributo virtuale Mongoose è configurato per combinare convenientemente il nome e la priorità dell'attività. Ho solo specificato un getter qui, ma sono supportati anche i setter virtuali.

Ho anche definito un semplice metodo di attività chiamato isHighPriorityper dimostrare come i metodi funzionano con questa configurazione.

Nella ListSchemadefinizione noterai come la chiave delle attività è configurata per contenere un array di TaskSchemaoggetti. La chiave dell'attività diventerà un'istanza della DocumentArrayquale fornisce metodi speciali per gestire i documenti Mongo incorporati.

Per ora ho passato l' ListSchemaoggetto solo a mongoose.model e ho lasciato fuori TaskSchema. Tecnicamente non è necessario trasformarlo TaskSchemain un modello formale poiché non lo salveremo nella sua collezione. Più avanti ti mostrerò come non danneggia nulla se lo fai e può aiutarti a organizzare tutti i tuoi modelli allo stesso modo, specialmente quando iniziano a occupare più file.

Con la Listconfigurazione del modello aggiungiamo un paio di attività e le salviamo in 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'attributo tasks sull'istanza del nostro Listmodel ( simpleList) funziona come un normale array JavaScript e possiamo aggiungervi nuove attività usando push. La cosa importante da notare è che le attività vengono aggiunte come normali oggetti JavaScript. È una sottile distinzione che potrebbe non essere immediatamente intuitiva.

È possibile verificare dalla shell Mongo che il nuovo elenco e le nuove attività siano stati salvati in 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" }

Ora possiamo usare ObjectIdper richiamare Sample Liste iterare attraverso i suoi compiti.

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

Se esegui l'ultimo bit di codice riceverai un errore che dice che il documento incorporato non ha un metodo isHighPriority. Nella versione corrente di Mongoose non è possibile accedere direttamente ai metodi sugli schemi incorporati. C'è un ticket aperto per risolverlo e dopo aver posto la domanda al Mongoose Google Group, manimal45 ha pubblicato un'utile soluzione da utilizzare per ora.

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

Se esegui quel codice dovresti vedere il seguente output sulla riga di comando.

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

Con questa soluzione in mente, trasformiamo il TaskSchemain un modello 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 TaskSchemadefinizione è la stessa di prima, quindi l'ho lasciata fuori. Una volta trasformato in un modello, possiamo ancora accedere all'oggetto Schema sottostante usando la notazione a punti.

Creiamo un nuovo elenco e incorporiamo due istanze del modello di attività al suo interno.

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

Poiché stiamo incorporando le istanze del modello Task nella List, le chiediamo toObjectdi convertire i loro dati in semplici oggetti JavaScript che List.tasks DocumentArraysi aspettano. Quando salvi le istanze del modello in questo modo, i tuoi documenti incorporati conterranno ObjectIds.

L'esempio di codice completo è disponibile come sintesi . Si spera che questi work-around aiutino a smussare le cose mentre Mongoose continua a svilupparsi. Sono ancora abbastanza nuovo per Mongoose e MongoDB, quindi sentiti libero di condividere soluzioni e suggerimenti migliori nei commenti. Buona modellazione dei dati!


3
In genere si consiglia di non inviare collegamenti nudi come risposta alle domande pubblicate in SO poiché il collegamento potrebbe smettere di funzionare (come in questo caso). Almeno copia / incolla e cita le sezioni pertinenti degli articoli a cui ti colleghi.
Behrang Saeedzadeh

1
fatto - era ancora nella cache di Google, quindi relativamente semplice
Adam Comerford

1
Per la cronaca, il problema del metodo del documento incorporato è stato risolto: github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Dakota

5
Non sto cercando di far piovere sulla parata di nessuno, ma questa risposta sembra più un tutorial: rispondere al come, ma non al perché. Pur avendo meno fino voti, ho trovato la seguente risposta molto più utile: stackoverflow.com/a/22950402/26331
aaaidan

2
Ho visto quella risposta (e l'ho votata per favore), questa è stata risposta e accettata più di 2 anni prima. Sono felice che ci sia una risposta migliore da trovare, non piove sulla parata di nessuno e c'è stato un link alla risposta a cui hai fatto riferimento nei commenti della domanda da febbraio 2015, quindi non ho sentito il bisogno di collegarlo da solo
Adam Comerford

54

Lo schema è un oggetto che definisce la struttura di tutti i documenti che verranno archiviati nella raccolta MongoDB; ti consente di definire tipi e validatori per tutti i tuoi elementi di dati.

Il modello è un oggetto che offre un facile accesso a una raccolta denominata, consentendo di interrogare la raccolta e utilizzare lo schema per convalidare i documenti salvati in quella raccolta. Viene creato combinando uno schema, una connessione e un nome di raccolta.

Originariamente formulato da Valeri Karpov, MongoDB Blog


5

Non credo che la risposta accettata risponda effettivamente alla domanda che è stata posta. La risposta non spiega perché Mongoose ha deciso di richiedere a uno sviluppatore di fornire sia una variabile Schema che una variabile Model. Un esempio di framework in cui hanno eliminato la necessità dello sviluppatoreper definire lo schema dei dati è django: uno sviluppatore scrive i propri modelli nel file models.py e lascia che sia il framework a gestire lo schema. La prima ragione che mi viene in mente per cui lo fanno, data la mia esperienza con django, è la facilità d'uso. Forse la cosa più importante è il principio DRY (non ripetere te stesso) - non devi ricordarti di aggiornare lo schema quando cambi il modello - django lo farà per te! Rails gestisce anche lo schema dei dati per te: uno sviluppatore non modifica direttamente lo schema, ma lo modifica definendo migrazioni che manipolano lo schema.

Un motivo per cui ho potuto capire che Mongoose separerebbe lo schema e il modello sono i casi in cui vorresti costruire un modello da due schemi. Un tale scenario potrebbe introdurre una complessità maggiore di quella che vale la pena gestire: se si hanno due schemi gestiti da un modello, perché non sono uno schema?

Forse la domanda originale è più una reliquia del tradizionale sistema di database relazionali. Nel mondo NoSQL / Mongo, forse lo schema è un po 'più flessibile di MySQL / PostgreSQL, e quindi cambiare lo schema è una pratica più comune.


Come se lo schema e il modello non fossero sufficienti Repeating Yourself, si verificano più duplicazioni quando si cerca di mantenere un'interfaccia TypeScript corrispondente e ancora di più quando si crea uno schema GraphQL.
Dan Dascalescu

0

Per capire perché? devi capire cos'è veramente la mangusta?

Bene, la mangusta è una libreria di modellazione dei dati a oggetti per MongoDB e Node JS, che fornisce un livello più alto di astrazione. Quindi è un po 'come la relazione tra Express e Node, quindi Express è uno strato di astrazione sul normale Node, mentre Mongoose è uno strato di astrazione sul normale driver MongoDB.

Una libreria di modellazione dati a oggetti è solo un modo per noi di scrivere codice Javascript che interagirà poi con un database. Quindi potremmo usare un normale driver MongoDB per accedere al nostro database, funzionerebbe perfettamente.

Ma invece usiamo Mongoose perché ci offre molte più funzionalità pronte all'uso, consentendo uno sviluppo più rapido e semplice delle nostre applicazioni.

Quindi, alcune delle funzionalità che Mongoose ci offre gli schemi per modellare i nostri dati e le nostre relazioni, una facile convalida dei dati, una semplice API di query, middleware e molto altro.

In Mongoose, uno schema è il luogo in cui modelliamo i nostri dati, dove descriviamo la struttura dei dati, i valori predefiniti e la convalida, quindi prendiamo quello schema e ne creiamo un modello, un modello è fondamentalmente un involucro dello schema, che ci consente di interfacciarci effettivamente con il database per creare, eliminare, aggiornare e leggere documenti.

inserisci qui la descrizione dell'immagine

Creiamo un modello da uno schema.

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

Secondo la convinzione, la prima lettera del nome di un modello deve essere in maiuscolo.

Creiamo un'istanza del nostro modello che abbiamo creato usando mangusta e schema. inoltre, interagisci con il nostro database.

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

Quindi avere sia schama che modle mongoose ci rende la vita più facile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.