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.
Related
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 }
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 update a specific field in a concrete JSON object inside an JSON array with mongoose.
My MongoDB contains:
db.users.find({username:"xxx"})
{ "_id" : ObjectId("56cb877e73da764818ec5ded"),..., "githubtoken" : [ { "token" : "9e37axxx", "username" : "xxx", "_id" : ObjectId("572a7cfafe95dec51d9cbf2d") }, { "token" : "4529yyy", "username" : "yyy", "_id" : ObjectId("572a7d3cfe95dec51d9cbf2e") } ] }
And I want to get the JSON object that matches with "username" : "yyy"and "user._id" = "56cb877e73da764818ec5ded" and change its token to "token" : "4529zzz" with mongoose, like this:
{ "_id" : ObjectId("56cb877e73da764818ec5ded"),..., "githubtoken" : [ { "token" : "9e37axxx", "username" : "xxx", "_id" : ObjectId("572a7cfafe95dec51d9cbf2d") }, { "token" : "4529zzz", "username" : "yyy", "_id" : ObjectId("572a7d3cfe95dec51d9cbf2e") } ] }
The schema of db is:
var userSchema = new Schema({
username : { type: String, required: true },
...
githubtoken: [ { username: {type: String, required: false },
token: {type: String, required: false} }]
});
And update method:
userSchema.statics.updateuser = function updateuser (query, update, options) {
var promise = new Hope.Promise();
this.findAndUpdate(query, update, options,function(error, user) {
if (error) {
return promise.done(error, null);
}else {
return promise.done(null, user);
}
});
return promise;
};
And in my service with express.js:
query = {$and: [{_id: userid}, {githubtoken: {username:username, token:oldToken}} ]};
update = {$set : {githubtoken:{username:username, token:token}}};
options = { new: true};
User.updateuser(query,update,options).then(function (error,user){
if(error){
return promise.done(error,null);
}else{
}});
But it doesn't work, because remove all array of githubtokens and push only the new githubtoken, like this:
{ "_id" : ObjectId("56cb877e73da764818ec5ded"),..., "githubtoken" : { "token" : "4529zzz", "username" : "yyy", "_id" : ObjectId("572a7d3cfe95dec51d9cbf2e") } }
Any idea?
Thank you very much :D
You can use the $ - positional operator for the update:
db.collection('users').update({
"_id": ObjectId("56cb877e73da764818ec5ded"),
"githubtoken.username": "xxx"
}, {
$set: {
"githubtoken.$.username": username,
"githubtoken.$.token": token
}
});
And this should do the trick. More details on usage of $ - positional operator https://docs.mongodb.org/manual/reference/operator/update/positional/
Try changing the query like this:
var query = {
{
_id: new ObjectId(userid),
'githubtoken.username': username,
'githubtoken.token': oldToken
}
};
And update like this:
var update = {
$set: {
'githubtoken.$.username': username,
'githubtoken.$.token': token
}
};
I found the solution in this page:
query = {"githubtoken" :{$elemMatch: {"username": username}}};
update = {$set : {
"githubtoken.$.username": username,
"githubtoken.$.token": token
}};
Thanks for the help :D
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
}
};
I have a Mongo DB structure which looks something like this :
db.users.find().pretty();
{
"_id" : ObjectId("52b42b148ffa91f7ebbe8ebc"),
"username" : "test",
"password" : "test",
"party" : [
"4988",
"5037"
],
"something" : [
"3571"
],
"ludilo" : [],
}
Im using express js in my app and this module for connecting to Mongo https://npmjs.org/package/mongodb ,
How can I insert one entry into "something" array for user with id that I get from session.
I tried something like this , but with no success
var collection = db.collection('users');
collection.find({'_id':new ObjectID(req.user.id)}).toArray(function(err, items) {
console.dir(items);
}).insert({"something":"1234"});
You can $push a value to an array with
db.users.update(
{ _id: ObjectId( "52b42b148ffa91f7ebbe8ebc" ) },
{ $push: { something: "1234" } }
)
or if you do not want any duplicates in your array you can use $addToSet
db.users.update(
{ _id: ObjectId( "52b42b148ffa91f7ebbe8ebc" ) },
{ $addToSet: { something: "1234" } }
)
You can try this code:
collection.find({_id: new ObjectID(req.user.id)}).toArray(function(err, items) {
var doc = items[0];
doc.something.push('1234');
collection.update({_id: doc._id}, {$set: {something: doc.something}}, {safe: true}, function() {
//your next actions
});
});
I run this code on my local machine and it seems to work fine