Mongoose - Increment Field On Subdoc if exists, else create new - javascript

What I am trying to do.
I have a userSchema that contains a list of operationCountSchemaobjects. What I am trying to do, is to create a static method that updates the count field on one of these operation count subdocuments if it exists (identified by a month_id) field. If an operationCountSchema document does not exist for the current month, it should create a new document. Is there a way to achieve this behaviour in mongoose? I have tried using upsert to no avail. How would one do this? Thanks.
CODE
var operationCountSchema = mongoose.Schema({
month_id: String,
count: { type: Number, default: 0 }
}, {_id : false});
var userSchema = mongoose.Schema({
username : { type: String, unique: true, required: true },
email: { type: String, unique: true, required: true },
password: String,
operation_counts: [operationCountSchema]
});
userSchema.statics.incrementOperationCount = function(userID, callback) {
var currDate = new Date();
var dateIdentifier = currDate.getFullYear() + "-" + currDate.getMonth();
//NEED TO INCREMENT OPERATION COUNT IF ONE FOR MONTH EXISTS,
//ELSE IF IT DOES NOT EXIST, CREATE A NEW ONE.
}
Also, any suggestions on alternative ways in which this functionality can be achieved are welcome.

I think you want findOneAndUpdate() with upsert : true:
operationCountSchema.findOneAndUpdate({
month_id : dateIdentifier,
}, {
$inc : { count : 1 }
}, {
upsert : true
}, callback);
(untested)

You can do it in two steps, here is a example in mongo shell:
mongos> db.collection.findOne()
{
"username" : "mark",
"email" : "admin#example.com",
"password" : "balalalala",
"operation_counts" : [
{
"month_id" : "2016-05",
"count" : 6
}
]
}
First, make sure the subdoc exists, if not just create one use $addToSet.
mongos> db.collection.update({username:"mark", "operation_counts.month_id": {$ne:"2016-05"}}, {$addToSet: {"operation_counts":{month_id: "2016-05", count:0}}})
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
// only update when the subdoc of specified month not exists
mongos> db.collection.update({username:"mark", "operation_counts.month_id": {$ne:"2016-06"}}, {$addToSet: {"operation_counts":{month_id: "2016-06", count:0}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.collection.findOne()
{
"_id" : ObjectId("575636c21e9b27fe715df654"),
"username" : "mark",
"email" : "admin#example.com",
"password" : "balalalala",
"operation_counts" : [
{
"month_id" : "2016-05",
"count" : 6
},
{
"month_id" : "2016-06",
"count" : 0
}
]
}
Then, increment the count field.
mongos> db.collection.update({username:"mark", "operation_counts.month_id": "2016-06"}, {$inc:{ "operation_counts.$.count":1 }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.collection.findOne()
{
"_id" : ObjectId("575636c21e9b27fe715df654"),
"username" : "mark",
"email" : "admin#example.com",
"password" : "balalalala",
"operation_counts" : [
{
"month_id" : "2016-05",
"count" : 6
},
{
"month_id" : "2016-06",
"count" : 1
}
]
}

So you can have a mongoose.find() or mongoose.findOne() and check if the sub-document exists. If it doesn't we can create the a new object and if it does, we can increment and save.
Here I'm using mongoose.findOne(). Refer to the docs here.
userSchema.statics.incrementOperationCount = function(userID, callback) {
var currDate = new Date();
var dateIdentifier = currDate.getFullYear() + "-" + currDate.getMonth();
//NEED TO INCREMENT OPERATION COUNT IF ONE FOR MONTH EXISTS,
//ELSE IF IT DOES NOT EXIST, CREATE A NEW ONE.
operationCountSchema.findOne({'month_id': dateIdentifier}, function(err, subDoc) {
// If there is an error in finding the document, catch them
if(err) {
// Handle errors
return err;
}
// If you find a document, increment the `count` and save
if(subDoc) {
subDoc.count += 1;
subDoc.save(function(err2) {
if(err2) {
// Handle errors
return err2;
} else {
return "Success";
}
});
}
// If no document is found, create a new one
else {
// Populate the values to create the object
var data = {
"month_id": dateIdentifier,
"count": 0
};
operationCountSchema.create(data, function(err3, subDoc) {
if(err3) {
// Handle errors
return err3;
}
// Else return success
return "Success";
});
}
});
};
Let me know if I got your question worng or didn't address something.

Related

mange is delete(soft delete) in mongodb by passing it in filter checking the existing criteria

I have a situation where I have to check name of the company and code of the company and if both match with existing than it should say exists, or if one of them is matched in existing db then also it should be saying it exists.
How would I use in mongo?
So if the data receive isDeleted true than I also want to add in if section that checks if not adding than goes for checking id and update, than I also want to pass isDelete, so that if any data received which is previously deleted so that it can set to false again.
how would I handle this delete scenario?
{
"userId":"tt838984984s",
"company":{
"addressee" : {
"addressee" : "Ms.1",
"title_name" : "",
"salutation" : "Drks:",
"comments" : "",
"email" : "opus7ent#example.com",
"phone" : "123456666",
"fax" : "",
"extention_group" : "",
"ext" : ""
},
"abbreviation" : "",
"care_of" : "",
"address_1" : "HELLO2",
"address_2" : "",
"address_3" : "",
"state" : "CA",
"zip" : "90024",
"is_deleted" : true,
"company_code" : "ABACAB",
"parent_company" : null,
"name" : "Abacab",
"createdBy" : "Data lolz",
"modifiedBy" : "Data",
"createdDate" : "2019-08-22T19:10:50.000+0000",
"modifiedDate" : "2019-08-22T19:10:50.000+0000",
"company_id_legacy" :1246,
"__v" : 0,
"is_registered_for" : false,
},
}
is_deleted == false
if(!isAdd) {
filter["_id"] ={ "$ne" : id};
}
let filter = {
name: { $regex: new RegExp(`^${company.name}$`, 'i') },
company_code: { $regex: new RegExp(`^${company.company_code}$`, 'i') }
}
cModel.find(filter, function (err, docs) {
if (docs.length) {
result.error = "Name already exists: " + company.name;
console.log("Name already exists", null);
let resp = api_respose.getSuccessResponse(process.env.WEB_URI, result.error);
resolve(resp);
}
else{
///saving here
}
Now suppose I pass that JSON, and if there's is_deleted = false (this is Json while adding new entry in db)
And now if there's some old entry with name or company_code exists in db with is_delete =true then it throws error that name already exits
Now my question is how to resolve this scenario? Like I want to overwrite that file with new entry or is there any other way of doing this?

Mongoose - go through object

Using mongoose on node.js I'm trying to find all games where player game.players.id equals the id I passed.
Schema:
var Game = mongoose.Schema({
id: String,
date: { type: Date, default: Date.now },
game: Object,
isOnline: Boolean
});
I'm not sure what is wrong in this function but it returns empty array:
var specificGameStatistics = function (user, game) {
var deferred = q.defer()
Game.find({ "game.players.id" : user, "game.rules.gameType": game.gameType, "game.rules.quatro": game.quatro}, function(err, data) {
deferred.resolve(data);
});
return deferred.promise;
}
////////////////////USAGE///////////////
var testGame = {rules: {gameType : 1, quatro : null}}
UsersCtrl.specificGameStatistics(data.id, testGame).then(function(userData) {
console.log(userData);
});
And here is the example of the game already saved in database:
{
"isOnline" : true,
"game" : {
"numberOfPlayers" : NumberInt("1"),
"players" : [
{
"id" : "58a2c0ecd8ba9f8602836870",
"name" : "PlayerName",
"type" : NumberInt("1"),
"avgStatistic" : "30.00",
"numbersHit" : NumberInt("1"),
"totalScore" : NumberInt("60"),
..............................
}
], //there is more players here
"rules" : {
"gameType" : NumberInt("1"),
"quatro" : null,
"rounds" : NumberInt("1"),
} // there is more in JSON object
...............................
"_id" : ObjectId("58aed4aeea20ecdf0c426838"),
"date" : ISODate("2017-02-23T13:25:18.284+01:00"),
"__v" : NumberInt("0")
}
I have tested the player ID to be equal and it is but still it returns empty array. Test code:
///////////TEST//////////////
console.log(data.id, "58a2c0ecd8ba9f8602836870");
if (data.id === "58a2c0ecd8ba9f8602836870") {console.log("this is true");}
var testGame = {rules: {gameType : 1, quatro : null}}
UsersCtrl.specificGameStatistics(data.id, testGame).then(function(userData) {
console.log(userData);
});
//////////TEST///////////////
and it returns:
58a2c0ecd8ba9f8602836870 58a2c0ecd8ba9f8602836870
this is true
[]
--------------------------------------------------------------------------------------------------------
Answer: With help of Deividas Karžinauskas the solution is:
Game.where('game.players.id', user).where('game.rules.gameType', game.rules.gameType).find({}, function(err, data) { //, "game.rules.quatro": game.quatro
deferred.resolve(data);
});
This is because of the additional rules that you specify ({gameType : 1, quatro : null}), which do not exist in the player object (
{
"id" : "58a2c0ecd8ba9f8602836870",
"name" : "PlayerName",
"type" : NumberInt("1"),
"avgStatistic" : "30.00",
"numbersHit" : NumberInt("1"),
"totalScore" : NumberInt("60"),
..............................
}
). You can confirm this by simply looking for a game by id.
If you want to add these rules then you should find all games which match these rules and then look for the games of a specific player.

MongoDB Incrementing Dynamically Generated Sub-Document

I have the document below and I want to dynamically increase the quantity of a specific cart item. Since I don't know the id at compile-time, I can't use the $inc operator as you normally would. I was wondering if someone knows how to increase the quantity dynamically. I've got this outline so far.
Current Progress
function userCartAvail(err, success) {
if (err) { unableToInsert(); }
if (success) {
// 1. Check if user already has that item in his/her cart
let itemExists = false;
let cart = success.cart;
if (cart.length >= 1) {
cart.map(item => {
if (itemId == item._id) { itemExists = true; };
});
}
// 2. IF item already in cart, increase the quantity
if (itemExists) {
}
}
}
User.findOne({ _id: userId }, { cart: 1 }, userCartAvail);
Document
{
"_id" : ObjectId("587b72870862be18ebc514b1"),
"firstName" : "John",
"lastName" : "Doe",
"verified" : false,
"dateCreated" : ISODate("2017-01-15T13:00:55.335Z"),
"cart" : [
{
"quantity" : 1,
"_id" : ObjectId("587b6b69799ad7ff650edbb5")
},
{
"quantity" : 1,
"_id" : ObjectId("587b6bedbb5")
},
]
}

How to get count of elements in document array - MongoDB?

I have the following MongoDB collection (JSON):
{
"_id" : ObjectId("570185458351bbac27bc9a20"),
"email" : "test#gmail.com",
"applicants" : [
{
"id" : "570724e4ae4f8a5026156999",
"email" : "a#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156333",
"email" : "a2#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156111",
"email" : "a3#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156222",
"email" : "a4#gmail.com",
}
],
},
{
"_id" : ObjectId("570185458351bbac27bc9a20"),
"email" : "test#gmail.com",
"applicants" : [
{
"id" : "570724e4ae4f8a5026156555",
"email" : "a#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156666",
"email" : "a2#gmail.com",
},
],
},
{
"_id" : ObjectId("570185458351bbac27bc9a20"),
"email" : "test2#gmail.com",
"applicants" : [
{
"id" : "570724e4ae4f8a5026156555",
"email" : "a#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156666",
"email" : "a2#gmail.com",
},
],
}
I would like to get the count of the elements in all arrays of the of the document where the email = test#gmail.com. How can I go about getting that count?
I am using the following to get the number of documents with email test#gmail.com using this:
collection.count({"email" : tmpEmail}, function (err, count) {
res.json(count);
console.log("Number: " + count);
});
How can I go ahead and count the number of elements in all applicant arrays for the documents where the email is test#gmail.com? The could for the example above would be: 6.
EDIT:
As per one of the answers I modified my query to the following:
Answer 1:
collection.aggregate(
{$match: {"email": req.user.username, "status" : "true"}},
{$unwind: "$applicants"},
{$group: {_id:null, count: {$sum :1}}, function (err, count) {
res.json(count);
console.log("Number of New Applicants: " + count);
}
});
Answer 2:
collection.aggregate(
[{$match:{"email" : req.user.username, "status" : "true"}},
{$project:{_id:0, email:1, totalApplicants:{$size:"$applicants"}}},
{$group:{_id:"$employer", count:{$sum:"$totalApplicants"}}}],
function (err, count){
res.json(count);
console.log("Number of New Applicants: " + count);
});
You can use an aggregate query instead:
collection.aggregate(
[{$match: {"email": req.user.username, "status" : "true"}},
{$unwind: "$applicants"},
{$group: {_id:null, count: {$sum :1}}}], function (err, result) {
console.log(result);
console.log("Number of New Applicants: " + result[0].count);
if(result.length > 0)
res.json(result[0]);
else
res.json({count:0});
}
});
This will result you in one document where count will have your required result
This may require to write a aggregation since you need to count the size of applicants array grouped by email:
Here is the equivalent mongodb query that returns the expected email with count:
db.yourCollection.aggregate(
[{$match:{"email" : "test#gmail.com"}},
{$project:{_id:0, email:1,totalEmails:{$size:"$applicants"}}},
{$group:{_id:"$email", count:{$sum:"$totalEmails"}}}])
This returns { "_id" : "test#gmail.com", "count" : 6 }
You may need to change this according to your code.

$pull from array in document

I am trying to $pull from an array within a mongodb document.
The document has the structure:
{
"_id" : ObjectId("54ee62ef688b41ff072b934b"),
"pictures" : [
{
"url" : "...",
"_id" : ObjectId("54ee6303688b41ff072b934d")
},
{
"url" : "...",
"_id" : ObjectId("54ee6304688b41ff072b934e")
},
{
"url" : "",
"_id" : ObjectId("54ee6304688b41ff072b934f")
}
]
}
I tried the update object
var update = { $pull: { pictures: {$elemMatch: {_id:req.params.picid } } } }
db.activity.update({_id: new ObjectId(req.params.id)}, update)
which returns writeresult: 1, but the picture is never removed.
ps I am using node, hense the req.params.picid
The $pull operator acts as a query document in itself and is also considered against every element of the array so $elemMatch is not needed:
var update = {
"$pull": { "pictures": { "_id": new ObjectId(req.params.picid) } }
};
You also need to cast your "string" from request params to an ObjectId.

Categories