Wrong _id field using mongodb-memory-server and jest - node.js

I have an express controller that creates a new Post document like this
exports.createPost = async (req, res, next) => {
try {
const user = await User.findOne({ _id: req.uid });
if (!user) throw new ServerError(
"user with provided authorid doesn't exist",
400
);
const newPost = new Post({
author: req.uid,
title: req.body.title,
caption: req.body.caption,
media: req.body.media,
location: req.body.location
});
const createdPost = await newPost.save();
res.status(201).send(createdPost);
} catch (error) {
next(error);
}
};
When tested with postman it works as intended and the _id field shows no problems
{
author: "LJFlAgRjqXQ1K7HUSyj12EmxxXD2",
title: "title",
caption: "caption",
comments: [],
media: [],
likes: [],
location: { coordinates: [] },
_id: "61af0c16423073390394717d", // _id as expected
date: "2021-12-07T07:24:06.809Z",
__v: 0
}
But when testing the same controller with Jest and mongodb-memory-server, the _id field turns to be a call to the ObjectId construtor instead of being a new id
{
author: "61af0c43870651bbd1a7a81d",
title: "title",
caption: "caption",
comments: [],
media: [],
likes: [],
location: { coordinates: [] },
_id: new ObjectId("61af0c43870651bbd1a7a820"), // _id is a constructor call
date: "2021-12-07T07:24:51.653Z",
__v: 0
}
is this an issue with mongodb-memory-server? how would I get the correct id of the post to use it in a test?

Related

NodeJS, Mongoose if ID exists do nothing, if doesn't exist push the new one to the array

I have User model and every user can have an array of ownerId's. I want to make an API which will push a new ownerId to the array, but if this ownerId already exists, do nothing..
I have tried $addToSet but it doesn't work..
However it works with $push, but if ownerId already exists it keeps pushing the same ownerId so i have duplicates which is not OK..
User model
const UserSchema = new mongoose.Schema({
email: { type: String, required: true, min: 6, max: 255 },
password: { type: String, required: true, min: 4, max: 1024 },
role: { type: String, required: true, default: "User" },
owners: [
{
type: Schema.Types.ObjectId,
ref: "Owners",
required: false,
},
],
});
And my NodeJS Mongoose API:
exports.addOwnerToUser = async (req: Request, res: Response) => {
try {
let ObjectID = require("mongodb").ObjectID;
const user = {
email: req.body.email,
ownerId: req.body.ownerId,
};
const updatedUser = await User.findOneAndUpdate(
{ _id: req.params.userId },
{
$push: { owners: req.body.ownerId },
}
);
console.log(updatedUser);
res.status(201).json({ sucess: true, msg: "User updated sucessfully" });
} catch (err) {
res.status(404).json(err);
}
};
Thanks
$push will just push data in array, in your case you should use $addToSet
$addToSet only ensures that there are no duplicate items added to the set and does not affect existing duplicate elements.
const updatedUser = await User.findOneAndUpdate({
{ _id: req.params.userId },
'ownerId.ownerId': {
'$ne': new mongoose.ObjectID(req.body.ownerId)
}
}, {
$addToSet: {
'ownerId.ownerId': new mongoose.ObjectID(req.body.ownerId)
}
}, {
new: true
});
just remove below query
'ownerId.ownerId': {
'$ne': req.body.ownerId
}
Updated code.
const updatedUser = await User.findOneAndUpdate({
_id: req.params.userId,
}, {
$addToSet: {
'ownerId.ownerId': req.body.ownerId
}
}, {
new: true
});
OR
with ownerId Query
const updatedUser = await User.findOneAndUpdate({
_id: req.params.userId,
'ownerId.ownerId': {
'$ne': req.body.ownerId
}
}, {
$push: {
'ownerId': {ownerId: req.body.ownerId }
}
}, {
new: true
});
Try this:
exports.addOwnerToUser = async (req: Request, res: Response) => {
try {
let ObjectID = require("mongodb").ObjectID;
const user = {
email: req.body.email,
ownerId: req.body.ownerId,
};
const updatedUser = await User.findOne({
_id: req.params.userId
})
.then(user => {
if (user.ownerId[0]) {
user.ownerId[0].ownerId = req.body.ownerId;
}
})
console.log(updatedUser);
res.status(201).json({
sucess: true,
msg: "User updated sucessfully"
});
} catch (err) {
res.status(404).json(err);
}
};
Your schema design is not right, that is why $addToSet is not working for you.
so, if you want multiple owners in the user object please change your schema design to this
const UserSchema = new mongoose.Schema({
email: { type: String, required: true, min: 6, max: 255 },
password: { type: String, required: true, min: 4, max: 1024 },
role: { type: String, required: true, default: "User" },
owners: [{
type: Schema.Types.ObjectId,
ref: "Owners",
required: false,
}],
});
After this use $addToSet to add the new owner id in the user object
User.findOneAndUpdate(
{ _id: req.params.userId },
{ $addToSet: { owners: req.body.ownerId } }
)
it will not add the duplicates
For Reference: https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/
Note:
As per my previous experience with this kind of work, it is better if you change the key ownerId to owners
because in general these are the owners array not the ownerId

Inesrt data into Nested Array (comment action) MongoDB

I'm building a simple social platform; I'm currently building out the schema for commenting on a post, but users should be able to comment on other comments as well. So what I'm trying to do is, once a user comments on the post, a condition is being checked whether the postId params matches the post._id in the database, therefore this is a higher order comment action. Now if I were to comment on somebody else's comment, I would be checking whether the post._id matches the post as well as checking whether the comment._id matches as well, but I'm having issues by inserting it into the nested array.
Schema (POST)
onst mongoose = require('mongoose');
const Schema = mongoose.Schema;
const postSchema = new Schema({
uid: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "users",
},
uname: {type: String, required: true},
upic: {type: String, required: true},
message: { type: String,},
media: {type: String,},
likes:[],
comments:[],
commentCount: {type: Number, default: 0},
likeCount: {type: Number, default: 0},
repostCount: {type: Number, default: 0},
postedOn: { type: Date},
});
postSchema.pre('save', function(next){
this.postedOn = Date.now();
next();
});
var PostModel = mongoose.model('posts', postSchema);
module.exports = PostModel;
Controller (Comment)
{/* Higher order comment */}
module.exports.commentPost = async (req, res) => {
try{
await PostModel.findByIdAndUpdate(req.params.postId,
{
$push: {
comments: [{
_id: uuid.v1(),
postId: req.params.postId,
uid: req.user._id,
message: req.body.message,
postedOn: Date.now(),
uname: req.user.name,
upic: req.user.pic,
likes: [],
comments: [],
likeCount: 0,
commentCount: 0,
}]
},
$inc: { commentCount: 1}
}).exec((err,result)=>{
if(err){
return res.status(422).json({error:err})
}else{
res.json(result)
}
});
} catch(err){
throw new ErrorResponse(err, 404);
}
}
{/* Lower order comment */}
module.exports.commentCommentPost = async (req, res) => {
try{
await PostModel.findByIdAndUpdate({"_id": req.params.postId, "comments._id": req.body.commentId},
{
$push: {
comments: [{
comments: [{
_id: uuid.v1(),
postId: req.params.postId,
uid: req.user._id,
message: req.body.message,
postedOn: Date.now(),
uname: req.user.name,
upic: req.user.pic,
likes: [],
comments: [],
likeCount: 0,
commentCount: 0,
}]
}]
},
$inc: { commentCount: 1}
}).exec((err,result)=>{
if(err){
return res.status(422).json({error:err})
}else{
res.json(result)
}
});
} catch(err){
throw new ErrorResponse(err, 404);
}
}
I must be doing something wrong by updating the nested array within a nested array, any help would be appreciated.
I was able to fix the issue, here is the solution on what I've done, which works perfectly fine now.
module.exports.commentCommentPost = async (req, res) => {
try{
await PostModel.update({_id: req.params.postId, 'comments._id': req.body.commentId},
{
$push: {
'comments.$.comments': {
_id: uuid.v1(),
postId: req.params.postId,
uid: req.user._id,
message: req.body.message,
postedOn: Date.now(),
uname: req.user.name,
upic: req.user.pic,
likes: [],
comments: [],
likeCount: 0,
commentCount: 0,
}
}
}).exec((err,result)=>{
if(err){
return res.status(422).json({error:err})
}else{
res.status(200).json(result)
}
});
} catch(err){
throw new ErrorResponse(err, 404);
}
}

Find ObjectId of an element inside an array of docs, that is inside a model with Mongoose

I have this model:
const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
firstname: String,
lastname: String,
password: {
type: String,
required: true
},
activities: [{
description: {
type: String,
required: true
},
status: String,
from: Date,
to: Date
}]
}, { timestamps: true })
After I get the user with User.findById(), how can I get the ObjectId of an activity inside the activities array so I can perform (for example) delete and update operations?
EDIT 1
I can see every ObjectId with console.log() or in my GUI because MongoDB creates an ObjectId for every element inside the array even if I don't specify the field _id for each element. Here's a sample:
USER ACTIVITIES [
{
_id: 603765c7bb1d1c24cc7f80ff,
description: 'Complete the project',
status: 'pending',
from: 2021-02-25T08:54:31.719Z,
to: 2021-02-25T08:54:31.719Z
},
{
_id: 60377672bb1d1c24cc7f8100,
description: 'Figure out how to get the ObjectId',
status: 'pending',
from: 2021-02-25T10:05:38.144Z,
to: 2021-02-25T10:05:38.144Z
}
]
I'd like to access this _id so I can perform operations on a single activity
I've just found a solution. In my routes file I've added one more parameter in the endpoint's path (the element ID) so I can access it via req.params. Then everything is simple. Here's a sample:
Route
router.delete('/activities/:id/:activityId', userController.deleteActivity);
The logic
exports.deleteActivity = async (req, res, next) => {
try {
const id = req.params.id;
const user = await User.findById(id);
if (user) {
const activityId = req.params.activityId;
await user.activities.pull(activityId);
await user.save();
console.log("ACTIVITY REMOVED! YOUR ACTIVITIES", user.activities);
return res.status(200).json({ activities: user.activities });
}
return res.status(400).json({ message: "USER NOT FOUND!" });
} catch (error) {
console.log(error);
res.status(500).json({ error });
}
};

Need to push data in nested subdocument array

I need to push data in nested subdocument array(replyComment):
This is an example of a document from my database:
{
comments: [
{
replyComment: [],
_id: 601a673735644c83e0aa1be3,
username: 'xyz123#gmail.com',
email: 'xyz213#gmail.com',
comment: 'test123'
},
{
replyComment: [],
_id: 601a6c94d1653c618c75ceae,
username: 'xyz123#gmail.com',
email: 'xyz123#gmail.com',
comment: 'reply test'
}
],
_id: 601a3b8038b13e70405cf9ea,
title: 'latest test',
snippet: 'latest test snippet',
body: 'latest test body',
createdAt: 2021-02-03T05:58:24.123Z,
updatedAt: 2021-02-03T12:28:33.237Z,
__v: 7
}
I am also mentioning my code snippet:
app.post('/:id/replyComment',(req,res) => {
const replyComm = new Comment(req.body);
Topic.findById(req.params.id)
.then((result) => {
topic = result,
console.log(topic);
topic.update({_id:req.params.id, "comments._id": req.body.comment_id},
{ $push: {"comments.$.replyComment": {replyComment: replyComm}}}
)
topic.save()
.then((result) => {
// console.log(result);
res.send({
text: "Replied",
})
})
.catch((err) => {
console.log(err);
});
})
});
By running the request I am not getting any error but still the same documented is getting printed on my terminal and there is no change in "replyComment" subarray.
Pls suggest how to make this work or any alternate method.
I prefer to use objects instead of arrays by converting objects to the array Object.keys(data.comments)
{
comments:
{
'601a673735644c83e0aa1be3':{
username: 'samyakjain971#gmail.com',
email: 'samyakjain971#gmail.com',
comment: 'test123'
}
},
{
'601a6c94d1653c618c75ceae':{
username: 'samyakjain971#gmail.com',
email: 'samyakjain971#gmail.com',
comment: 'reply test'
}
},
_id: 601a3b8038b13e70405cf9ea,
title: 'latest test',
snippet: 'latest test snippet',
body: 'latest test body',
createdAt: 2021-02-03T05:58:24.123Z,
updatedAt: 2021-02-03T12:28:33.237Z,
__v: 7
}
define topic variable like this :
let topic = result;
console.log(topic);
topic.update({_id:req.params.id, "comments._id": req.body.comment_id},
{ $push: {"comments.$.replyComment": {replyComment: replyComm}}}
)

populate fields of nested object in mongoose schema

I have these two models
var postSchema = new Schema({
content : String,
author : [{
user : {
type: Schema.ObjectId,
ref: 'user'
},
bool: Boolean
}]
});
var userSchema = new Schema({
name : String
});
I'm trying to populate Post with user name instead of the just showing [Object] or the id if i .toString it
what i'm getting at the moment :
{ author: [ [Object], [Object], [Object] ],
_id: 5aedc4d3c534213279faec79,
content: 'hello',
__v: 0 } ]
with .toString() i get
{ author:
[ { _id: 5aedc364ce3b022d4ff74922,
user: 5aedb9e47e055d1ac26aa572,
bool: true } ]
what i want :
{ author:
[ { _id: 5aedc4d3c534213279faec7c,
user: "Some name here", //( name instead of user _id )
bool: true },
{ _id: 5aedc4d3c534213279faec7b,
user: "Some name here",
bool: false },
{ _id: 5aedc4d3c534213279faec7a,
user: "Some name here",
bool: true } ],
_id: 5aedc4d3c534213279faec79,
content: 'hello',
__v: 0 }
is this possible ?
You can use the populate method with the nested syntax
const Post = mongoose.model('Post', postSchema);
const User = mongoose.model('User', userSchema);
Post
.find({})
.populate({path:'author.user', 'model':'User'})
.exec((err, posts) => {
if (err) return next(err);
posts.forEach(post => {
console.log(JSON.stringify( post, null, 2) ); // spacing level = 2
}
});
Will output for each post (this is not exactly what you required, but hopefully you may have flexibility in the User format
{
_id: 5aedc4d3c534213279faec79,
content: 'some content',
author: [
{
user: {_id: 5aedc4d3c534213279faec7c, "user name here"}
bool: true
},
{
user: {_id: 5aedc4d3c534213279faec7b, "user name 2 here"}
bool: true
}
__v: 0
}
Reference for mongoose.populate
http://mongoosejs.com/docs/populate.html

Resources