I'm trying to update a value inside my array of objects.
Looking at the above mongoDB schema what I want is:
Find an expense with the ID match with the _id and need to update the fields with new ones from the req.body.
Just need to update the: expensesType, description, price and status.
The following code is what I tried to do.
First I need to match the right expense and it works fine but when I try to house.save() show me a message 'house.save is not a function'. So I think maybe I need to use a mongoDB function to get the result.
router.put("/editExpense/:id", ensureAuthenticated, (req, res) => {
var id = mongoose.Types.ObjectId(req.params.id);
House.find(
{ "expensesHouse._id": id },
{
members: 1,
name: 1,
description: 1,
address: 1,
type: 1,
user: 1,
userID: 1,
userType: 1,
expensesHouse: { $elemMatch: { _id: id } },
date: 1
}
).then(house => {
console.log(house);
expenseType = req.body.expenseType;
description = req.body.description;
price = req.body.price;
status = req.body.status;
house.save().then(() => {
req.flash("success_msg", "Expenses Updated");
res.redirect("/houses/dashboard");
});
});
});
****** UPDATED ******
After a search I found this updateOne and after adjusts, this is my final result but this way I delete every record..
router.put("/editExpense/:id", ensureAuthenticated, (req, res) => {
var id = mongoose.Types.ObjectId(req.params.id);
House.updateOne(
{ "expensesHouse._id": id },
{
members: 1,
name: 1,
description: 1,
address: 1,
type: 1,
user: 1,
userID: 1,
userType: 1,
expensesHouse: { $elemMatch: { _id: id } },
date: 1
},
{ $set: { "expensesHouse.expenseType": req.body.expenseType } }
).then(house => {
req.flash("success_msg", "Expenses Updated");
res.redirect("/houses/dashboard");
});
});
*********** RESOLUTION ***********
I just fixed the problem the way I show below.
House.updateOne(
{ "expensesHouse._id": id },
{
$set: {
expensesHouse: {
expenseType: req.body.expenseType,
description: req.body.description,
price: req.body.price,
status: req.body.status
}
}
}
You are really close to the answer the problem right now that you are having is syntax difference between find and UpdateOne
This is what Find expects, Check MongoDB docs
db.collection.find(query, projection)
This is what updateOne expects, Check Mongo docs
db.collection.updateOne(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
See the Difference? Second parameter should be update not projection because Update one
returns
matchedCount containing the number of matched documents
modifiedCount containing the number of modified documents
upsertedId containing the _id for the upserted document.
A boolean acknowledged as true if the operation ran with write concern or false if write concern was disabled.
So Your code should be
House.updateOne(
{ "expensesHouse._id": id },
{ $set: { "expensesHouse.expenseType": req.body.expenseType } }
).then(house => {
req.flash("success_msg", "Expenses Updated");
res.redirect("/houses/dashboard");
});
});
House.findOneAndUpdate({userId : req.params.userId},
{ $set: { "expensesHouse.$[element].status": req.body.status } },
{ multi:true, arrayFilters: [{ "element.userID" : req.params.subUserId }], new:true })
Your Api reuquest consist of both the IDs (outer as well as inner) like /api/update/:userId/:subUserId
Related
I try to query data from mongodb with spesific column, at first it returns all the value and columns i need but then when i try to change the value of the timestamp to date it shows error like this
Options must be an object, got \"_id formId title username date createdAt\"
Here is my code
const answers = await Answer.find(
{ userId: req.params.userId },
{ $toDate: "createdAt" },
"_id formId title username date createdAt"
);
i wonder where did i do wrong here...
Try using aggregate:
const answers = await Answer.aggregate([
{ $match: { userId: req.params.userId } },
{
$project: {
formId: 1,
title: 1,
username: 1,
date: 1,
createdAt: { $toDate: '$createdAt' },
},
},
]);
I have an array of reviews, I want to add a review using addToSet that will check if user is present in the array, then we do not want to add since one user can only review once.
My schema looks like this:
const sellerSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
reviews: [
{
by: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
title: {
type: String,
},
message: {
type: String,
},
rating: Number,
imagesUri: [{ String }],
timestamp: {
type: Date,
default: Date.now,
},
},
],
});
I might be doing the query wrong, but can't figure out how to add a review and check if current user has not reviewed before.
Here is the query where I add the review:
router.post("/review/:_id/", async (req, res) => {
try {
const stylist_id = mongoose.Types.ObjectId(req.params._id);
const review = {
by: req.user._id,
title: req.body.title,
message: req.body.message,
rating: parseFloat(req.body.rating),
};
if (req.body.imagesUri) {
//if there is images, we need to set it up
review.imagesUri = req.body.imagesUri;
}
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
return res.status(200).send(true);
}catch(err){
res.status(502).send({
error: "Error creating a review.",
});
}
});
I'm thinking of checking for seller's id and also check that no review is by current user, but it is not working.
const userID = req.user._id;
await Seller.updateOne(
{ _id: seller_id, reviews: { $elemMatch: { by: { $ne: { userID } } } } },
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
ANSWER:
I was able to solve the issue, in case other people have same issue. I did this:
await Seller.updateOne(
{
_id: seller_id,
"reviews.by": { $nin: [req.user.id] },
//knowing req.user._id is a mongoose.Types.ObjectId.
//You can also use [id1, id2, ...] to the array to check for other id's
},
{ $addToSet: { reviews: review } } //get the review that matches to the user_id
);
Here is the documentation for $nin operator: https://www.mongodb.com/docs/manual/reference/operator/query/nin/
You are pushing the review object inside an object.
Instead do this:
await Seller.updateOne(
{ _id: seller_id },
{ $addToSet: { reviews: review } }
);
i'm making a web site with MEAN Stack
I made a schema.
below is the schema.
const StoreSchema = mongoose.Schema({
storename: {
type: String
},
storenumber: {
type: Number
},
product: [{
pname: String,
pcode: Number,
price: String,
detail: String
}]
});
product is array.
when i make this schema, mongoose make it like this.
_id:5c3afaa96649e84364aab24b
storenumber:1
product:Array
__v:0
now there is a problem .
i want to insert values to pname, pcode, price, detail in product array.
but i can't push the values to the array.
below is the inserting value code.
module.exports.addProd = function(newProd, callback) {
Store.updateOne({
storenumber: number
}, {
$push: {
pname: newProd.pname
},
$push: {
pcode: newProd.pcode
},
$push: {
price: newProd.price
},
$push: {
detail: newProd.detail
}
}, callback);
}
but it doesn't work.
even i wrote product.
the values in newProd are all correct
what i want is like this
product[0] = {
pname:blah,
pcode:1,
price:5,
detail:blah blah
}
how can i insert values to array in mongoose?
please help me
You have to push the whole object together rather than each field like this -
Store.updateOne({
storenumber: number
}, { $push: { "product": newProduct } }, callback);
and also rather than using callback you should use async await -
module.exports.addProd = async newProd => {
let res = await Store.updateOne({
storenumber: number
}, { $push: { "product": newProduct } });
return res;
}
I have a problem with populate. I made a query to get User, Project and Topic information (Those are my 3 models). I need to show multiples dates in profile view. This is my code:
Project.js:
module.exports = {
attributes: {
name: {
type: "string"
},
topics: {
collection: "topic",
via: "projects"
},
members: {
collection: "user",
via: "projects"
},
content: {
collection: "content",
via: "projectData"
}
}
};
Topic.js:
module.exports = {
attributes: {
name: {
type: "string"
},
projects: {
collection: "project",
via: "topics"
}
}
};
in User.js:
show: function(req, res, next) {
User.findOne({id: req.session.User.id}).populateAll().exec(function prjFound(err, user){
if (err) return next(err);
if (!user) return next();
console.log(user);
res.view({
user: user
});
});
},
Console print this:
{ projects:
[ { name: 'Fran',
createdAt: '2017-06-19T21:33:17.152Z',
updatedAt: '2017-06-19T21:33:17.190Z',
id: 97 },
{ name: 'River Plate',
createdAt: '2017-06-19T21:36:38.757Z',
updatedAt: '2017-06-19T21:36:38.798Z',
id: 98 },
{ name: 'Guido',
createdAt: '2017-06-20T01:33:53.843Z',
updatedAt: '2017-06-20T01:33:53.926Z',
id: 99 } ],
group: [],
mat: 222222,
name: 'Francisco',
lastname: 'P',
email: 'fran#1.com.ar',
encryptedPassword: '$2a$10$nKp/eAOCDPw4BS.PvQCThe42wa2/8ZABw4JzA0no9GPVT4VjFl3ZO',
createdAt: '2017-06-19T21:32:10.535Z',
updatedAt: '2017-06-19T21:32:10.535Z',
id: '594842da6aeecd880ebab4e6'
}
I want to get all atributes of project model (Content, topic, and members), not only the name and id.
Anyone can explain Why my code is wrong?
Sails/Waterline populate/populateAll do 1 level of population. For 2 or deeper level you need to write code for it.
E.g. Gather ids of user's project and do populateAll on Project.find
Sailsjs doesn't currently support population within a populated field. Write a query in the returned response and append it to the field that you want to populate, send the response with your desired results.
Check this.
let result = await model.find(filter).populate("fieldName", {select:['attribute1','attribute1']})
i am trying to push inside a subarray using $push but got a Mongo error, and not able to get through this after considerable search on google, and findOneAndUpdate didn't worked out so i used find and update separately
{ [MongoError: can't append to array using string field name: to]
name: 'MongoError',
err: 'can\'t append to array using string field name: to',
code: 13048,
n: 0,
lastOp: { _bsontype: 'Timestamp', low_: 2, high_: 1418993115 },
Schema:
var NetworkSchema = new Schema({
UserID: {
type: Schema.Types.ObjectId,
ref: 'User'
},
NetworkList: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
NetworkRequest: [{
from: [{
type:Schema.Types.ObjectId,
ref: 'User'
}],
to: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
}]
});
Document:
{
"UserID" : ObjectId("549416c9cbe0e42c1adb42b5"),
"_id" : ObjectId("549416c9cbe0e42c1adb42b6"),
"NetworkRequest" : [
{
"from" : [],
"to" : []
}
],
"NetworkList" : [],
"__v" : 0
}
Controller:
exports.update = function(req,res) {
var network = req.network;
var query={'UserID':req.body.UserID};
var update = {$push:{'NetworkRequest.to': req.body.FriendID}};
Network.find(query,function(err){
if (err) {
console.log(err);
return err;
} else {
}
});
Network.update(query,update,{upsert:true},function(err,user){
console.log(user);
if (err) {
console.log(err);
return err;
} else {
console.log('User'+user);
}
});
};
Everything #cbass said in his answer is correct, but since you don't have a unique identifier in your NetworkRequest element to target, you need to do it by position:
var query = {'UserID': req.body.UserID};
var update = {$push:{'NetworkRequest.0.to': req.body.FriendID}};
Test.update(query, update, {upsert: true}, function(err, result) { ... });
'NetworkRequest.0.to' identifies the to field of the first element of the NetworkRequest array.
Your query var query={'UserID':req.body.UserID}; identifies the document you want to edit. Then you need another query to identify which object in the NetworkRequest array that the UserID should be pushed into. Something like below:
var query = {
'UserID':req.body.UserID,
'NetworkRequest._id': ObjectId(someNetworkRequestId)
};
Then use this update query containing $ which is the index of the object in the nested array(NetworkRequest)
var update = {
$push:{
'NetworkRequest.$.to': req.body.FriendID
}
};