mongodb-js / mongoose-autopopulate

Always populate() certain fields in your mongoose schemas
Apache License 2.0
221 stars 36 forks source link

autopopulate across databases #93

Closed hastudillo closed 2 years ago

hastudillo commented 2 years ago

Good morning,

It is possible to populate documents across databases (and therefore managing different connections) since 3.9.0: https://github.com/Automattic/mongoose/issues/2137

When using mongoose-autopopulate I'm getting this error: MissingSchemaError: Schema hasn't been registered for model... Use mongoose.model(name, schema)

Is there any way of using a model obtained via connection.model('Name', Schema) from a chosen connection? If it is the case, how to pass it to the "father" Schema which has been already declared? I can't see how to define a Schema with autopopulate having a DYNAMIC reference to another Schema (for it could correspond to a database or another)

As a quick reference, it is possible and quite easy with mongoose populate: https://stackoverflow.com/a/47038544

NB. I'm using NestJS.

vkarpov15 commented 2 years ago

Without code samples, hard to tell why this error is happening. You can use cross-db populate with mongoose-autopopulate as shown below:

    const userSchema = new Schema({
      name: String
    });

    const conn2 = await mongoose.createConnection('mongodb://localhost:27017/test');
    const User = conn2.model('User', userSchema);

    const responseSchema = new Schema({
      user: {
        type: Schema.Types.ObjectId,
        ref: User,
        autopopulate: true
      }
    });
    responseSchema.plugin(autopopulate);
    const Response = db.model('Response', responseSchema);

    const user = await User.create({ name: 'test' });
    const response = await Response.create({ user: user._id });

    const res = await Response.findById(response);
    console.log(res.user.name); // 'test'

Another option for Mongoose >= 5.6.0 is to set the connection option, which tells Mongoose which connection to use when looking up the model:

    const responseSchema = new Schema({
      user: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        autopopulate: { connection: conn2 } // Set the connection used to look up 'User'
      }
    });
hastudillo commented 2 years ago

@vkarpov15 thank you very much for taking into account this question. Let's say we have 2 (or more) connections and the same User collection (at least) in one of them. I have the following code (based on yours):

  const userSchema = mongoose.Schema({
    name: String
  });
  const responseSchema = mongoose.Schema({
    user: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User',
    }
  });

  const conn1 = mongoose.createConnection('mongodb://localhost:27017/test');
  const conn2 = mongoose.createConnection('mongodb://localhost:27017/test2');

  const User = conn1.model('User', userSchema);
  const Response = conn2.model('Response', responseSchema);

  await Response.findOne().populate({ path: 'user' });

I will have the MissingSchemaError. With the options of mongoose populate() it's quite easy to solve:

  await Response.findOne().populate({ path: 'user', model: User });

Let's say now that I cannot go back to the definitions of the schemas. (autopopulate: { connection: conn2 } was a good solution, I have to say, but I guess quite tricky to implement in NestJs). So I'm able to know which model I have to use just when doing the query, among other reasons because the connections come and go, they're not fixed at the beginning (multi-tenancy).

Is there a way to use something similar to the populate { model: } option with autopopulate?

(Maybe specifying a function as option? Maybe with an extra option with lean()?)

Thank you very much in advance.

vkarpov15 commented 2 years ago

@hastudillo you can set autopopulate to a function: https://plugins.mongoosejs.io/plugins/autopopulate#it-can-specify-a-function-that-returns-options