Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose document references with a one-to-many relationship

I'm working on designing a database structure for a new project, and I'm pretty new to MongoDB, and obviously Mongoose.

I've read Mongooses population documentation, where it has a one-to-many relationship, with one Person document to many Story documents, but the part that confuses me is where instead of the Story documents referencing what Person document it belongs to, the Person schema has it setup so it has an array of what Story documents it 'owns'.

I'm setting up something very similar to this. But I keep thinking it would be easier when creating new Story documents to have the Person document ID. But maybe thats just because I'm more familiar with MySQL relationships using joins.

If this is the best way to do it (and I'm sure it is, since its in the docs), when new Story documents are created, whats the best way to update the array of stories in the associated People document it belongs to? I looked but couldn't find any examples of updating existing documents to add references to other documents (or deleting them for that matter)

I'm sure this is an easy solution that I just overlooked or something, but any help would be great. Thanks!

like image 292
Justin Avatar asked Jan 25 '16 05:01

Justin


People also ask

How can you implement 1 to many relationships in MongoDB?

In MongoDB, one-to-one, one-to-many, and many-to-many relations can be implemented in two ways: Using embedded documents. Using the reference of documents of another collection.

Can Mongoose connect to multiple databases?

Mongoose doesn't allow to use multiple databases in single mongoose instance as the models are build on one connection.

How do mongooses make relationships?

To model relationships between connected data, you can reference a document or embed it in another document as a sub document. Referencing a document does not create a “real” relationship between these two documents as does with a relational database. Referencing documents is also known as normalization.


3 Answers

Refer to population, here extract an example from Mongoose.

var mongoose = require('mongoose') , Schema = mongoose.Schema  var personSchema = Schema({   _id     : Schema.Types.ObjectId,   name    : String,   age     : Number,   stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }] });  var storySchema = Schema({   _creator : { type: Schema.Types.ObjectId, ref: 'Person' },   title    : String,   fans     : [{ type: Schema.Types.ObjectId, ref: 'Person' }] });  var Story  = mongoose.model('Story', storySchema); var Person = mongoose.model('Person', personSchema); 

So the example about, Story model stores related Person._id in Story._creator. When you find a document of Story, you can use populate() method to define which attribute in Person model you want to retrieve at the same time, such as:

Story.findOne({_id: 'xxxxxxx'}).populate('person', 'name age').exec(function(err, story) {   console.log('Story title: ', story.title);   console.log('Story creator', story.person.name); }); 

I believe this is what you looking for. Or else, you can use nested collections instead.

like image 124
Keiran Tai Avatar answered Sep 30 '22 03:09

Keiran Tai


The previous answers to this question were helpful, but it may be useful to see more detailed code. The below code is from my Express.js backend for my application. My application allows users to write reviews. When querying the user, I return all of the reviews that the user has made.

user_model.js

import mongoose, { Schema } from 'mongoose';   const UserSchema = new Schema({   firstname: String,   lastname: String,   username: { type: String, unique: true },   reviews: [{ type: Schema.Types.ObjectId, ref: 'Review' }], }, {   toJSON: {     virtuals: true,   }, });  const UserModel = mongoose.model('User', UserSchema); export default UserModel; 

review_model.js

import mongoose, { Schema } from 'mongoose';  const ReviewSchema = new Schema({   body: String,   username: String,   rating: Number, }, {   toJSON: {     virtuals: true,   }, });  const ReviewModel = mongoose.model('Review', ReviewSchema); export default ReviewModel; 

review_controller.js

// . . . export const createReview = (req, res) => {     const review = new Review();     review.username = req.body.username;     review.rating = req.body.rating;     review.body = req.body.body;     review.save()       .then((result) => {         User.findOne({ username: review.username }, (err, user) => {             if (user) {                 // The below two lines will add the newly saved review's                  // ObjectID to the the User's reviews array field                 user.reviews.push(review);                 user.save();                 res.json({ message: 'Review created!' });             }         });       })       .catch((error) => {         res.status(500).json({ error });       }); }; 

user_controller.js

 export const createUser = (req, res) => {    const user = new User();    user.username = req.body.username;    user.email = req.body.email;    user.save()        .then((result) => {             res.json({ message: 'User created!', result });         })         .catch((error) => {           res.status(500).json({ error });         });     };  // . . . // returns the user object associated with the username if any // with the reviews field containing an array of review objects  // consisting of the reviews created by the user export const getUser = (req, res) => {     User.findOne({ username: req.params.username })       .populate('reviews')       .then((result) => {         res.json(result);       })       .catch((error) => {         res.status(500).json({ error });       });   }; 
like image 44
College Student Avatar answered Sep 30 '22 01:09

College Student


As in population docs said

var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });

aaron.save(function (err) {
  if (err) return handleError(err);

  var story1 = new Story({
    title: "Once upon a timex.",
    _creator: aaron._id    // assign the _id from the person
  });

  story1.save(function (err) {
    if (err) return handleError(err);
    // thats it!
  });
  //then add story to person
  aaron.stories.push(story1);
  aaron.save(callback);
});
like image 40
Manasov Daniel Avatar answered Sep 30 '22 03:09

Manasov Daniel