Por que o Mongoose tem esquemas e modelos?

92

Os dois tipos de objetos parecem estar tão próximos um do outro que ter os dois parece redundante. Qual é o ponto de ter ambos os esquemas e modelos?

Randomblue
fonte

Respostas:

61

Freqüentemente, a maneira mais fácil de responder a esse tipo de pergunta é com um exemplo. Neste caso, alguém já fez isso por mim :)

Dê uma olhada aqui:

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

EDITAR: A postagem original (conforme mencionado nos comentários) parece não existir mais, então estou reproduzindo-a abaixo. Se ele retornar ou se tiver apenas mudado, entre em contato.

Ele fornece uma descrição decente do uso de esquemas dentro de modelos no mongoose e por que você gostaria de fazer isso, e também mostra como enviar tarefas por meio do modelo enquanto o esquema trata da estrutura etc.

Postagem original:

Vamos começar com um exemplo simples de incorporação de um esquema dentro de um 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'});

Criei um novo TaskSchemaobjeto com informações básicas que uma tarefa pode ter. Um atributo virtual Mongoose é configurado para combinar convenientemente o nome e a prioridade da Tarefa. Eu apenas especifiquei um getter aqui, mas os setters virtuais também são suportados.

Eu também defini um método de tarefa simples chamado isHighPrioritypara demonstrar como os métodos funcionam com esta configuração.

Na ListSchemadefinição, você notará como a chave de tarefas é configurada para conter uma série de TaskSchemaobjetos. A chave de tarefa se tornará uma instância DocumentArrayque fornece métodos especiais para lidar com documentos Mongo embutidos.

Por enquanto, eu apenas passei o ListSchemaobjeto para mongoose.model e deixei o TaskSchema de fora. Tecnicamente não é necessário transformar o TaskSchemaem um modelo formal, pois não o estaremos salvando em sua própria coleção. Posteriormente, mostrarei como isso não prejudica nada se você fizer isso e pode ajudar a organizar todos os seus modelos da mesma maneira, especialmente quando eles começam a se espalhar por vários arquivos.

Com a Listconfiguração do modelo, vamos adicionar algumas tarefas a ele e salvá-las no 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'); 
    }
});

O atributo de tarefas na instância de nosso Listmodel ( simpleList) funciona como um array JavaScript regular e podemos adicionar novas tarefas a ele usando push. O importante a notar é que as tarefas são adicionadas como objetos JavaScript regulares. É uma distinção sutil que pode não ser imediatamente intuitiva.

Você pode verificar no shell do Mongo se a nova lista e as tarefas foram salvas no 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" }

Agora podemos usar o ObjectIdpara obter o Sample Liste iterar por meio de suas tarefas.

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 você executar o último pedaço de código, receberá um erro dizendo que o documento incorporado não tem um método isHighPriority. Na versão atual do Mongoose, você não pode acessar métodos em esquemas integrados diretamente. Há um tíquete aberto para consertá-lo e, depois de fazer a pergunta ao Mongoose Google Group, manimal45 postou uma solução útil para usar agora.

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 você executar esse código, verá a seguinte saída na linha de comando.

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

Com essa solução alternativa em mente, vamos transformar o TaskSchemaem um 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');

A TaskSchemadefinição é a mesma de antes, então eu a deixei de fora. Depois de transformado em um modelo, ainda podemos acessar seu objeto Schema subjacente usando a notação de ponto.

Vamos criar uma nova lista e incorporar duas instâncias do modelo Task nela.

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

Como estamos incorporando as instâncias do modelo Task na Lista, estamos chamando toObject-as para converter seus dados em objetos JavaScript simples que o List.tasks DocumentArrayestá esperando. Quando você salva instâncias de modelo desta forma, seus documentos incorporados irão conter ObjectIds.

O exemplo de código completo está disponível como uma essência . Esperançosamente, essas soluções alternativas ajudam a suavizar as coisas conforme o Mongoose continua a se desenvolver. Ainda sou muito novo no Mongoose e no MongoDB, então sinta-se à vontade para compartilhar melhores soluções e dicas nos comentários. Modelagem de dados feliz!

Adam Comerford
fonte
3
Em geral, é recomendado não enviar links vazios como uma resposta às perguntas postadas no SO, pois o link pode parar de funcionar (como neste caso). Pelo menos copie / cole e cite as seções relevantes dos artigos para os quais você tem links.
Behrang Saeedzadeh
1
pronto - ainda estava no cache do Google, de forma relativamente simples
Adam Comerford
1
Para fins de
Dakota
5
Não estou tentando chover no desfile de ninguém, mas essa resposta parece mais um tutorial: responder como, mas não por quê. Apesar de ter menos votos
positivos
2
Eu vi essa resposta (e votei), esta foi respondida e aceita mais de 2 anos antes disso. Estou feliz que haja uma resposta melhor a ser encontrada, não está chovendo no desfile de ninguém e há um link para a resposta que você mencionou nos comentários da pergunta desde fevereiro de 2015, então não senti a necessidade de vincular eu mesmo
Adam Comerford
54

Schema é um objeto que define a estrutura de qualquer documento que será armazenado em sua coleção MongoDB; ele permite que você defina tipos e validadores para todos os seus itens de dados.

Modelo é um objeto que fornece acesso fácil a uma coleção nomeada, permitindo que você consulte a coleção e use o Schema para validar quaisquer documentos salvos nessa coleção. Ele é criado combinando um Schema, uma Connection e um nome de coleção.

Originalmente formulado por Valeri Karpov, MongoDB Blog

Zeeshan Hassan Memon
fonte
5

Não acho que a resposta aceita realmente responda à pergunta que foi feita. A resposta não explica por que o Mongoose decidiu exigir que um desenvolvedor forneça uma variável Schema e uma Model. Um exemplo de uma estrutura em que eles eliminaram a necessidade do desenvolvedorpara definir o esquema de dados é django - um desenvolvedor escreve seus modelos no arquivo models.py, e deixa para o framework gerenciar o esquema. A primeira razão que vem à mente para fazer isso, dada a minha experiência com django, é a facilidade de uso. Talvez o mais importante seja o princípio DRY (não se repita) - você não precisa se lembrar de atualizar o esquema quando alterar o modelo - o django fará isso por você! O Rails também gerencia o esquema dos dados para você - um desenvolvedor não edita o esquema diretamente, mas o altera definindo migrações que manipulam o esquema.

Um motivo pelo qual pude entender que o Mongoose separaria o esquema e o modelo são as instâncias em que você deseja construir um modelo a partir de dois esquemas. Tal cenário pode apresentar mais complexidade do que vale a pena gerenciar - se você tem dois esquemas que são gerenciados por um modelo, por que eles não são um esquema?

Talvez a pergunta original seja mais uma relíquia do sistema de banco de dados relacional tradicional. No mundo NoSQL / Mongo, talvez o esquema seja um pouco mais flexível do que o MySQL / PostgreSQL e, portanto, alterar o esquema é uma prática mais comum.

johnklawlor
fonte
Como se esquema versus modelo não fosse suficiente Repetindo-se, você se depara com mais duplicação ao tentar manter uma interface TypeScript correspondente e ainda mais ao criar um esquema GraphQL.
Dan Dascalescu
0

Para entender por quê? você tem que entender o que realmente é o Mongoose?

Bem, o mongoose é uma biblioteca de modelagem de dados de objeto para MongoDB e Node JS, fornecendo um nível mais alto de abstração. Portanto, é um pouco como o relacionamento entre Express e Node, então Express é uma camada de abstração sobre Node regular, enquanto Mongoose é uma camada de abstração sobre o driver MongoDB regular.

Uma biblioteca de modelagem de dados de objeto é apenas uma maneira de escrevermos código Javascript que irá interagir com um banco de dados. Portanto, poderíamos apenas usar um driver MongoDB regular para acessar nosso banco de dados, funcionaria perfeitamente.

Mas, em vez disso, usamos o Mongoose porque ele nos dá muito mais funcionalidade pronta para uso, permitindo o desenvolvimento mais rápido e simples de nossos aplicativos.

Portanto, alguns dos recursos que o Mongoose nos fornece esquemas para modelar nossos dados e relacionamento, validação de dados fácil, uma API de consulta simples, middleware e muito mais.

No Mongoose, um esquema é onde modelamos nossos dados, onde descrevemos a estrutura dos dados, valores padrão e validação, então pegamos esse esquema e criamos um modelo a partir dele, um modelo é basicamente um invólucro em torno do esquema, o que nos permite realmente interagir com o banco de dados para criar, excluir, atualizar e ler documentos.

insira a descrição da imagem aqui

Vamos criar um modelo a partir de um 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 acordo com a convenção, a primeira letra de um nome de modelo deve ser maiúscula.

Vamos criar uma instância do nosso modelo que criamos usando mangusto e esquema. também, interaja com nosso banco de dados.

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

Portanto, ter tanto o schama quanto o modle mangusto torna nossa vida mais fácil.

senhor
fonte