¿Por qué Mongoose tiene tanto esquemas como modelos?

92

Los dos tipos de objetos parecen estar tan cerca uno del otro que tener ambos se siente redundante. ¿Cuál es el punto de tener tanto esquemas como modelos?

Randomblue
fuente

Respuestas:

61

A menudo, la forma más sencilla de responder a este tipo de preguntas es con un ejemplo. En este caso, alguien ya lo ha hecho por mí :)

Eche un vistazo aquí:

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

EDITAR: La publicación original (como se menciona en los comentarios) parece que ya no existe, así que la reproduzco a continuación. Si alguna vez regresa, o si se acaba de mudar, hágamelo saber.

Proporciona una descripción decente del uso de esquemas dentro de modelos en mangosta y por qué querría hacerlo, y también le muestra cómo impulsar tareas a través del modelo mientras que el esquema trata sobre la estructura, etc.

Publicación original:

Comencemos con un ejemplo simple de incrustar un esquema dentro de un modelo.

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

Creé un nuevo TaskSchemaobjeto con información básica que podría tener una tarea. Se configura un atributo virtual de Mongoose para combinar convenientemente el nombre y la prioridad de la tarea. Solo especifiqué un captador aquí, pero los configuradores virtuales también son compatibles.

También definí un método de tarea simple llamado isHighPrioritypara demostrar cómo funcionan los métodos con esta configuración.

En la ListSchemadefinición, notará cómo la clave de tareas está configurada para contener una matriz de TaskSchemaobjetos. La clave de tarea se convertirá en una instancia de la DocumentArrayque proporciona métodos especiales para tratar con documentos de Mongo incrustados.

Por ahora solo pasé el ListSchemaobjeto a mongoose.model y dejé TaskSchema. Técnicamente no es necesario convertir el TaskSchemaen un modelo formal ya que no lo guardaremos en su propia colección. Más adelante le mostraré cómo no daña nada si lo hace y puede ayudar a organizar todos sus modelos de la misma manera, especialmente cuando comienzan a abarcar varios archivos.

Con la Listconfiguración del modelo, agreguemos un par de tareas y guárdelas en 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'); 
    }
});

El atributo tasks en la instancia de nuestro Listmodelo ( simpleList) funciona como una matriz JavaScript normal y podemos agregarle nuevas tareas usando push. Lo importante a tener en cuenta es que las tareas se agregan como objetos JavaScript normales. Es una distinción sutil que puede no ser intuitiva de inmediato.

Puede verificar desde el shell de Mongo que la nueva lista y las tareas se guardaron en 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" }

Ahora podemos usar el ObjectIdpara extraer Sample Liste iterar a través de sus tareas.

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 ejecuta ese último fragmento de código, obtendrá un error que indica que el documento incrustado no tiene un método isHighPriority. En la versión actual de Mongoose, no puede acceder directamente a métodos en esquemas incrustados. Hay un ticket abierto para solucionarlo y, después de plantear la pregunta al Grupo de Google Mongoose, manimal45 publicó una solución útil para usar por ahora.

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 ejecuta ese código, debería ver el siguiente resultado en la línea de comando.

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

Con esa solución alternativa en mente, convierta el TaskSchemamodelo en un modelo 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 TaskSchemadefinición es la misma que antes, así que la dejé fuera. Una vez que se convierte en un modelo, todavía podemos acceder a su objeto Schema subyacente utilizando la notación de puntos.

Creemos una nueva lista e incrustemos dos instancias del modelo de tarea en ella.

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

A medida que incorporamos las instancias del modelo de tareas en la Lista, les pedimos toObjectque conviertan sus datos en objetos JavaScript simples que List.tasks DocumentArrayesperan. Cuando guarde instancias de modelo de esta manera, sus documentos incrustados contendrán ObjectIds.

El ejemplo de código completo está disponible como resumen . Con suerte, estas soluciones ayudan a suavizar las cosas a medida que Mongoose continúa desarrollándose. Todavía soy bastante nuevo en Mongoose y MongoDB, así que no dude en compartir mejores soluciones y consejos en los comentarios. ¡Feliz modelado de datos!

Adam Comerford
fuente
3
En general, se recomienda no enviar enlaces desnudos como respuesta a las preguntas publicadas en SO, ya que el enlace podría dejar de funcionar (como en este caso). Al menos copie / pegue y cite las secciones relevantes de los artículos a los que enlaza.
Behrang Saeedzadeh
1
hecho - todavía estaba en la caché de Google, así que relativamente simple
Adam Comerford
1
Para el registro, se ha solucionado el problema del método de documento integrado: github.com/LearnBoost/mongoose/issues/249#ref-commit-e18077a
Dakota
5
No estoy tratando de llover sobre el desfile de nadie, pero esta respuesta se parece más a un tutorial: respondiendo el cómo, pero no el por qué. A pesar de tener menos votos, encontré la siguiente respuesta mucho más útil: stackoverflow.com/a/22950402/26331
aaaidan
2
He visto esa respuesta (y la voté a favor), esta fue respondida y aceptada más de 2 años antes. Me alegro de que se pueda encontrar una mejor respuesta, no llueve en el desfile de nadie y ha habido un enlace a la respuesta a la que hizo referencia en los comentarios de la pregunta desde febrero de 2015, por lo que no sentí la necesidad de vincularlo yo mismo.
Adam Comerford
54

Schema es un objeto que define la estructura de cualquier documento que se almacenará en su colección MongoDB; le permite definir tipos y validadores para todos sus elementos de datos.

El modelo es un objeto que le brinda fácil acceso a una colección con nombre, lo que le permite consultar la colección y usar el esquema para validar cualquier documento que guarde en esa colección. Se crea combinando un esquema, una conexión y un nombre de colección.

Originalmente redactado por Valeri Karpov, Blog de MongoDB

Zeeshan Hassan Memon
fuente
5

No creo que la respuesta aceptada realmente responda a la pregunta que se planteó. La respuesta no explica por qué Mongoose ha decidido requerir que un desarrollador proporcione tanto una variable de esquema como una de modelo. Un ejemplo de un marco en el que han eliminado la necesidad del desarrollador.para definir el esquema de datos es django: un desarrollador escribe sus modelos en el archivo models.py y deja que el marco administre el esquema. La primera razón que me viene a la mente de por qué hacen esto, dada mi experiencia con django, es la facilidad de uso. Quizás lo más importante sea el principio DRY (no te repitas): no tienes que acordarte de actualizar el esquema cuando cambies el modelo, ¡django lo hará por ti! Rails también administra el esquema de los datos por usted: un desarrollador no edita el esquema directamente, sino que lo cambia definiendo migraciones que manipulan el esquema.

Una razón por la que podría entender que Mongoose separaría el esquema y el modelo son las instancias en las que querría construir un modelo a partir de dos esquemas. Tal escenario podría introducir más complejidad de la que vale la pena administrar: si tiene dos esquemas administrados por un modelo, ¿por qué no son un solo esquema?

Quizás la pregunta original sea más una reliquia del sistema tradicional de bases de datos relacionales. En el mundo NoSQL / Mongo, quizás el esquema sea un poco más flexible que MySQL / PostgreSQL y, por lo tanto, cambiar el esquema es una práctica más común.

johnklawlor
fuente
Como si el esquema frente al modelo no fuera suficiente Repitiéndote, te encuentras con más duplicaciones cuando intentas mantener una interfaz TypeScript coincidente , y aún más cuando creas un esquema GraphQL.
Dan Dascalescu
0

¿Para entender por qué? tienes que entender qué es realmente Mongoose?

Bueno, la mangosta es una biblioteca de modelado de datos de objetos para MongoDB y Node JS, que proporciona un mayor nivel de abstracción. Entonces, es un poco como la relación entre Express y Node, por lo que Express es una capa de abstracción sobre Node regular, mientras que Mongoose es una capa de abstracción sobre el controlador MongoDB regular.

Una biblioteca de modelado de datos de objetos es solo una forma de escribir código Javascript que luego interactuará con una base de datos. Así que podríamos usar un controlador MongoDB normal para acceder a nuestra base de datos, funcionaría bien.

Pero en su lugar usamos Mongoose porque nos brinda mucha más funcionalidad lista para usar, lo que permite un desarrollo más rápido y simple de nuestras aplicaciones.

Por lo tanto, algunas de las características que Mongoose nos brinda esquemas para modelar nuestros datos y relaciones, una validación de datos fácil, una API de consulta simple, middleware y mucho más.

En Mongoose, un esquema es donde modelamos nuestros datos, donde describimos la estructura de los datos, los valores predeterminados y la validación, luego tomamos ese esquema y creamos un modelo a partir de él, un modelo es básicamente un envoltorio alrededor del esquema, lo que nos permite realmente interactuar con la base de datos para crear, eliminar, actualizar y leer documentos.

ingrese la descripción de la imagen aquí

Creemos un modelo a partir de un esquema.

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

De acuerdo con la convención, la primera letra del nombre de un modelo debe estar en mayúscula.

Creemos una instancia de nuestro modelo que creamos usando mongoose y esquema. también, interactúe con nuestra base de datos.

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

Así que tener tanto schama como modle mangosta nos facilita la vida.

Señor
fuente