I'm creating like a fast food online service for ordering food(I'm just practicing). The thing that bothers me now is I've successfully created an order when a customer picks a meal. After another meal is picked order is updated by order.id and another meal is added to the same order using $push. The problem that I have is when the order is created for the first time the first meal is overwritten by findbyIdAndUpdate when adding a second meal. After adding the third meal I get the data from the second meal and the third meal but the first one doesn't exist.I'm using MongoDB. Can someone tell me how to fix this?
Sorry for the log and probably bad code in advance!
Order Schema is here:
var mongoose = require("mongoose");
var orderSchema = new mongoose.Schema({
order: [{
meal: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Menu'
},
quantity: String
}],
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
}
});
module.exports = mongoose.model("Order", orderSchema);
Express route is here:
app.post("/menu/order", middlewareObject.isLoggedin, function (req, res) {
let order = {
user: req.user._id,
meal: req.body.foodID,
quantity: req.body.quantity,
}
if (app.locals.orderID !== undefined) {
order._id = app.locals.orderID;
console.log(order._id);
Order.findByIdAndUpdate(order._id, {
$push: {
order: {
meal: order.meal,
quantity: order.quantity,
}
}
}, function (err, updated) {
if (err) {
console.log(err);
} else {
console.log("UPDATED!")
res.redirect("back");
}
})
} else {
Order.create(order, function (err, newOrder) {
if (err) {
console.log(err)
} else {
console.log("CREATED!")
app.locals.orderID = newOrder._id;
res.redirect("back");
}
})
}
})
Create() data:
{ "_id" : ObjectId("5eb6b78bd336a7324c63621c"), "user" : ObjectId("5eb6b788d336a7324c63621a"), "order" : [ ], "__v" : 0 }
FindByUpdateandID data:
{ "_id" : ObjectId("5eb6ba776d8684455cb8ece5"), "user" : ObjectId("5eb6b788d336a7324c63621a"), "order" : [ { "_id" : ObjectId("5eb6ba7d6d8684455cb8ece6"), "meal" : ObjectId("5eb6ba6a6d8684455cb8ecd7"), "quantity" : "5" }, { "_id" : ObjectId("5eb6ba7f6d8684455cb8ece7"), "meal" : ObjectId("5eb6ba6a6d8684455cb8ecd8"), "quantity" : "2" } ], "__v" : 0 }
Related
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
I have this object:
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877a8"),
"websites" : [
"",
"",
""
],
"keys" : [
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877af"),
"name" : "Google",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ae"),
"name" : "Built With",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ad"),
"name" : "Check Host",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ac"),
"name" : "Alexa",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877ab"),
"name" : "Facebook",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877aa"),
"name" : "Instagram",
"value" : ""
},
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877a9"),
"name" : "Moz",
"value" : ""
}
],
"username" : "admin#admin",
"isPremium" : false,
"accType" : "admin",
"hash" : "very long hash",
"salt" : "long salt",
}
Now. Using NodeExpress and Mongoose I need to be able to edit the value field inside of every object inside the keys array.
My GET operation is this:
// GET: /websites/:_id - show edit form
router.get('/keys/edit/:_id', isAdmin, function(req, res, next) {
// console.log('tada');
// console.log(req.params._id);
Account.findOne({ _id: req.user._id }, function(err, user) {
var selectedKey = findById(user.keys, req.params._id);
// var keys = user.keys.findOne(req.params._id);
console.log(selectedKey);
res.render('admin/edit', {
title: 'Edit websites',
user: req.user,
value: selectedKey.value,
});
});
});
How the app works is: The admin logs in. He sees all users and chooses which one he wants to modify, then admin sees all keys. I will attach screenshots to explain it more clearly.
Now. I think I know what I need to do, but I have no clue how to translate it to code.
I think I need to: Find the index of the array element, like in the GET request, update the value with the posted value. I think I need to find the index in the array.
But as I said I have no clue how to do it.
My POST looks like this right now:
// POST: /keys/edit/_id - save updates
router.post('/keys/edit/:_id', isAdmin, function(req, res, next) {
var p = req.params;
var b = req.body;
Account.findOne({ _id: req.user._id }, function(err, user) {
var selectedKey = findById(user.keys, req.params._id);
// console.log('Key value: ' + req.body.keyValue);
// console.log('Selected key: ' + selectedKey);
console.log('id:' + req.params._id);
if (err) {
console.log(err);
} else {
console.log(user);
user.keys.set(req.params._id, req.body.keyValue);
user.save(err => {
if (err) {
console.log(err);
} else {
console.log('all good');
}
res.redirect('/admin');
});
}
});
EDIT: So I was working on it for a while now and I figured out this. I am using the correct user, I am grabbing the keys array inside, but I don't know how to find the id of the object in the array, which (object) I need to edit.
There is a lot of nesting and this might cause some issues.
EDIT 2: I'm attacking my account model. Forgot about it earlier. Sorry.
var mongoose = require('mongoose');
var website = require('./website');
var plm = require('passport-local-mongoose');
var accountSchema = new mongoose.Schema({
isPremium: Boolean,
accType: String,
websites: [],
keys: [
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
{ name: String, value: String },
],
});
accountSchema.plugin(plm);
module.exports = mongoose.model('Account', accountSchema);
You can perform the update atomically using $positional operator.
You include the field (_id) from the keys to locate the index of element and replace the placeholder($) with the found index from query part in the update part to set the value in keys.
router.post('/keys/edit/:_id', isAdmin, function(req, res, next) {
var p = req.params;
var b = req.body;
Account.findOneAndUpdate(
{_id: req.user._id,'keys._id':req.params._id },
{$set:{'keys.$.value':req.body.keyValue}},
{new: true},
function(err, account) {}
);
The question isn't entirely clear to me what you're looking to do, but what I can infer is that you want to do the following:
You have some object that has an Array of keys that has the following shape:
{
"_id" : ObjectId("5a8d83d5d5048f1c9ae877af"),
"name" : "Google",
"value" : ""
}
Judging from your sample object, I'm inferring the schema is defined something like:
const mongoose = require('mongoose')
const definition = {
websites: [String],
keys: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Key'
}]
}
const accountSchema = new mongoose.Schema(definition)
module.exports = mongoose.model('Account', topicSchema)
By the looks of the route, you want to update/edit that object at the given index: keys[i]. If this is the case, then there is no need to manually traverse the array, update the model directly:
const Key = require('./path/to/models/Key')
router.post('/keys/edit/:id', async (req, res) => {
const { keyValue } = req.body
const conditions = { _id: req.params.id }
await Key.findOneAndUpdate({ id }, { value: keyValue }).exec()
res.status(201).json()
})
The item in the array will be updated when you query the parent model.
I have a list of users in Mongodb that needs to searched according to some filters as shown in below picture:Only gender is mandatory and users may or may not have other details
User.find({
"gender": userEntry.gender,
"dob": { $gte: convert.getDobFromAge(userEntry.ageHigherLimit), $lte: convert.getDobFromAge(userEntry.ageLowerLimit), $exist: false },
"details.chest": { $gte: userEntry.chestLowerLimit, $lte: userEntry.chestHigherLimit, $exist: false },
"details.waist": { $gte: userEntry.waistLowerLimit, $lte: userEntry.waistHigherLimit, $exist: false },
"details.height": { $gte: userEntry.heightLowerLimit, $lte: userEntry.heightHigherLimit, $exist: false },
"details.weight": { $gte: userEntry.weightLowerLimit, $lte: userEntry.weightHigherLimit, $exist: false }
}, function (err, users) {
return res.render('client/search.html', { users: users });
});
Above is the mongoose query to search and the userEntry looks like this
userEntry={
"gender":2,
"ageLowerLimit":28,"ageHigherLimit":40,
"chestLowerLimit":"","chestHigherLimit":"",
"heightLowerLimit":"","heightHigherLimit":"",
"waistLowerLimit":"","waistHigherLimit":"",
"weightLowerLimit":"","weightHigherLimit":"",
"state":"","city":"",
"country":"","skin_color":"",
"profession_type":"","experience":"",
"hair_type":""
}
My problem is the find function , it should search all records with gender as '2' and age>=28 and age=<40 (from the above query gives me empty array even though one record satisfies it),giving all the results that satisfy the above conditions irrespective of whether other fields are empty or doesnot exist.
Any help would be appreciated.
As rightly suggested changed the query to but still 0 records fetched
var query = {
details: {}
};
if (userEntry.gender) {
query.gender = userEntry.gender;
}
if(userEntry.ageLowerLimit && userEntry.ageHigherLimit ) {
query.dbo = { $gte: convert.getDobFromAge(userEntry.ageHigherLimit), $lte: convert.getDobFromAge(userEntry.ageLowerLimit)};
}
console.log(query);
User.find(query, function (err, users) {
if(!err) {
console.log(users);
return res.render('client/search.html', { users: users });
}
console.log(err);
});
});
one of the records trying to fetch
{ "_id" : ObjectId("59c3f47e6388613b94556b78"), "name" : "tanzeel", "email" : "im_tanzeel#yahoo.co.in", "password" : "$2a$10$kvachEZL0vEPPJiS7bIAMeGMXiZ.MRaZmrBECXB207jme1I4JEn6i", "created_at" : ISODate("2017-09-21T17:18:54.822Z"), "role" : 1, "following" : [ ], "dp" : "/dp/default.jpg", "gender" : 2, "__v" : 0, "dob" : ISODate("1994-11-29T00:00:00Z"), "details" : { "height" : 160, "weight" : 65, "profession_type" : "Actor", "skin_color" : "Tan", "eye_color" : "Black", "waist" : 32, "chest" : 35 } }
You got correct value from MongoDB but not your expected value because your query build not correct. Also $exist not valid operator should be $exists and you compare with empty string for some fields like details.waist = "" because userEntry.weightLowerLimit is empty. However you should build query correctly to get expected result.
can try like this...
var query = {
details: {}
};
if (userEntry.gender) {
query.gender = userEntry.gender;
}
if(userEntry.ageLowerLimit && userEntry.ageHigherLimit ) {
query.dbo = { $gte: convert.getDobFromAge(userEntry.ageHigherLimit), $lte: convert.getDobFromAge(userEntry.ageLowerLimit)};
}
if(userEntry.chestLowerLimit && userEntry.chestHigherLimit) {
query['details.chest'] = { $gte: userEntry.chestLowerLimit, $lte: userEntry.chestHigherLimit };
}
//... for others conditions
User.find(query, function (err, users) {
if(!err) {
return res.render('client/search.html', { users: users });
}
console.log(err);
});
I'm trying to attach objects from another collection to the Meteor.user collection by a click event. I have a collection with a list of items called "categories" each category has a name field, its that name i want to push into the meteor.user.
Its supposed to work in a way that the user can push as many of these names as they want however its only accepting one entry, and when i click on another name, the new name replaces the old one, instead of being an array. how can i make it so that it can allow many entries?
client/users.js
Template.CategoriesMain.events({
'click .toggle-category': function(e){
//var id = $(e.target).attr('posts.name');
var id = $(e.target).parent().find("a").text();
console.log(id);
e.preventDefault();
Meteor.call('addingCategory', id, function(error, user){ console.log(id)});
}
});
server/users.js
Meteor.methods({
addingCategory: function(name) {
var cats = [{}];
cats.push(name);
console.log(Meteor.userId());
Meteor.users.update({
_id: Meteor.userId()
}, {
$set: {
name: name
}
});
}
});
and this is the user from db.user.find() as you can see with
"name" : "a-reece"
its clearly pushing the name but i cannot add more, i can only replace
{ "_id" : "4CHcZjSD4hCrqweGA", "createdAt" :
ISODate("2016-07-13T21:38:59.505Z"), "services" : { "password" : {
"bcrypt" :
"$2a$10$lKZtrYSMD4EGPj6eamgFDuPZ41Jw52DgivBly3lUYWbGDtfZBg1X." },
"resume" : { "loginTokens" : [ { "when" :
ISODate("2016-07-13T21:38:59.719Z"), "hashedToken" :
"BsqTGedB2FkmSPO3+5I31rOM2+MPtF97Zc9tRQ4pf8Y=" } ] } }, "emails" : [ {
"address" : "mun#les.com", "verified" : false } ], "roles" : [
"discoveror", "yes" ], "isAdmin" : true, "name" : "a-reece" }
how can i add more names instead of replacing?
EDIT
Meteor.methods({
addingCategory: function(name) {
//Meteor.users.update(Meteor.userId(), { $addToSet: { name: name} } );
console.log(Meteor.userId());
//Meteor.users.update(Meteor.userId(), { $set: { "categories": cats }} );
Meteor.users.update({
_id: Meteor.userId()
},
{
$unset: {
name: name
}
},
{
$addToSet: {
name: name
}
});
}
});
ANSWER
Template.CategoriesMain.events({
'click .toggle-category': function(e){
//var id = $(e.target).attr('posts.name');
var ob = $(e.target).parent().find("a").text();
var id = $.makeArray( ob );
console.log(id);
e.preventDefault();
Meteor.call('addingCategory', id, function(error, user){ console.log(id)});
}
});
You're currently doing:
Meteor.users.update({ _id: Meteor.userId() }, { $set: { name: name } });
You have two choices: $push or $addToSet:
Meteor.users.update({ _id: Meteor.userId() }, { $push: { name: name } });
or
Meteor.users.update({ _id: Meteor.userId() }, { $addToSet: { name: name } });
The former pushes onto an array, allowing duplicates, the latter avoids dupes.
You don't need:
var cats = [{}];
cats.push(name);
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
}
};