I have the following mongodb document:
{
"_id" : ObjectId("5ee95b41ca023a3deb252ef2"),
"uid" : "jdoe",
"name" : "John Doe",
"employee_hire_date" : "2012-06-20",
"three_month_review_target" : "2012-09-20",
"six_month_review_target" : "2012-12-20",
"three_month_review_status" : {
"is_scheduled" : null,
"is_team_member_emailed" : null,
"is_review_executed" : null,
},
"six_month_review_status" : {
"is_scheduled" : null,
"is_team_member_emailed" : null,
"is_review_executed" : null,
}
}
I would like to update the three_month_review_status.is_scheduled nested property to true. I am using a put request to accomplish this:
const mongoose = require('mongoose');
const Reviews = require('../modules/reviews/models/reviews');
module.exports = (app) => {
app.put('/api/reviews/isScheduled', async (req, res) => {
console.log('body', req.body)
console.log('uid', req.body.uid)
console.log('is_scheduled', req.body.three_month_review_status.is_scheduled)
Reviews.findOneAndUpdate( { 'uid': req.body.uid }, { $set: { 'three_month_review_status.is_scheduled': req.body.is_scheduled }}, (err, result) => {
if (err) {
console.log('error', err)
}
else {
console.log('updated', result);
res.status(200).send(result);
}
} )
});
}
To test this, I execute this PUT request through Postman with the following request body:
{
"uid": "jdoe",
"three_month_review_status": {
"is_scheduled": "true"
}
}
However, when the request gets executed, the other two nested objects are removed and is_scheduled remains null. This is the document after the request is executed:
{
"_id" : ObjectId("5ee95b41ca023a3deb252ef2"),
"uid" : "jdoe",
"name" : "John Doe",
"employee_hire_date" : "2012-06-20",
"three_month_review_target" : "2012-09-20",
"six_month_review_target" : "2012-12-20",
"three_month_review_status" : {
"is_scheduled" : null
},
"six_month_review_status" : {
"is_scheduled" : null,
"is_team_member_emailed" : null,
"is_review_executed" : null,
}
}
What am I doing wrong? Here is my reviewsSchema for more context:
const { Schema, model } = require('mongoose');
const reviewsSchema = new Schema({
uid: String,
name: String,
employee_hire_date: String,
three_month_review_target: String,
six_month_review_target: String,
three_month_review_status: {
is_scheduled: Boolean,
is_team_member_emailed: Boolean,
is_review_executed: Boolean,
},
six_month_review_status: {
is_scheduled: Boolean,
is_team_member_emailed: Boolean,
is_review_executed: Boolean,
},
})
const Reviews = model('review', reviewsSchema);
module.exports = Reviews;
In Mongoose you don't need to specify the $set. Also based on the sample JSON that you send from the postman instead of req.body.is_scheduled you need to provide req.body.three_month_review_status.is_scheduled in the query. Also, need to add {new: true} if you want the findOneAndUpdate to return the updated document
So you can update the query like
Reviews.findOneAndUpdate(
{ uid: req.body.uid },
{
"three_month_review_status.is_scheduled":
req.body.three_month_review_status.is_scheduled,
},
{ new: true },
(err, result) => {
if (err) {
console.log("error", err);
} else {
console.log("updated", result);
res.status(200).send(result);
}
}
);
Related
I'm trying to write a loopback 4 filter that returns the objects where creatorId = userId OR userId in sharedUsers = userId but I cant seem to formulate the filter correctly
(neither of these properties are unique id's)
this is what my object shape looks like:
{
"_id" : "20",
"configMetadata" : {
...
"creatorId" : "50",
"creatorName" : "Mark"
},
"sharedUsers" : [
{
"userId" : "15"
},
{
"userId" : "20"
}
],
"sharedRoles" : ....,
"tiles" : ...
}
here is what I tried
const filter2: Filter<ProductViewConfig> =
{
where: {
or: [
{ configMetadata : { creatorId : userId} },
{ sharedUsers: { [userId]: userId } },
],
},
};
but I'm getting 2 errors: one is : configMetadata is missing the rest of the properties, so just properties mismatch and for sharedUsers I'm getting a MongoError: unknown operator: $15 when I enter userId = 15
to provide context: this is what my endpoint looks like
#get('/product-view-configs/{userId}')
#response(200, {
description: 'ProductViewConfig model instance',
content: {
'application/json': {
schema: getModelSchemaRef(ProductViewConfig),
},
},
})
async findIt(
#param.path.string('userId') userId: string,
#param.filter(ProductViewConfig, { exclude: 'where' }) filter?: FilterExcludingWhere<ProductViewConfig>
): Promise<ProductViewConfig> {
//returns configs where creatorId = userId OR userId in sharedUsers = userId
const filter2: Filter<ProductViewConfig> =
{
where: {
or: [
{ configMetadata : { creatorId : userId} },
{ sharedUsers: { [userId]: userId } },
],
},
};
const records = this.productViewConfigRepository.find(filter2);
return this.productViewConfigRepository.findById(userId, filter);
}
can anyone point me in the right direction? I couldn't find examples on how to filter nested objects in the documentation, so any help would be appreciated!
This is the structure i have, i want to update the nested array element if an object key matches for example - i want to match grnno :"10431000" and update the other keys of that object like vehicle_no,invoice_no etc.
{
"_id" : ObjectId("5f128b8aeb27bb63057e3887"),
"requirements" : [
{
"grns" : [
{
"invoice_no" : "123",
"vehicle_no" : "345",
"req_id" : "5f128c6deb27bb63057e388a",
"grnno" : "10431000"
},
{
"invoice_no" : "abc",
"vehicle_no" : "def",
"req_id" : "5f128c6deb27bb63057e388a",
"grnno" : "10431001"
}
]
}
]
}
I have tried this code
db.po_grn.update({
"requirements.grns.grnno":"10431001"
}, {
$set: {
"requirements.$.grns": {"invoice_no":"test",vehicle_no:"5455"}
}
})
But this is changing the structure i have like this
"requirements" : [
{
"grns" : {
"invoice_no" : "test",
"vehicle_no":"5455"
},
"req_id" : ObjectId("5f128b8aeb27bb63057e3886")
}
],
grns key should be array, and update should be of the particular object which matches the key "grnno". Please help me out. Thanks.
==Edit==
var grnno = req.body.grnno;
db.po_grn.find({
"requirements.grns.grnno":grnno
}).toArray(function(err, po_grn) {
console.log("po_grn",po_grn);
if (po_grn.length > 0) {
console.log("data.grn.grnno ", grnno);
var query = {
requirements: {
$elemMatch: {
"grns.grnno": grnno
}
}
};
var update = {
$set: {
'requirements.$[].grns.$[inner].invoice_no': data.invoice_no,
'requirements.$[].grns.$[inner].vehicle_no': data.vehicle_no,
}
};
var options = {
arrayFilters: [
{ "inner.grnno" : grnno }
]
};
db.po_grn.update(query, update, options
, function(er, grn) {
console.log("grn",grn,"er",er)
res.send({
status: 1,
message: "Grn updated successfully"
});
}
);
} else {
res.send({
status: 0,
message: "Grn not found "
});
}
})
Use a combination of $[] positional-all operator with array filters to update your inner nested document.
var query = {
requirements: {
$elemMatch: {
"grns.grnno": "10431001"
}
}
};
var update = {
$set: {
'requirements.$[].grns.$[inner].invoice_no': "test",
'requirements.$[].grns.$[inner].vehicle_no': "5455",
}
};
var options = {
arrayFilters: [
{ "inner.grnno" : "10431001" }
]
};
db.collection.update(query, update, options);
Update -
NodeJS native MongoDb driver code attached, which is working fine
const { MongoClient } = require('mongodb');
const url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) {
throw err;
}
const dbo = db.db("test");
(async() => {
const query = {
requirements: {
$elemMatch: {
"grns.grnno": "10431001"
}
}
};
const update = {
$set: {
'requirements.$[].grns.$[inner].invoice_no': "test",
'requirements.$[].grns.$[inner].vehicle_no': "5455",
}
};
const options = {
arrayFilters: [
{ "inner.grnno" : "10431001" }
],
multi: true
};
try {
const updateResult = await dbo.collection("collection").update(query, update, options);
} catch (err) {
console.error(err);
}
db.close();
})();
});
while i was going through my problem on StackOverflow,i noticed same question was asked before aswell,but none of them had got a good response,or an actual answer.
Mongoose Find One on Nested Object
How can I find nested objects in a document?
well back to my question: i wanted to find the object that is nested in the schema. trying findMany gives all the objects,and findOne give just the first one,but i want particular objects whose id i pass through req.body.checkbox.
my JS code goes like..
app.post("/data", uploads, function (req, res) {
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
var checkedBox = req.body.checkbox;
console.log(checkedBox);
User.findMany({_id:foundUser._id},{comments:{$elemMatch:{_id:checkedBox}}} ,function(err,checkedobj){
if(err){
console.log(err);
}
else{
console.log(checkedobj.comments);
if (Array.isArray(checkedobj.comments)) {
res.render("checkout",{SIMG: checkedobj.comments});
} else {
res.render("checkout",{SIMG: [checkedobj.comments]});
}
}
})
}
}
});
});
here is my schema,for reference
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
permission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
permission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
example
{
"_id" : ObjectId("5ec3f54adfaa1560c0f97cbf"),
"firstname" : "q",
"lastname" : "q",
"username" : "q#q.com",
"salt" : "***",
"hash" : "***",
"__v" : NumberInt(2),
"comments" : [
{
"permission" : NumberInt(0),
"_id" : ObjectId("5ec511e54db483837885793f"),
"comment" : "hi",
"imagename" : "image-1589973477170.PNG"
}
],
"permission" : NumberInt(1)
}
also when i check 3 checkboxes, console.log(checkBox) logs:
[
'5ec543d351e2db83481e878e',
'5ec589369d3e9b606446b776',
'5ec6463c4df40f79e8f1783b'
]
but console.log(checkedobj.comments) gives only one object.
[
{
permission: 0,
_id: 5ec543d351e2db83481e878e,
comment: 'q',
imagename: 'image-1589986259358.jpeg'
}
]
When you want multiple matching elements from an array you should use $filter aggregation operator
And as a precaution, first check req.body.checkbox is an array or not and convert it into an array of ObjectIds
app.post("/data", uploads, function (req, res) {
var ObjectId = mongoose.Types.ObjectId;
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
console.log(checkedBox);
var checkedBoxArray = checkedBox.map(id => ObjectId(id))
User.aggregate([
{$match: {_id: foundUser._id}},
{
$project: {
comments: {
$filter: {
input: "$comments",
as: "comment",
cond: { $in: [ "$$comment._id", checkedBoxArray ] }
}
}
}
}
],function(err,checkedobj){
if(err){
console.log(err);
}
else{
console.log(checkedobj[0].comments);
if (Array.isArray(checkedobj[0].comments)) {
res.render("checkout",{SIMG: checkedobj[0].comments});
} else {
res.render("checkout",{SIMG: [checkedobj[0].comments]});
}
}
})
}
}
});
});
Working example - https://mongoplayground.net/p/HnfrB6e4E3C
Above example will return only 2 comments matching the ids
You can make use of findById() method, more documentation about it is provided here
You can use something like this to search by object id:-
var id = "123";
userSchema.findById(id, function (err, user) { ... } );
Hope this helps!
I want to increment a property value of an object if it does exist inside an array.
Mongo record:
{
"_id" : ObjectId("5b7bdd9f0465e8345ba83aad"),
"userID" : "400",
"userName" : "Jon Snow",
"pageName" : "1",
"courseName" : "Maths",
"socketID" : [
"aswKWYyE1euk2GNIAAAD"
],
"online" : true,
"userHistory" : {
"pagesVisited" : [
{
"page" : "1",
"timesVisited" : 1
}
],
"coursesVisited" : [
"Maths"
]
},
"date" : ISODate("2018-08-21T09:38:39.281Z")
}
Here on userHistory.pagesVisited on the page property if I get the value 1 again then I want to increment the timesVisited property like so:
"pagesVisited" : [
{
"page" : "1",
"timesVisited" : 2
}
],
Here's what I have tried with no luck:
let userDetails = {
userID: queryUser.userID,
userName: queryUser.username,
pageName: queryUser.pageName,
courseName: queryUser.courseName,
socketID: [socket.id],
online: true,
userHistory: {
pagesVisited: [
{
"page" : queryUser.pageName,
"timesVisited" : 1
}
],
coursesVisited: [queryUser.courseName]
},
date: new Date()
};
onlineUsersDB.findOne({'userID': userDetails.userID}).then(async (user) => {
if (user) {
let page = {"page": queryUser.pageName, "timesVisited": 1};
let course = queryUser.courseName;
let updatedUser = await onlineUsersDB.findOneAndUpdate(
{'userID': user.userID},
{
$set: {'online': true},
$push: { 'socketID': socket.id },
},
{ $addToSet: { 'userHistory.coursesVisited': course } },
{ returnOriginal: false }
);
let updatedUserPageRef = updatedUser.value.userHistory.pagesVisited;
if (updatedUserPageRef) {
let pageFound = await updatedUserPageRef.findIndex(item => item.page === page);
if (pageFound >= 0) {
let updatedUserPage = await onlineUsersDB.findOneAndUpdate(
{'userID': updatedUser.value.userID},
// Here I want to reference the variable pageFound
{$inc: { 'userHistory.pagesVisited.[pageFound].timesVisited': 1 }},
{ returnOriginal: false }
);
console.log(JSON.stringify(updatedUserPage,null, 2));
}
}
let users = await onlineUsersDB.find({'online': true}).toArray();
io.to(room).emit('online-users', users);
io.to(room).emit('user-back-online', updatedUser);
} else {
if (userDetails.userID !== '100') {
await onlineUsersDB.insert(userDetails);
}
let users = await onlineUsersDB.find({'online': true}).toArray();
io.to(room).emit('online-users', users);
}
}).catch((e) => console.log(e));
In the above code where my comment is I want to reference the variable pageFound in my object dot notation like so:
{$inc: { 'userHistory.pagesVisited.[pageFound].timesVisited': 1 }}
It works when I give it a hardcoded value like:
{$inc: { 'userHistory.pagesVisited.0.timesVisited': 1 }}
After experimenting a little bit I made it to work like this:
I broke out my query string into another variable using template literals.
let pageInc = `userHistory.pagesVisited.${pageFound}.timesVisited`;
And then referenced the variable in my query like so:
{$inc: { [pageInc]: 1 }}
And it works now.
I have a Model wich contains an array of sub-documents. This is a Company:
{
"_id" : ObjectId(":58be7c236dcb5f2feff91ac0"),
"name" : "sky srl",
"contacts" : [
{
"_id" : ObjectId("58be7c236dcb5f2feff91ac2"),
"name": { type: String, required: true },
"company" : ObjectId("58be7c236dcb5f2feff91ac0"),
"email" : "sky#gmail.com",
"chatId" : "",
"phone" : "123456789",
"name" : "John Smith"
},
{
"_id" : ObjectId("58be7f3a6dcb5f2feff91ad3"),
"company" : ObjectId("58be7f3a6dcb5f2feff91ad1"),
"email" : "beta#gmail.com",
"chatId" : "",
"phone" : "987654321",
"name" : "Bill Gaset"
}
],
"__v" : 1
}
I have several companies, and I want to update the field chatId of all the contacts in all the companies, that matches the phone I am searching for.
My Schema definitions (simplified, for focusing on question):
var contactSchema = new Schema({
[...]
phone: { type: String, required: true },
email: { type: String },
chatId: { type: String },
company: Schema.Types.ObjectId,
});
var companySchema = new Schema({
name: { type: String, required: true },
type: { type: String, default: "company" },
contacts: [contactSchema]
});
I tried
var conditions = { "contacts.phone": req.body.phone };
var partialUpdate = req.body; //it contains 'req.body.phone' and 'req.body.chatId' attributes
Company.find(conditions).then(
function (results) {
results.map( function(companyFound) {
companyFound.contacts.forEach(function (contactContainer){
if (contactContainer.phone == partialUpdate.phone) {
contactContainer.chatId = partialUpdate.chatId;
Company.save();
companyFound.save();
contactContainer.save();
results.save();
}
//not sure of what to save, so i save everything
companyFound.save();
contactContainer.save();
results.save();
});
});
});
following this answer; but it doesn't works. It does not save anything, what I'm doing wrong?
I have never done this before, but worth a try.
Maybe you need to use $elemMatch.
// find the companies that have contacts having the phone number
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone }}).exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
// see if you can at least get the query to work
console.log(companies);
async.eachSeries(companies, function updateCompany(company, done) {
// find and update the contacts having the phone number
company.contacts.forEach(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].chatId = req.body.chatId;
}
});
company.save(done);
}, function allDone (err) {
console.log(err);
});
});
Note, I am using async.js to do async operations on multiple items.
Honestly, I would have simply made contacts an array of Contact references -- much easier to query and update.
Just for the records: I did this to make it work without async.js:
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone } })
.exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
console.log("companies: " + JSON.stringify(companies, null, 4));
companies.forEach(function (company) {
company.contacts.map(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].telegramChatId = req.body.telegramChatId;
}
});
company.save();
},
function allDone(err) {
console.log(err);
});
});`