Não é possível sobrescrever o modelo uma vez compilado Mongoose

109

Não tenho certeza do que estou fazendo de errado, aqui está meu check.js

var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));

var a1= db.once('open',function(){
var user = mongoose.model('users',{ 
       name:String,
       email:String,
       password:String,
       phone:Number,
      _enabled:Boolean
     });

user.find({},{},function (err, users) {
    mongoose.connection.close();
    console.log("Username supplied"+username);
    //doSomethingHere })
    });

e aqui está meu insert.js

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/event-db')

var user = mongoose.model('users',{
     name:String,
     email:String,
     password: String,
     phone:Number,
     _enabled:Boolean
   });

var new_user = new user({
     name:req.body.name,
     email: req.body.email,
     password: req.body.password,
     phone: req.body.phone,
     _enabled:false
   });

new_user.save(function(err){
    if(err) console.log(err); 
   });

Sempre que tento executar check.js, recebo este erro

Não é possível sobrescrever o modelo de 'usuários' depois de compilado .

Eu entendo que esse erro ocorre devido à incompatibilidade do Schema, mas não consigo ver onde isso está acontecendo. Sou muito novo no mangusto e no nodeJS.

Aqui está o que estou obtendo da interface do cliente do meu MongoDB:

MongoDB shell version: 2.4.6 connecting to: test 
> use event-db 
  switched to db event-db 
> db.users.find() 
  { "_id" : ObjectId("52457d8718f83293205aaa95"), 
    "name" : "MyName", 
    "email" : "[email protected]", 
    "password" : "myPassword", 
    "phone" : 900001123, 
    "_enable" : true 
  } 
>
Anathema.Imbued
fonte
Aqui está o que estou obtendo da interface do cliente do meu MongoDB: MongoDB shell version: 2.4.6 conectando-se a: test> use event-db alterado para db event-db> db.users.find () {"_id": ObjectId ("52457d8718f83293205aaa95"), "name": "MyName", "email": "[email protected]", "password": "myPassword", "phone": 900001123, "_enable": true}>
Anathema .Imbuído em

Respostas:

110

O erro está ocorrendo porque você já tem um esquema definido e, em seguida, está definindo o esquema novamente. Geralmente, o que você deve fazer é instanciar o esquema uma vez e, em seguida, fazer com que um objeto global o chame quando precisar.

Por exemplo:

user_model.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
   name:String,
   email:String,
   password:String,
   phone:Number,
   _enabled:Boolean
});
module.exports = mongoose.model('users', userSchema);          

check.js

var mongoose = require('mongoose');
var User = require('./user_model.js');

var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
  User.find({},{},function (err, users) {
    mongoose.connection.close();
    console.log("Username supplied"+username);
    //doSomethingHere 
  })
});

insert.js

var mongoose = require('mongoose');
var User = require('./user_model.js');

mongoose.connect('mongodb://localhost/event-db');
var new_user = new User({
    name:req.body.name
  , email: req.body.email
  , password: req.body.password
  , phone: req.body.phone
  , _enabled:false 
});
new_user.save(function(err){
  if(err) console.log(err); 
});
thtsigma
fonte
69
Evite exportar / exigir modelos - se algum tiver refoutros modelos, isso pode levar a um pesadelo de dependência. Use em var User = mongoose.model('user')vez de require.
wprl
1
Na verdade, pode ser útil alterar um esquema após a definição para testar o código de migração do esquema.
Igor Soarez
1
@wprl você pode explicar melhor? por que exigir isso criaria problema?
varuog de
Essa resposta é enganosa. O fato é que se houvesse apenas uma instância do servidor mongoDB e mais Bancos de dados, se você definir em outro aplicativo o banco de dados já tomado, você obterá esse erro. Simplesmente assim
Carmine Tambascia
174

Portanto, outra razão pela qual você pode obter este erro é se você usar o mesmo modelo em arquivos diferentes, mas o requirecaminho tiver um caso diferente. Por exemplo, na minha situação eu tinha:

require('./models/User')em um arquivo e depois em outro onde precisei acessar o modelo de usuário que tinha require('./models/user').

Acho que procurar por módulos & mongoose é tratá-lo como um arquivo diferente. Depois de ter certeza de que o caso combinava em ambos, não era mais um problema.

Jonnie
fonte
7
Esse é um problema muito complicado, na verdade - acho que é específico do sistema operacional (deve acontecer apenas no Mac e no Windows, pois o FS ignora o caso). Eu tive esse problema, mas felizmente vi sua resposta :) Muito obrigado Jonnie!
Miroslav Nedyalkov
6
esse problema acontece no meu sistema OS X.
lutaoact
Eu nunca poderia ter pensado nisso, pelo menos não intuitivamente! obrigado
Naveen Attri
Esse era totalmente o meu problema. Eu nunca pensei que nomear maiúsculas causasse qualquer problema.
Sandip Subedi
Isso foi o mesmo para mim. Todos saudam o OS X e seu sistema de arquivos (não diferencia maiúsculas de minúsculas por padrão)
mithril_knight
50

Eu tive esse problema durante o teste de unidade.

A primeira vez que você chama a função de criação de modelo, o mongoose armazena o modelo sob a chave que você fornece (por exemplo, 'usuários'). Se você chamar a função de criação de modelo com a mesma tecla mais de uma vez, o mongoose não permitirá que você substitua o modelo existente.

Você pode verificar se o modelo já existe no mangusto com:

let users = mongoose.model('users')

Isso gerará um erro se o modelo não existir, então você pode envolvê-lo em um try / catch para obter o modelo ou criá-lo:

let users
try {
  users = mongoose.model('users')
} catch (error) {
  users = mongoose.model('users', <UsersSchema...>)
}
BJ Anderson
fonte
1
+1 Eu estava tendo o mesmo problema em que precisava definir algumas configurações para um plug-in antes de definir meu esquema. Isso não funcionou bem com o mocha de jeito nenhum e no final eu desisti e fui com essa abordagem de try catch
Victor Parmar
Estou usando o mesmo, mas ao contrário, isso é ruim:try exports.getModel = ()-> mongoose.model('User', userSchema) catch err exports.getModel = ()-> mongoose.model('User')
Andi Giga
Obrigado, bom senhor, desperdicei 5+ horas com este problema. Eu estava trabalhando com servidor sem servidor, ao contrário do servidor de nó com o qual estou acostumado.
mxdi9i7
43

Eu tive esse problema ao 'assistir' os testes. Quando os testes foram editados, o relógio os executou novamente, mas eles falharam exatamente por esse motivo.

Corrigi-o verificando se o modelo existe, então use-o, caso contrário, crie-o.

import mongoose from 'mongoose';
import user from './schemas/user';

export const User = mongoose.models.User || mongoose.model('User', user);
ZephDavies
fonte
Isso funcionou para mim. Eu tinha mudado o module.export = Userpara export defaults User. Eu também tive refsque usar outros modelos. Não sei por que mudar de module.exportspara export defaulttrouxe esse problema. No entanto, essa resposta parece ter corrigido.
runios
3
to bad mongoose.modelsnão existe, pelo menos em versões recentes
Pedro Luz
1
Eu tive o mesmo problema, mas resolvi limpar todos os modelos antes de todos os testes:for (let model in mongoose.models) delete mongoose.models[model]
E. Sundin
Meu script de teste é assim: "test": "NODE_ENV=test mocha --file mocha.config.js --watch"e nesse arquivo js de configuração eu tenho um before()e after()para lidar com a configuração e a desmontagem. @E.Sundin forneceu a solução perfeita aqui, e funciona perfeitamente. Obrigado!
Brandon Aaskov de
21

Tenho enfrentado esse problema e não era por causa das definições do esquema, mas sim do modo off-line sem servidor - acabei de resolver isso:

serverless offline --skipCacheInvalidation

Que é mencionado aqui https://github.com/dherault/serverless-offline/issues/258

Espero que isso ajude alguém que está construindo seu projeto sem servidor e executando o modo offline.

munyah
fonte
2
Muito útil. Obrigado.
Thanh Truong de
2
Achei irritante pular a invalidação do cache, recarregamentos constantes, em vez disso funcionamodule.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema);
perguntado_io
você fez meu dia
fstasi
Obrigado um milhão!
AndyFaizan,
Isso foi muito útil. Obrigado!
ifiok
20

Se você estiver usando Serverless offline e não quiser usar --skipCacheInvalidation, pode muito bem usar:

module.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema);
Julian
fonte
Você também deve usar isso se estiver importando um modelo dentro de outro, mesmo com--skipCacheInvalidation
Powderham
1
Esta é a resposta exata que eu estava procurando, para usar no Next.js. Eu gostaria que isso estivesse no topo da página!
Brendan Nee
18

Se você fez isso aqui é possível que você tenha o mesmo problema que eu. Meu problema era que eu estava definindo outro modelo com o mesmo nome . Chamei minha galeria e meu modelo de arquivo de "Arquivo". Droga, você copia e cola!

James Harrington
fonte
11

Isso aconteceu comigo quando eu escrevo assim:

import User from '../myuser/User.js';

No entanto, o verdadeiro caminho é '../myUser/User.js'

ip192
fonte
A combinação de casos de caminhos de esquema durante a importação parece causar esse problema - verifique se todos os arquivos que importam o esquema usam o mesmo caso.
Andrew Cupper
isso nos salvou! temos a sensação de que isso pode ser devido ao uso do Windows
Lyka
11

Eu resolvi isso adicionando

mongoose.models = {}

antes da linha:

mongoose.model(<MODEL_NAME>, <MODEL_SCHEMA>)

Espero que isso resolva seu problema

Toufiq
fonte
Isso foi o que eu fiz e resolvi. mongoose.connection.models = {};
Fortune
6

Para resolver isso verifique se o modelo existe antes de fazer a criação:

if (!mongoose.models[entityDBName]) {
  return mongoose.model(entityDBName, entitySchema);
}
else {
  return mongoose.models[entityDBName];
}
Alpha BA
fonte
4

Sei que existe uma solução aceita, mas sinto que a solução atual resulta em muitos clichês apenas para que você possa testar os Modelos. Minha solução é essencialmente pegar seu modelo e colocá-lo dentro de uma função, resultando no retorno do novo modelo se o modelo não tiver sido registrado, mas retornando o modelo existente se ele foi.

function getDemo () {
  // Create your Schema
  const DemoSchema = new mongoose.Schema({
    name: String,
    email: String
  }, {
    collection: 'demo'
  })
  // Check to see if the model has been registered with mongoose
  // if it exists return that model
  if (mongoose.models && mongoose.models.Demo) return mongoose.models.Demo
  // if no current model exists register and return new model
  return mongoose.model('Demo', DemoSchema)
}

export const Demo = getDemo()

Abrir e fechar conexões em todos os lugares é frustrante e não compacta bem.

Dessa forma, se eu solicitasse o modelo em dois locais diferentes ou mais especificamente em meus testes, não obteria erros e todas as informações corretas estão sendo retornadas.

Moosecouture
fonte
2

Este problema pode ocorrer se você definir 2 esquemas diferentes com o mesmo nome de coleção

Rohit Reddy Abbadi
fonte
1
If you want to overwrite the existing class for different collection using typescript
then you have to inherit the existing class from different class.

export class User extends Typegoose{
  @prop
  username?:string
  password?:string
}


export class newUser extends User{
    constructor() {
        super();
    }
}

export const UserModel = new User ().getModelForClass(User , { schemaOptions: { collection: "collection1" } });

export const newUserModel = new newUser ().getModelForClass(newUser , { schemaOptions: { collection: "collection2" } });
Rohit Jangid
fonte
1

Eu tive o mesmo problema, o motivo foi definir esquema um modelo em uma função JS, eles devem ser definidos globalmente em um módulo de nó, não em uma função.

apicultor
fonte
1

Existe outra maneira de lançar esse erro.

Lembre-se de que o caminho para o modelo diferencia maiúsculas de minúsculas.

Neste exemplo semelhante envolvendo o modelo "Categoria", o erro foi lançado sob estas condições:

1) O comando require foi citado em dois arquivos: ..category.js e ..index.js 2) No primeiro, o caso estava correto, no segundo arquivo não era o seguinte:

category.js

insira a descrição da imagem aqui

index.js

insira a descrição da imagem aqui

Tim
fonte
0

A definição do esquema deve ser exclusiva para uma coleção, não deve ser mais do que um esquema para uma coleção.

KARTHIKEYAN.A
fonte
0

é porque seu esquema já existe, valide antes de criar um novo esquema.

var mongoose = require('mongoose');
module.exports = function () {
var db = require("../libs/db-connection")();
//schema de mongoose
var Schema = require("mongoose").Schema;

var Task = Schema({
    field1: String,
    field2: String,
    field3: Number,
    field4: Boolean,
    field5: Date
})

if(mongoose.models && mongoose.models.tasks) return mongoose.models.tasks;

return mongoose.model('tasks', Task);
Diego Santa Cruz Mendezú
fonte
0

Você pode resolver isso facilmente fazendo

delete mongoose.connection.models['users'];
const usersSchema = mongoose.Schema({...});
export default mongoose.model('users', usersSchema);
Shyam
fonte
0

Eu tenho uma situação em que tenho que criar o modelo dinamicamente com cada solicitação e por causa disso recebi este erro, no entanto, o que usei para corrigi-lo foi usando o método deleteModel como o seguinte:

var contentType = 'Product'

var contentSchema = new mongoose.Schema(schema, virtuals);

var model = mongoose.model(contentType, contentSchema);

mongoose.deleteModel(contentType);

Espero que isso possa ajudar alguém.

Engr.MTH
fonte
0
The reason of this issue is: 

you given the model name "users" in the line 
<<<var user = mongoose.model('users' {>>> in check.js file

and again the same model name you are giving in the insert file
<<< var user = mongoose.model('users',{ >>> in insert.js

This "users" name shouldn't be same when you declare a model that should be different 
in a same project.
Rohit Jangid
fonte
0

Para todas as pessoas que terminam aqui por causa de uma base de código com uma mistura de Typegoose e Mongoose :

Crie uma conexão db para cada um:

Mongoose:

module.exports = db_mongoose.model("Car", CarSchema);

Typegoose:

db_typegoose.model("Car", CarModel.schema, "cars");
Além do mar
fonte
0

Acabei de ter um erro ao copiar e colar. Em uma linha eu tinha o mesmo nome que em outro modelo (modelo de anúncio):

const Admin = mongoose.model('Ad', adminSchema);

O correto é:

const Admin = mongoose.model('Admin', adminSchema);

A propósito, se alguém tiver "salvamento automático" e usar o índice para consultas como:

**adSchema**.index({title:"text", description:"text", phone:"text", reference:"text"})

Ele tem que excluir o índice e reescrever para o modelo correto:

**adminSchema**.index({title:"text", description:"text", phone:"text", reference:"text"})
Titoih
fonte
0

Eu resolvi esse problema fazendo isso

// Created Schema - Users
// models/Users.js
const mongoose = require("mongoose");

const Schema = mongoose.Schema;

export const userSchema = new Schema({
  // ...
});

Em seguida, em outros arquivos

// Another file
// index.js
import { userSchema } from "../models/Users";
const conn = mongoose.createConnection(process.env.CONNECTION_STRING, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});
conn.models = {};
const Users = conn.model("Users", userSchema);
const results = await Users.find({});

Melhor Solução

let User;
try {
  User = mongoose.model("User");
} catch {
  User = mongoose.model("User", userSchema);
}

Eu espero que isso ajude...


fonte
Não tenho ideia de por que é tão difícil fornecer explicações. Imagine o tempo que você perde enquanto todos lêem seu código.
robertfoenix
-1

Já que esse problema aconteceu porque chamando o modelo outra hora. Contorne esse problema envolvendo seu código de modelo no bloco try catch. o código datilografado é assim -

         Import {Schema, model} from 'mongoose';
         export function user(){
              try{
                   return model('user', new Schema ({
                            FirstName: String,
                            Last name: String
                     }));
              }
             catch{
                   return model('user');
              }
         }

Da mesma forma, você também pode escrever código em js.

AKP
fonte
-2

Você está usando mongoose.model com o mesmo nome de variável "usuário" em check.js e insert.js.

David Khan
fonte
-4

Se você estiver trabalhando com expressjs, pode ser necessário mover sua definição de modelo para fora de app.get () para que seja chamado apenas uma vez quando o script for instanciado.

Elesin Olalekan Fuad
fonte
isso não faz sentido, os modelos de mangusto são definidos apenas uma vez, a menos que haja um problema com a nomenclatura (por exemplo, caso), uma vez que é chamado pela primeira vez, é inicializado, as necessidades futuras devem apenas obter a instância e não reinstanciar
jonnie
Esta não é uma solução.
Prathamesh More