Mongoose documents find by object _id - node.js

I have mongoose model like this:
const ChatRoomSchema = new mongoose.Schema(
{
roomId: {
type: String,
required: true,
},
sender: {
_id: { type: String },
name: { type: String },
dp: { type: String },
},
receiver: {
_id: { type: String },
name: { type: String },
dp: { type: String },
},
},
{
timestamps: true,
}
);
Now i want find documents by this query:
const rooms = await ChatRoom.find({sender: {_id: userId }}) // it return the document if
sender itself a _id,
but its a object.
It returns an empty array. How can I find the document throw the sender object's _id field?

Related

Pushing items to array in mongoose

i am trying to create items to specific id of collection using nodejs and mongoose but i am getting CastError which i shared below picture. So goal is, I binded items id in collection schema because collection has many items and when i create items to the specific id of collection i want to push them to items array in the collection schema.
ERROR
ITEMS ROUTE
itemRouter.post("/:collectionId", JWTAuthMiddleware, async (req, res, next) => {
const {name} = req.body
if (req.params.collectionId.length !== 24)
return next(createHttpError(400, "Invalid ID"));
const collection = await CollectionModal.findByIdAndUpdate(
req.params.collectionId,
{
$push : { items: { ...req.body, owner: req.user._id, id: uuidv4() } },
},
{ new: true }
);
if (!collection)
return next(
createHttpError(
400,
`The id ${req.params.collectionId} does not match any collections`
)
);
res.send(collection);
});
Collection schema
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const collectionSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
additionalCustomFields: {
fieldNumber: { type: Number },
fieldName: { type: String },
fieldType: { type: String },
fieldChecked: { type: Boolean },
fieldDate: { type: Date },
},
owner: { type: Schema.Types.ObjectId, ref: "User" },
items: [{ type: Schema.Types.ObjectId, ref: "Item" }],
},
{ timestamps: true }
);
// collectionSchema.index({ "$**": "text" });
export default model("Collection", collectionSchema);
ITEM schema
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const itemSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
comments: [
{
user: { type: Schema.Types.ObjectId, ref: "User", required: true },
text: { type: String },
},
],
tags: { type: String },
owner: { type: Schema.Types.ObjectId, ref: "User" },
likes: [{ type: Schema.Types.ObjectId, ref: "User" }],
collections: { type: Schema.Types.ObjectId, ref: "Collection" },
},
{ timestamps: true }
);
itemSchema.index({ "$**": "text" });
export default model("Item", itemSchema);
Are you sure that using referenced documents is the correct way to go here? Mongoose is complaining because you are trying to push whole objects to the "items" array, instead of ObjectIds of referenced documents.
You have 2 choices here:
1. use embedded documents
This way you can easily store objects directly to the items array, which does not allow you to store the Items in a seperate collection
2. first create Item documents, then push the reference to items array
In this case you have to first create the items in Item collection. Afterwards you can map the result to only ObjectIds and push these to items array of your collection document.

How can I update some fields of an embedded object using mongoose's findOneAndUpdate method and not lose the other fields?

router.put('/experience/update/:exp_id',
auth,
async (req, res) => {
const {
title,
company,
location,
from,
to,
current,
description
} = req.body;
const newExp = {};
newExp._id = req.params.exp_id;
if (title) newExp.title = title;
if (company) newExp.company = company;
if (location) newExp.location = location;
if (from) newExp.from = from;
if (to) newExp.to = to;
if (current) newExp.current = current;
if (description) newExp.description = description;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//UPDATE Experience
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
res.json(profile);
} catch (error) {
console.log(error.message);
res.status(500).send('Internal Server Error');
}
}
)
I am using the findOneAndUpdate method to update the experience field inside a profile mongoose model.
After accesssing the endpoint, I put the updated details, for eg. company and location. But I lose all the other fields. So how can I update only select fields while others remain unchanged ?
Below is the profile schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false
},
description: {
type: String,
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date,
required: true
},
current: {
type: Boolean,
default: false
},
description: {
type: String,
}
}
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedIn: {
type: String,
},
instagram: {
type: String,
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
There are some problems in your code.
You are passing only one argument to findOneAndUpdate. Ideally the syntax is findOneAndUpdate(filter, update). So basically you need to pass update query as 2nd argument.
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
In below code you are modifying the profile object and saving it. Which is not required. And this is also the reason why you are losing fields.
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
Solution-
We need to figure out the update part of findOneAndUpdate(filter, update).
Here is the update query -
db.collection.update({
"user": "5f96dc85ac5ae03160a024a8",
"experience._id": "5f9826c3a3fa002ce0f11853"
},
{
"$set": {
"experience.$": {
"current": false,
"_id": "5f9826c3a3fa002ce0f11853",
"title": "Senior developer",
"company": "Morgan Stanley",
"location": "Pune",
"from": "2017-04-30T18:30:00.000Z",
"to": "2020-07-08T18:30:00.000Z",
"description": "testing"
}
}
})
Try it here
Trying Mongoose way :
const filter = { user: req.user.id, "experience._id": req.params.exp_id }
const update = { $set: { "experience.$": newExp } }
profile = await Profile.findOneAndUpdate(filter,update);

Add timestamp to a new subdocument or subschema in mongoose

I have this document in mongo atlas
_id: 5f8939cbedf74e363c37dd86,
firstname: "Person",
lastname: "Person lastname",
sex: "Masculino",
age: "20",
birthDay: 2020-10-07T00:00:00.000+00:00,
vaccines: Array
0:Object
dose: Array
_id: 5f8939cbedf74e363c37dd87
vaccine:5f7023ad96f7ed21e85be521
createdAt:2020-10-16T06:12:27.726+00:00
updatedAt:2020-10-16T06:12:27.726+00:00
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
This is my mongoose schema
const mySchema = new Schema({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
sex: {
type: String,
required: true,
},
age: {
type: String,
required: true,
},
birthDay: {
type: Date,
required: true,
},
vaccines: [
{
type: new Schema(
{
vaccine: {
type: Schema.ObjectId,
ref: "Vaccine",
},
dose: Array,
},
{ timestamps: true }
),
},
],
});
every time I add a new person the vaccines array gets one new object with the timestamp as you can see, in my js file I use this code:
const addPerson = (person) => {
const myPerson= new Model(person);
return myPerson.save();
};
Then when I add a new vaccine for the same person this does not get the timestamp, I'm using this code for that:
const addPersonVaccine = async ({ params, body }) => {
if (!params) return Promise.reject("Invalid ID");
const vaccines = [body];
const foundPerson = await Model.updateOne(
{
_id: params,
},
{
$push: {
vaccines: vaccines,
},
}
);
return foundPerson;
};
This is what my body inside vaccines array has:
[ { vaccine: '5f72c909594ee82d107bf870', dose: 'Primera' } ]
The problem is that I have no results about the next timestamps, as you can see in my mongo atlas document:
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
Is that the best way to implement timestamps in subdocuments or sub schemas?
I will appreciate your answers, thnks 👏
You can use mongoose schema timestamps options to the inner schemas
const mongoose = require("mongoose");
const forumSchema = new mongoose.Schema(
{
title: { type: String, required: true },
biddings: [
{
type: new mongoose.Schema(
{
biddingId: String,
biddingPoints: Number
},
{ timestamps: true }
)
}
]
},
{ timestamps: true }
);
const Forum = mongoose.model("Forum", forumSchema);
module.exports = Forum;
for more Mongoose schema set timestamp on nested document

updateMany and elemMatch in with nested schemas in mongoose (Node.js)

I'm trying to query a MongoDB database via mongoose to updateMany the fields of my database. I suppose that the first request is correct because mongoose doesn't fire any error, but for the nested schemas, I'm getting the following error.
My goal is to delete the occurences of the userTag in friends and remove the friendRequestsSent when userTarget equals userTag, friendRequestsReceived when userRequest equals userTag and notification when data equals userTag.
Here are the schemas of my Model
const NotificationSchema = new Schema({
title: String,
type: Number,
icon: String,
data: String,
createdAt: { type: Date, default: Date.now },
})
const FriendRequestSchema = new Schema({
userRequest: { type: String, required: true },
userTarget: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
})
const UserSchema = new Schema({
tag: { type: String, required: true, unique: true },
friendRequestsSent: { type: [FriendRequestSchema] },
friendRequestsReceived: { type: [FriendRequestSchema] },
friends: { type: [String] },
notifications: { type: [NotificationSchema] },
})
The request
const updateResponse = await User.updateMany(
{
friends: { $elemMatch: { $eq: userTag } },
friendRequestsSent: {
userTarget: {
$elemMatch: { $eq: userTag },
},
},
friendRequestsReceived: {
userRequest: {
$elemMatch: { $eq: userTag },
},
},
notifications: {
data: {
$elemMatch: { $eq: userTag },
},
},
},
{
$pull: {
friends: userTag,
friendRequestsSent: { userTarget: userTag },
friendRequestsReceived: { userRequest: userTag },
notifications: { data: userTag },
},
}
)
The error
Error while deleting the user account: Cast to String failed for value "{ '$elemMatch': { '$eq': '0eQzaAwpt' } }" at path "userRequest" for model "User"
The userRequest field in friendRequestsReceived is type String, not array so $elemMatch will not work. Also, you don't need to use $elemMatch because you specify only a single condition in the $elemMatch expression as it says in the docs:
If you specify only a single condition in the $elemMatch expression, you do not need to use $elemMatch.
In your case, you just need to do something like (details here):
await User.updateMany({
friends: userTag,
"friendRequestsSent.userTarget" : userTag,
"friendRequestsReceived.userRequest": userTag,
"notifications.data": userTag
}...

Unable to push object into array in Mongodb collection but Id is inserted

I am using comment array in my schema as fallows. I want to push comments data into that comment array using nodejs api
var Schema = mongoose.Schema;
var myfeeds = new Schema({
title: {
type: String,
required: true
},
feed: {
type: String,
required: true
},
createdBy: {
type: String,
required: true,
unique: true
},
createdDate: {
type: Date,
required: true,
default: Date.now()
},
comment: [
{
commentBy: {
type: String
},
commentText: {
type: String
},
createdDate: {
type: Date
}
}
],
likes: [
{
likesCount: {
type: Number,
required: false
},
likeBy: {
type: String,
required: false
}
}
]
});
I want to push object to this comment array. so, for that I did in this way please tell me if anything wrong in this
let _id = req.body.id;
let commentBy = req.body.commentedBy;
let commentedText = req.body.commentedText;
let commentedDate = req.body.commentedDate;
let data = {
commentBy: commentBy,
commentText: commentedText,
createdDate: commentedDate
};
MyFeeds.findByIdAndUpdate(
{ _id: _id },
{
$push: {
comment: data
}
}
)
.then((result) => {
res.status(200).json({
status: result
});
})
.catch((err) => {
res.status(500).json({
status: 'invalid',
err: err
});
});
but only id are inserted into that comment array but not the required content
"comment": [
{
"_id": "5badfd092b73fa14f4f0aa7c"
},
{
"_id": "5badfd102b73fa14f4f0aa7d"
},
{
"_id": "5badfd142b73fa14f4f0aa7e"
},
{
"_id": "5badfd31500fb11bb06b4c8a"
},
{
"_id": "5badfd35500fb11bb06b4c8b"
},
{
"_id": "5badff3d439a151190d62961"
}
],

Resources