dmr07
dmr07

Reputation: 1478

Mongoose: 3 way document joining

I'm learning mongoose and need some help. I have 3 collections, and on a single API call, I want to create 3 documents that reference each other; "joins" below:

Question: I know that I can do a model.create() and pass in the new document in each callback, and then update to the respective docs, but I was wondering if there's a cleaner way of doing it?

Sorry if I'm not clear on the question. Please ask me if something doesn't make sense.

Code

var chirpSchema = new mongoose.Schema({
    date_created: { type: Date, default: Date.now }
  , content: { post : String }
  , _video: { type: $oid, ref: "video" }
  , _author: { type: $oid, ref: "user" }
});
var chirp = mongoose.model('chirp', chirpSchema);


var userSchema = new mongoose.Schema({
    date_joined: { type : Date, default: Date.now }
  , cookie_id: String,
  chirp_library: [{type: $oid, ref: "chirp"}]
})
var user = mongoose.model('user', userSchema);


var videoSchema = new mongoose.Schema({
    date_tagged: { type : Date, default: Date.now }
  , thumbnail_url : String
  , _chirps: [{type: $oid, ref: "chirp" }]
});
var video = mongoose.model('video', videoSchema);

Upvotes: 1

Views: 172

Answers (1)

chrisbajorin
chrisbajorin

Reputation: 6153

Mongo and other NoSQL databases aren't just an interchangeable alternative to a SQL database. It forces you to rethink your design in a different way. The concept isn't to define a relationship. The idea is to make information available in less queries. Arrays are generally a thing to avoid in Mongo, especially if they have the potential to grow infinitely. Based on your naming, that seems like a strong possibility. If you keep the rest of your schema and just delete those two arrays off of your user and video schemas:

chirp.find({_author: yourUserId}).populate("_author") gives you the same information as user.findOne({_id: yourUserId}) in your current design.

similarly,

chirp.find({_video: yourVideoId}).populate("_video") and video.findOne({_id: yourVideoId})

The only issue with this is that the .populate() is running on every single chirp you are pulling. A way around this is to denormalize some (or all) of your author and video documents on the chirp document. How I would likely design this is:

var chirpSchema = new mongoose.Schema({
    date_created: { type: Date, default: Date.now },
    content: { 
        post : String 
    },
    _author: { 
        _id: { type: $oid, ref: "video" },
        date_joined: { type: Date, default: Date.now }
    },
    _video: { 
        _id: { type: $oid, ref: "user" },
        date_tagged: { type: Date, default: Date.now },
        thumbnail_url: String
    }
});

var userSchema = new mongoose.Schema({
    date_joined: { type : Date, default: Date.now }
  , cookie_id: String
})

var videoSchema = new mongoose.Schema({
    date_tagged: { type : Date, default: Date.now }
  , thumbnail_url : String
});

It's perfectly OK to have data repeated, as long as it makes your queries more efficient. That being said, you need to strike a balance between reading and writing. If your user information or video information changes regularly, you'll have to update that data on each chirp document. If there is a specific field on your video/author that is changing regularly, you can just leave that field off the chirp document if it's not necessary in queries.

Upvotes: 1

Related Questions