MongoDB Incrementing Dynamically Generated Sub-Document - javascript

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")
},
]
}

Related

How to get all fields of a MongoDB document

In application I write down in a collection of users documents with separate users. Each document is an object in which there is a name of the user and his category. Categories are an object. How can I take all recorded categories. I try to take them through find (), but there I need to specify the key-value. And I just need to specify the category field and take all the key-values ​​there. How can I get all categories of an individual user?
I need to find them by key.
mongoClient.connect(function (err, client) {
const db = client.db("expensesdb");
const collection = db.collection("users");
if (err) return console.log(err);
collection
.find({ name: "Bob"})
.toArray(function (err, results) {
console.log(results);
client.close();
});
});
Sample data-set
{ "_id" : ObjectId("63050125848392dcf6f3ebba"), "name" : "max", "category" : { "cat1" : "max1", "cat2" : "max2" } }
{ "_id" : ObjectId("63050132848392dcf6f3ebbb"), "name" : "box", "category" : { "cat1" : "box1", "cat2" : "box2" } }
Try this aggregation pipeline :
db.users.aggregate([
{ $match: { "name": "max" } },{$project : {"category":1}}
]);
db.users.aggregate([
{ $match: { "category.cat1": "cat1" } },{$project : {"category":1}}
]);
Output for both:
{ "_id" : ObjectId("630500ec848392dcf6f3ebb9"), "category" : { "cat1" : "cat1", "cat2" : "cat2" } }

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.

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

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.

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.

MongoDB: Adding an array into an existing array

I'm trying to add an "Instructors" array into an already existing "Camps" array.
The hierarchical structure looks something like this:
owner = {
email : 'john.smith#gmail.com',
password : 'mypassword',
firstName : 'john',
lastName : 'smith',
camps : [
{
name : 'cubs-killeen',
location : 'killeen',
manager : {name: 'joe black', email: '', password: ''},
instructors : [
{
firstName : 'bill',
lastName : 'jones',
classes : []
},
{
firstName : 'jill',
lastName : 'jones',
classes : [],
},
],
students : []
}
]
};
I am using Node Express with MongoJS and have been able to successfully add an owner and add "camps", however, in the "addInstructor" function, when I try and add "Instructors" to a particular camp that is when the problems occur. I get no error message, instead it simply appends the "Instructors" array AFTER the items in the camps array.
Any help would be greatly appreciated. Below is my full code, with working functions and then the one that is not working and below that is my mongodb output (albeit wrong):
CampRepository = function(){};
CampRepository.prototype.addOwner = function(owner, callback){
console.log(db);
db.owners.save(owner, function(err, saved){
if (err || !saved) {
console.log('broke trying to add owner : ' + err);
callback(err);
} else {
console.log('save was successful');
callback(null, saved);
}
});
};
CampRepository.prototype.addCamp = function(ownerEmail, camp, callback){
db.owners.update(
{email: ownerEmail},
{$push: {
camps:{
name: camp.name,
location: camp.location,
managerName: camp.managerName,
managerEmail: camp.managerEmail,
managerPassword: camp.managerPassword,
managerPayRate: camp.managerPayRate,
instructors: [],
students: []
}
}
}, function(err, saved){
if (err || !saved) {
console.log('broke trying to add camp ' + err);
callback(err);
} else {
console.log('save was successful');
callback(null, saved);
}
});
};
/*
THIS IS THE ONE THAT DOESN'T WORK
*/
CampRepository.prototype.addInstructor = function(ownerEmail, campName, instructor, callback){
db.owners.update(
{email: ownerEmail, 'camps.name': campName},
{$push: {
camps:{
instructors: {
firstName: instructor.firstName,
lastName: instructor.lastName,
email: instructor.email
},
}
}
}, function(err, saved){
if (err || !saved) {
console.log('broke trying to add camp ' + err);
callback(err);
} else {
console.log('save was successful');
callback(null, saved);
}
});
};
OUTPUT
{
"_id" : ObjectId("51c7b04d2746ef6078000001"),
"email" : "john.smith#gmail.com",
"firstName" : john,
"lastName" : smith,
"password" : "mypassword",
"camps" : [
{
"name" : "cubs-killeen",
"location" : "killeen",
"managerName" : "bill jones",
"managerEmail" : "bill#gmail.com",
"managerPassword" : "secretpasscode",
"instructors" : [ ],
"students" : [ ]
},
{ "instructors" : { "name" : "jon tisdale" } }
]
}
You might need to take a look at this. you can achieve this using dot.notation
It's very powerfull way to find or update items in a larger array of document scheme. If you still not able to achieve this i would happy to provide you the following code...
I've inserted a new owner2
owner2 = {
email : 'murali.ramakrishnan#gmail.com',
password : 'mypassword',
firstName : 'murali',
lastName : 'ramakrishnan',
camps : [
{
name : 'Rotary club',
location : 'trichy',
manager : {name: 'baskaran', email: 'baskaran#mit.edu', password: 'baskaran'},
instructors : [
{
firstName : 'baskaran',
lastName : 'subbiah',
classes : []
},
{
firstName : 'david',
lastName : 'nover',
classes : [],
},
],
students : []
}
]};
If you see we just need to add a new instructor as requested...
let first add the document to the collection
db.try.insert(owner2);
here you go you have added a new document
now, i'm going to create a new instructor object to insert #newly created owner2
instructor1 = {
firstName : 'lakshmi',
lastName : 'kanthan',
classes : []
};
above is the document object for new instructor
you can perform this update in many ways, using mongodbs methods like
collection.update
collection.findAndModify
if you want to insert or update any value to the sub-document we need to find using a dot.notation and push the sub-document to the document, here the code
db.try.update(
{'camps.name': "Rotary club" },
{
$push: { 'camps.$.instructors' : instructor1 }
}
)
the above code inserts a new record under the instructor field as in the field an array it just pushes the sub-document
End-Result
{
"_id" : ObjectId("51c7b222c0468dc711a60916"),
"email" : "murali.ramakrishnan#gmail.com",
"password" : "mypassword",
"firstName" : "murali",
"lastName" : "ramakrishnan",
"camps" : [
{
"name" : "Rotary club",
"location" : "trichy",
"manager" : {"name": "baskaran", "email": "baskaran#mit.edu", "password": "baskaran"},
"instructors" : [
{
"firstName" : "baskaran",
"lastName" : "subbiah",
"classes" : []
},
{
"firstName" : "david",
"lastName" : "nover",
"classes" : [],
},
{
"firstName" : "lakshmi",
"lastName" : "kanthan",
"classes" : [],
}
],
"students" : []
}
]};

Categories