Mongoose References

Tables

In your Mongoose Schemas, you will have objects that have specified properties. These properties can reference other objects in other Schemas. This tutorial will explore some of the methods to doing this.

One user has multiple todos

If you are a user with todos, your todo schema will look like this:

const todoSchema = new Schema({  
    title: {
        type: String,
        required: true
    },
    completed: {
        type: Boolean,
        default: false
    },
    // Add a reference to the user to whom this todo belongs
    user: {
        type: Schema.Types.ObjectId,
        ref: "User",
        required: true
    }
});

Look again at the user property.

The "type" property of that object says that each todo will be assigned to a user ID. The whole user object would not be stored there, just an Id that refers to the rest of that User object.

The "ref" property of that object points to the collections. That value needs to match up with what you named when you registered the refered to schema's schema.

module.exports = mongoose.model("User", userSchema);

The user schema would not have a todo property because when we query for all of a users todos, we could just query for todos with that user ID.

Todo.find({user: req.user._id},  (err, todos) => {
    if (err) res.status(500).send(err);
    res.send(todos);
});

There is a saying in Mongo communities that says, "Store what you query for." Since we wouldn't query a user through the todos, we would store that on the user.

Items that every user can own

Let's say, for example, that you have have a website where people can make their ideal outfit. In the database are tons of shoes, pants and shirts. A user can create a new outfit with Keen Sandals, Jnco Jeans, and a Kanye-Designed Hip Hop t-shirt.

All of those pieces of clothing would be an object created with this Schema:

const clothingSchema = new Schema({  
    title: {
        type: String,
        required: true
    },
    type: String
});

Notice how we don't have a reference to a user or outfit in this schema. That is because many users could want the same item in their outfits.

So you may want to have an array of clothing items in your outfit schema.

const outfitSchema = new Schema({
    title: {
        type: String,
        required: true
    },
    clothingItems:[
        {
             type: Schema.Types.ObjectId,
             ref: "ClothingItem",
             required: true
        }
    ]
})

Here the outfits would have an array of IDs that refer to the clothing items.

Embedding objects versus referring to other schemas.

Sometimes we don't want to reference another Schema. Look at the following example:

const residentSchema = new Schema({  
    name: {
        type: String,
        required: true
    },
    address: {
        street: String,
        city: String, 
        state: String, 
        zip: Number        
    }
});

Even though a person has an address, and each address has it's own properties, often times we may decide to just nest an embedded object in our schema. In this case it makes sense, because we know more or less that only a couple people will have the same address. Another reason that this makes sense is that addresses themselves don't often change. Let's use this same schema and throw on a "apartmentComplex" property.

const residentSchema = new Schema({  
    name: {
        type: String,
        required: true
    },
    apartmentComplex: {
        owner: String,
        amenities: [String]
    }
});

This would not make sense. What if we added more amenities? We would have to change every resident. What if the owner of the complex changes? Same thing.

Where to put the reference

In our apartment complex schema, why don't we have an array or residents like this?

const apartmentComplexSchema = new Schema({  
    name: {
        type: String,
        required: true
    },
    residents: [
        {
             type: Schema.Types.ObjectId,
             ref: "Resident",
             required: true
         }
     ]
});

The reason is because we will get an unbounded number of residents. If we know we'll get just a few residents, this Schema is ok. For example, if you had a list of houses someone owned. Even residents in an apartment complex might be ok because it will only be a couple hundred, but there are some things you for sure you don't want in an array.