1

I have a user and a message model. The relation is the following: a user can have many messages.

Goal: I'm looking for the best or most elegant way of adding a new message and link it to the corresponding user.

User model:

const UserSchema = new Schema({
  username: {
    type: String,
    required: true,
    unique: true
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
  },
  register_date: {
    type: Date,
    default: Date.now
  },
  osrs_messages: [OsrsMessage]
});

Message model:

const OsrsMessageSchema = new Schema({
  message: {
    type: String,
    required: true,
  },
  username: {
    type: String,
    required: true,
    unique: true,
  },
  send_date: {
    type: Date,
    default: Date.now
  }
});

My router that adds a new message and adds the message to the user in question:

router.post("/osrs", verifyAccessToken, async (req, res) => {
  try {
    const { username, message } = req.body;
    // create a new message and save
    let newMessage = new OsrsMessage({ username, message });
    newMessage = await newMessage.save();
    // get the associated user and add new message to messages array
    let user = await User.findById(req.user.id);
    user.osrs_messages.push({ username, message });
    user = await user.save();
    return res.status(200).json({ message: "message received" });
  } catch (error) {
    console.log("error message:", error);
    return res
      .status(500)
      .json({ message: "Oops... looks like something went wrong" });
  }
});

Are there any ways I can improve this? Should I use findOneAndUpdate or insert instead? Can I add a message automatically to my message collection if I link it to a user or do I always have to manually add it to the collection?

2 Answers 2

3

Instead of 2 querys, findById and .save() you can go with 1 query updateOne.

router.post("/osrs", verifyAccessToken, async (req, res) => {
  try {
    const { username, message } = req.body;

    // create a new message and save
    let newMessage = new OsrsMessage({ username, message });

    newMessage = await newMessage.save();

    await User.updateOne({_id: req.user.id}, {
       $push: {
          osrs_messages: newMassage._id
       }
    });

    return res.status(200).json({ message: "message received" });

  } catch (error) {
    console.log("error message:", error);
    return res
      .status(500)
      .json({ message: "Oops... looks like something went wrong" });
  }
});
Sign up to request clarification or add additional context in comments.

Comments

1

You need to add messages to the collection manually. But you can improve your implementation by using Populate

Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s)

You can have a good overview of Populate here

The main advantage of using populate is, in the collection, it keeps the reference _id of the document and if you need to update any nested document, OsrsMessage in your case, you do not need to update/re-insert it in the User.

The documentation of populate has a good overview of how you can use it. I hope this one will help you to improve your current implementation.

You can write in this way,

const UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
register_date: {
type: Date,
default: Date.now
},
osrs_messages: [{ type: Schema.Types.ObjectId, ref: 'OsrsMessage' }]
});

In your code add the message in the following way

router.post("/osrs", verifyAccessToken, async (req, res) => {
try {
const { username, message } = req.body;
// create a new message and save
let newMessage = new OsrsMessage({ username, message });
newMessage = await newMessage.save();
// get the associated user and add new message to messages array
let user = await User.findById(req.user.id);
user.osrs_messages.push(newMessage._id);
user = await user.save();
return res.status(200).json({ message: "message received" });
} catch (error) {
console.log("error message:", error);
return res
  .status(500)
  .json({ message: "Oops... looks like something went wrong" });
}
});

After that, whenever you need to get the user with message you can do the query as like this

const user = await User.
    findById(req.user.id).
    populate({ path: 'osrs_messages'}).
    exec();

Here you will get the user with all osrs_messages as a list

3 Comments

As of how far I understand populate, I would use populate after creating the new message to set the message fields in the user collection right? Are u able to provide an updated answer on how something like this would look? I'm seeing multiple implementations of populate so I'm not quite sure
So populate is only used for when you are querying a result? I would still need to add the message to the message collection and then to the user?
@Dax Yes. But In your implementation, the message was stored 2 times, one as a message and again inside the message collection of the user. Using the populate you are just keeping the _id of the created message in the osrs_messages collection.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.