Search code examples
mongodbmongoosemongoose-schemamongoose-populate

Mongoose: reference between models with same schema keys


As a front-end developer, I would like to have some isomorphic object for two mongoose models.

Let's say I have a user profile:

const profileSchema = new Schema({
  firstName: { type: String },
  lastName: { type: String },

  // example of difference between model schemas
  __user: { type: ObjectId, ref: 'Users' },
}

And I would like to create a list of Contact, where each contact will have some of the same keys:

const contactSchema = new Schema({
  firstName: { type: String },
  lastName: { type: String },

  __profile: {
    type: ObjectId,
    ref: 'Profiles',
    unique: true,
  },

  comment: { type: String }, 
}

NOTE: Contact could be the both:

  • as a reference to the Profile
  • and as independent record in DB / document.

==============================

My question: which is the best way to, organize models on such a way, so

  • contact could be a reference to the profile
  • when similar Profile key, like firstName will be updated, contact firstName will be updated too

AVOID of next ref

await Contact.findById(SOME_ID).populate('__profile');

// result
{
  firstName: '',
  lastName: '',

  __profile: {
    firstName: 'Chuck',
    lastName: 'Norris',
  }
}

Desired result - keep contact "isomorphic" like:

{
  firstName: 'Chuck', // the key value from profile
  lastName: 'Norris', // the key value from profile

  __profile: SOME_PROFILE_ID,
}

Is this possible?

P.S: in my app, I'm using refs and started to use discriminators approaches.


Solution

  • In my case, complete usage of Mongoose discriminators did not give me an advantage, because discriminators give you an opportunity to:

    They enable you to have multiple models with overlapping schemas on top of the same underlying MongoDB collection.

    As a result, by using discriminators approach, I will reive one collection of:

    • profiles

    And there will be a mix of users and contact profiles.

    ==============================

    So I decided to use two approaches:

    RESULT:

    // keys which are same for both user Profile and Contact
    const Schema = require('mongoose').Schema;
    const util = require('util');
    
    function BaseProfileSchema(...args) {
      Schema.apply(this, args);
    
      this.add({
        firstName: { type: String },
        lastName: { type: String },
      });
    }
    util.inherits(BaseProfileSchema, Schema);
    
    // user Profile Model
    const profileSchema = new BaseProfileSchema({
      __user: {
        type: String,
        ref: 'users',
        required: true,
        unique: true,
      },
    });
    
    const Profile = mongoose.model('profiles', profileSchema);
    
    // Contact with profile as subdocument
    const contactProfileSchema = new BaseProfileSchema();
    
    const contactSchema = new Schema({
      // Associations
      __refProfile: {
        type: Schema.Types.ObjectId,
        ref: 'profiles',
        index: {
          unique: true,
          sparse: true,
        },
      },
      profile: contactProfileSchema,
    });
    
    const Contact = mongoose.model('contacts', contactSchema);
    

    As a result, I'm having DB with next collections:

    • users
    • profiles
    • contacts

    Both profiles and contacts.profile are IDENTICAL because I'm extending the base shared schema.

    Moreover:

    • inside Contact I'm having different keys for real referenced profile (__refProfile which can NOT be edit by others) and contact.profile
    • profile inside connection can be edited ONLY when the contact was edited by itself

    P.S: happy codding 👨‍💻🍻