MongoDB increment number in subdocument - javascript

My mongoDB document looks like this:
{
valOne: "one",
valTwo: "two",
valThree: {
threeOne: 0,
threeTwo: 0
}
}
i would like to increment either "threeOne" or "threeTwo" depending on the user request.
My code so far:
var whichFieldToUpdate = request.body.field; //can be either threeOne or threeTwo
var id = new BSON.ObjectID(request.body.id); //contains document id
db.collection('name').update({_id: id}, {$inc: { ?????? } },
function(err, result) {
});
???? should be something like this: {$inc: {valThree: {whichFieldToUpdate : 1 } }

var field = 'valThree.' + request.body.field;
var inc = {};
inc[field] = 1;
db.collection('name').update({_id: id}, {$inc: inc } },
function(err, result) {
});

Related

How to remove object from array and update all others objects position value?

Environment
Native Node driver
MongoDB 4.0.2
Desired Behaviour
Delete an object from an array of objects based on its id, and decrement other object's position value where applicable.
Specifically, I am trying to:
iterate over an array of objects in MongoDB and update a property in
each object
Perhaps it might involve using $inc and -1 , but i'm still unsure how to do the iteration.
What I've Tried
I have the logic working for a JavaScript scenario (works in browser console):
function remove_and_update_position(id) {
// faux array in collection document
var statements = [{
position: 0, // < i want to update 'position' of all objects after an object is removed
id: "a"
},
{
position: 1,
id: "b"
},
{
position: 2,
id: "c"
},
{
position: 3,
id: "d"
},
{
position: 4,
id: "e"
}
];
// delete an object from the 'statements' array by its id
statements.splice(statements.indexOf(statements.find(item => item.id == id)), 1);
// iterate over the modified array
for (var i = 0; i < statements.length; i++) {
// for all objects in the modified array,
// with position numbers greater than the
// position removed ( minus 1),
// decrement their position numbers
if (i > position - 1) {
var new_position = statements[i].position - 1;
statements[i].position = new_position;
}
}
// verify adjustments were correct
console.log(statements);
}
// call the function
remove_and_update_position("c");
I just need to translate it into a MongoDB/Node scenario - so far i just have the object removal part:
const remove_and_update_position = (req, res) => {
var request_body = req.body;
var topic_id = request_body.topic_id;
var o_id = new ObjectID(topic_id);
var collection = mongo_client.db("topics").collection("topics");
var statement_id = request_body.statement_id;
var position = Number(request_body.position);
// query for the relevant document *and* target nested statement by id
var filter = { _id: o_id, "statements.id": statement_id };
// remove from statements array
var update = { $pull: { "statements": { id: statement_id } } };
// perform the update
collection.updateOne(filter, update, function(err, doc) {
if (err) {
res.send(err);
} else {
res.json({ response_type: "success" });
}
});
}
Updated per answer here.
const remove_and_update_position = (req, res) => {
var request_body = req.body;
var topic_id = request_body.topic_id;
var o_id = new ObjectID(topic_id);
var collection = mongo_client.db("topics").collection("topics");
var statement_id = request_body.statement_id;
var position = Number(request_body.position);
// query for the relevant document *and* target nested statement by id
var filter = { _id: o_id, "statements.id": statement_id };
// remove from statements array
var update = { $pull: { "statements": { id: statement_id } } };
// perform the update
collection.updateOne(filter, update, function(err, doc) {
if (err) {
res.send(err);
} else {
// BEGIN decrement others objects position
var position_indicator = position - 1;
console.log("deleted object position: " + position);
console.log("will decrement objects with position greater than: " + position_indicator);
var new_filter = { _id: o_id, "statements.position": { $gt: position_indicator } };
var new_update = { $inc: { "statements.$[elem].position": -1 } };
var array_filters = { "arrayFilters": [{ "elem.position": { $gt: position_indicator } }], "multi": true };
collection.updateOne(new_filter, new_update, array_filters, function(err, doc) {
if (err) {
res.send(err);
} else {
res.json({ response_type: "success" });
}
});
// END decrement others objects position
}
});
}

Mongodb 3.2 Node js $push is not working

I want to push new friend JSON object to an array : friend
I have try $push directly via mongodb but it is not working on NodeJS
Anyone know want is going wrong?
I have try UsersInfo.friend -> friend UsersInfo.friend.user_id -> user_id
and no err return.
The Structure of Player :
socket.on('follow_friend',function(data){
var me = current_players_socket.indexOf(socket);
console.log("PLayer id:"+data.user_id+" Player name:"+current_players[me].name+ "'s friend following request received");
console.log("Target:"+data.fdid);
MongoClient.connect(url,function(err,db){
assert.equal(null,err);
follow_friend_to_db(data,db,function(){
db.close();
});
});
});
var follow_friend_to_db = function(data,db,callback){
var me = current_players_socket.indexOf(socket);
console.log("Following "+data.fdid+" to "+data.user_id);
current_players[me].friend.push(data.fdid);
db.collection('Users').update(
{"UsersInfo.user_id":data.user_id},
{
$push: {
"UsersInfo.friend" :
{$each:
{
"UsersInfo.friend.user_id" : data.fdid,
"UsersInfo.friend.add_date" : new Date("2014-10-01T00:00:00Z")
}
}
}
},function(err,results){
for(i= 0;i<current_players[me].friend.length;i++){
console.log(current_players[me].friend[i]);
}
socket.emit('friend_followed',{fdid:data.fdid});
callback();
});
};
here is the $set function which is work
socket.on('gold_add',function(data){
console.log("Player id:"+data.user_id+" add gold request recevied");
var i = current_players_socket.indexOf(socket);
MongoClient.connect(url,function(err,db){
assert.equal(null,err);
update_user_gold_to_db(data,db,function(){
db.close();
});
});
console.log("Player:"+current_players[i].get_name()+"'s gold:"+current_players[i].gold);
});
var update_user_gold_to_db = function(data,db,callback){
var i = current_players_socket.indexOf(socket);
console.log("Player id:"+data.user_id+" add gold amount:"+data.amount);
var t = data.amount + current_players[i].get_gold();
console.log(t);
db.collection('Users').update(
{"UsersInfo.user_id":data.user_id},
{
$set:{"UsersInfo.gold":t}
}, function(err,results){
//console.log(results);
current_players[i].gold+=data.amount;
socket.emit('gold_add_success',{gold:current_players[i].gold});
callback();
});
};
Why are you using $each. We use $each when we have an array to push like mentioned in mongodb documentation:
db.inventory.update(
{ _id: 2 },
{ $addToSet: { tags: { $each: [ "camera", "electronics", "accessories" ] } } }
)
Try removing $each:
var follow_friend_to_db = function(data,db,callback){
var me = current_players_socket.indexOf(socket);
console.log("Following "+data.fdid+" to "+data.user_id);
current_players[me].friend.push(data.fdid);
db.collection('Users').update(
{"UsersInfo.user_id":data.user_id},
{
$push: {
"UsersInfo.friend" :
{
"user_id" : data.fdid,
"add_date" : new Date("2014-10-01T00:00:00Z")
}
}
},function(err,results){
for(i= 0;i<current_players[me].friend.length;i++){
console.log(current_players[me].friend[i]);
}
socket.emit('friend_followed',{fdid:data.fdid});
callback();
});
};

MongoDB $pull not working

I am building a Meteor app and I have Contests/Entries collections. When someone enters the contest, their user_id is pushed into the Contest.entered_users array with $addToSet. Here is the code:
entryInsert: function(entryAttributes) {
check(Meteor.userId(), String);
check(entryAttributes, {
contest_id: String
});
var user = Meteor.user();
var entry = _.extend(entryAttributes, {
user_id: user._id,
user_name: user.profile.name,
submitted: new Date(),
submitted_day: moment().format('MMM D')
});
var currentContest = Contests.findOne(entryAttributes.contest_id);
// Check to make sure that the person has not already entered the giveaway
if (currentContest.entered_users.indexOf(entry.user_id) !== -1) {
throw new Meteor.Error('invalid', "You have already entered the giveaway");
} else {
Contests.update(
currentContest._id,
{
$addToSet: {entered_users: entry.user_id},
$inc: {entries: 1}}
);
}
// Create entry in order to get the entry id
var entryId = Entries.insert(entry, function(err) {
if (err) {
alert(err.reason);
}
});
return {
_id: entryId
}
}
I want to remove a persons user_id from the Contest.entered_users array when an entry is removed. I am trying to use $pull but it doesn't appear to be working... When I remove an entry, the entry.user_id is still in the contest.entered_users array. Here is the relevant code:
'click .entry-delete': function(e, tmpl) {
e.preventDefault();
var currentEntry = this;
var currentEntryId = this._id;
var contestId = Contests.findOne(currentEntry.contest_id);
// Update the contest by removing the entry's useer_id from entered_users
Meteor.call('contestRemoveEntry', contestId, currentEntry, function(error) {
if (error) {
alert(error.reason);
}
});
Meteor.call('entryRemove', currentEntryId, function(error) {
if(error) {
alert(error.reason);
}
});
}
Here is the contestRemoveEntry method:
contestRemoveEntry: function(contestId, currentEntry) {
Contests.update({ _id: contestId }, { $pull: { entered_users: currentEntry.user_id } } );
}
Any ideas as to why this is not working? I've tried other SO solutions but nothing seems to be working.
It appears that this is the correct way to make $pull work:
Contests.update(contestId, { $pull: { entered_users: currentEntry.user_id } } );

How can I assign a operator to variable

I am working on like and unlike section of an image. When a user likes an image, it pushes the userID to an array in mongodb. When a user unlikes an image, it removes the userID from the array. I am trying to do it using $addToSet and $pull.
Question: How can I do it in a single block instead of writing two separate bolcks for these two? Currently I am using a variable opr but it is not working. How can I make it work?
if(likeAction == "like"){
var opr = '$addToSet'
}
else if(likeAction == "unlike"){
var opr = '$pull'
}
Like.update(
{ imageID: imageID },
{ opr : { userIDs: userID } },
function(err){
if(!err){
Like.findOne({imageID : imageID}, function(err,like){
image.likeCount = like.userIDs.length
image.save(function(err){
if(!err){
return res.send({
status: {
error: 0,
message: "Successful"
}
})
}
})
})
}
}
);
I think this should work to combine both queries and solve the opr issue:
var update = {};
if (likeAction === 'like'){
update.$addToSet = { userIDs: userID };
update.$inc = { likeCount : 1 };
} else if (likeAction === 'unlike'){
update.$pull = { userIDs: userID };
update.$inc = { likeCount : -1 };
}
Like.update({ imageID: imageID }, update, ...);
You can do something like that :
var opr, updateCommande = {};
if(likeAction == "like"){
opr = '$addToSet'
}
else if(likeAction == "unlike"){
opr = '$pull'
}
updateCommande[opr]= { userIDs: userID }
Like.update(
{ imageID: imageID },
updateCommande,
function(err){
if(!err){
Like.findOne({imageID : imageID}, function(err,like){
image.likeCount = like.userIDs.length
image.save(function(err){
if(!err){
return res.send({
status: {
error: 0,
message: "Successful"
}
})
}
})
})
}
}
);

double upsert in Node.js and mongoose

var urlSchema = new Schema ( {
url : String,
visitor : [{name: String,date: Date}],
counter : Number
});
var url = mongoose.model('url',urlSchema);
var domain = blah blah;
var conditions = { url: domain };
var update = { $inc : {counter:1},
$push : { visitor: [{
name: data.username,
date: new Date()
}]
}
};
var options = {upsert: true};
url.update(conditions,update,options,function(err){
if(err){
console.log(err);
}else{
console.log('A domain successfully added to DB');
}
Currently I am using the schema and code above to make an access counter for each page.
It upserts url documents which is counting total access and holding access user info.
so far so good It's working properly.
now I want to add "counter : Number" in visitor array and when the event occurs, if the user is already in visitor array,I want to update that instead of pushing new one.
But I have no idea how to make the 'double upsert' command.
is there anyway to do that?
It may be a bit difficult even impossible perform a query that satisfy your condition. I have reproduced your scenario with following query
url.findOne({'url': 'search.php', 'visitor.name': "JohnDoe"},
function (err, visitor) {
if (visitor) {
url.update(
{
'url': 'search.php', 'visitor.name': "JohnDoe"
},
{
$inc: {'visitor.$.counter': 1, 'counter': 1}
},
function(err, result) {
if (err) {
console.log("Error occured")
} else {
console.log("Success");
}
});
} else {
var conditions = { url: 'search.php' };
var update = { $inc : {counter:1},
$push : { visitor: [{
name: data.username,
date: new Date(),
counter: 0
}]
}
};
var options = {upsert: true};
url.update(conditions,update,options,function(err){
if(err){
console.log(err);
}else{
console.log('A domain successfully added to DB');
}
});
}
}
);
Simple, insert if not exists with counter = 0, update if exists with incrementing counter by 1

Categories