DEV Community

Zach
Zach

Posted on

MongoDB: One-to-Few

Following up on my last post, I'm sharing here my approach to relating answers to questions in that section of my blog project (and what I'm building for my HR 'MVP' project).

I'm anticipating relatively few answers for each question. Taking an example from the excellent official Mongo Schema Design guide, I could have gone with a design that embedded answers within each question.

It would have looked similar to this:

> db.person.findOne()
{
  name: 'Kate Monster',
  ssn: '123-456-7890',
  addresses : [
     { street: '123 Sesame St', city: 'Anytown', cc: 'USA' },
     { street: '123 Avenue Q', city: 'New York', cc: 'USA' }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The problem with this is that accessing those addresses outside of the context of the question involves searching first by person. That wouldn't work for what I'm trying to do. So Instead, I'm going with a child-referencing one-to-few schema.

Something like:

{
    name : 'left-handed smoke shifter',
    manufacturer : 'Acme Corp',
    catalog_number: 1234,
    parts : [     // array of references to Part documents
        ObjectID('AAAA'),    // reference to the #4 grommet above
        ObjectID('F17C'),    // reference to a different Part
        ObjectID('D2AA'),
        // etc
    ]
Enter fullscreen mode Exit fullscreen mode

To be able to have a quick reference for the question associated with each answer, I utilized two-way referencing by adding the question id to each answer.

Here are my models:

let questionsSchema = new mongoose.Schema({
  title: {type: String, required: true, unique: true},
  text: {type: String, required: true},
  date: { type: Date, default: Date.now },
  author: mongoose.ObjectId,
  answers: []
})

let answersSchema = new mongoose.Schema({
  text: {type: String, required: true},
  date: { type: Date, default: Date.now },
  author: mongoose.ObjectId,
  question: mongoose.ObjectId
})
Enter fullscreen mode Exit fullscreen mode

Alright, so first: What's the deal with ObjectIds?

ObjectIds appear to be a special type of Mongo/Mongoose object whose critical information is a unique string id associated with each object.

I like to play with the models a bit before committing them to code, so to practice creating answers (which need an ObjectId) I found this way of manually creating ObjectIds:

let q_Id = "6125b4b858dd643905644abc"
q_Id = mongoose.Types.ObjectId(q_Id)
Enter fullscreen mode Exit fullscreen mode

In the real application, I won't need to do this, because the question id will be known to the app when creating a new answer.

The trick is pushing each answer's id into the Question's answer array upon creating the answer.

To do that, I followed [this example]:(https://www.bezkoder.com/mongoose-one-to-many-relationship/):


const createImage = function(tutorialId, image) {
  console.log("\n>> Add Image:\n", image);
  return db.Tutorial.findByIdAndUpdate(
    tutorialId,
    {
      $push: {
        images: {
          url: image.url,
          caption: image.caption
        }
      }
    },
    { new: true, useFindAndModify: false }
  );
};
Enter fullscreen mode Exit fullscreen mode

To come up with this process of answer-creation and question-updating:

const createA = function(){
  let answer = new QA.answersModel({
    text: 'xthtAnswer',
    author: idToQuery,
    question: q_Id
  })
  return answer.save()
  .then(answer => {
    console.log(answer)
    return QA.questionsModel.findByIdAndUpdate(
      q_Id,
      {
        $push:{
          answers: answer._id
        }
      },
      {new:true}
    )
    .then(doc => {
      console.log(doc)
    })
  })
  .catch(err => {
    console.log(err)
  })
}

createA()
Enter fullscreen mode Exit fullscreen mode

I'm not happy with the way that promises work here. I'm going to come back around and clean it up, maybe using Promise.all() as described here and here.

Top comments (0)