Correct approach in Mongoose - validating my code - node.js

I have 3 collections in my application.
Member - has a field called MemberMatches which is referencing the Matches collection.
Tournament - has a field called TournamentMatches which is referencing the Matches collection
Matches.
On the REACT front end on the click of a button, I am creating a bunch of matches using a for loop and making entries in the matches collection via node. At the time I want to enter the Object ID into the Member and Tournament collections.
Here is the code:-
for(i=0; i<playersList.length;){
player1Name = playersList[i]
for(j=0; j<playersList.length;){
if(j<=i){
j=i
j++
continue
}
player2Name = playersList[j]
var newMatch = new MatchRegister({
Player1Name: player1Name,
Player1GroupNumber: groupNumber,
Player2Name: player2Name,
Player2GroupNumber: groupNumber,
});newMatch.save(async function(err,data){
if(err){
console.log(err)
} else {
await Tournament.findOneAndUpdate({_id: tid}, {$push: {TournamentMatches: data._id}})
await Member.findOneAndUpdate({MemberName: data.Player1Name}, {$push: {MemberMatches: data._id}})
await Member.findOneAndUpdate({MemberName: data.Player2Name}, {$push: {MemberMatches: data._id}})
}
})
j++
}
i++
}
}
return
I am calling this function from the route.push. Saving the matches first and then 3 FindOneAndUpdate with the await keyword.
This entire code and functionality works. Upon execution I can clearly see that the data is being populated in all 3 collections correctly.
Question: Is this the right approach? Is there another Mongoose way to do this.

Related

Can't splice mongoose document key

I am trying to remove keywords with Nodejs and Express from a mongoose document that looks somewhat like this:
{
name: "Instagram",
description: "Image sharing website",
keywords: [{name:"Image", value: 1}, {name:"sharing", value: 1}, {name:"website"}, {name:"Instagram", value:5}, {name:"application", value: 2}]
}
Here is the part of my update query which seems to the problem (it does not delete the keywords properly if there are many keywords, though it has worked a couple of times with few keywords):
Model.findOne({_id:req.body.id}, function(err,doc){
for(var i = 0; i < doc.keywords.length; i++){
if(doc.keywords[i].value == 1){
doc.keywords.splice(doc.keywords[i], 1); //does nothing
doc.save()
console.log(doc.keywords[i]) //Shows the correct keywords to be deleted.
}
};
})
Splice doesn't work on an array of objects. See Remove Object from Array using JavaScript for optional solutions to that. Otherwise I'd suggest a different method, why don't you just filter the keywords based on your needs like:
doc.keywords = doc.keywords.filter((key) => key.value === 1);

Mongoose find not match ids from array which passed

I'm stuck in mongoose query. I've an array of ids as input, I want to search that array of ids in a database for matching elements. It returns perfect result after using $in in find query.
Now, I want that Ids from an array which is not found in the database. what is the best way to do it?
Please try to comment it.
Template.find({
_ids : [
"as6d87as67da7s8d87a87", // available in database
"as6dasd8sa9d8a9a9s8d7", // not-available in database
"6756asd5as6dsadghasd3", // available in database
]
}, function(err, result){
// Need result as "as6dasd8sa9d8a9a9s8d7", which is not matched. or in object.
// Can we do with `aggregate` query?
});
I think this makes what you want
var _ids = [
"as6d87as67da7s8d87a87",
"as6dasd8sa9d8a9a9s8d7",
"6756asd5as6dsadghasd3"
];
Template.find({
_ids : _ids
}, function(err, result){
var filteredResult = _ids.filter(currentId => !result.some(item => item._id.toString() == currentId));
});
Answer by David will work. The idea here is to compare _ids array with the _ids in the result and return the missing ones. Adding more code just for understanding purposes:
Push _id of result into a new array
Compare _ids and the new array to return missing elements
var resIDs = new Array();
for (var i = 0; i < result.length; i++) {
resIDs.push(result[i]._id.toString());
}
var resultFiltered =
_ids.filter(function (v) {
return !resIDs.includes(v.toString());
})

nodejs Multiple Async operations on a collection

I'm in a process of building a nodejs app and i'm quite new to nodejs asynchronous model.
the problem is that i've modified a database collection to include a string field while sill referencing another collection, namely, I've modified vehicle model to include extra field named make name as string field instead of just referencing the make collection, i've done this de-normalization for the sake of efficiency as this field is frequently read not written.
an excerpt from DB schema :
var vehicleSchema = new mongoose.Schema({
stringId: String,
make: {
type: mongoose.Schema.Types.ObjectId, ref:"Make",
makeText: String
},
and here is an excerpt for make schema:
var makeSchema = new mongoose.Schema({
name: String,
models: [modelSchema]
});
the problem is that i've some data in the collection already, i need to write a code to loop through the vehicles in the vehicle collection and then lookup the vehicle's make id against the makes collection, then find the corresponding make name string and assign that value to vehicle.make.makeText
I done an extensive research and i reached asyn module, below is the code that i wrote to solve this problem:
// code to loop through all records in vehicles collection and add makeText
vehicle.find({},(err,allVehicles) =>{
console.log("executed vehicle.find()");
async.eachOfSeries(allVehicles,function(vehicle){
console.log(vehicle.stringId);
make.findById(vehicle.make.id,function(err,foundMake){
vehicle.make.makeText = foundMake.name;
console.log(foundMake.name);
vehicle.save(function(err,saved){
if(err){
console.log(err.message);
}
})
})
},function(err){
if (err){
console.log(err.message);
}
}
)});
unfortunately the code seem to execute only once and i don't see the effect of vehicle.save method as i can't see the change to the DB.
Thanks in advance
I think you don't use eachOfSeries right http://caolan.github.io/async/docs.html#eachOfSeries
A function to apply to each item in coll. The key is the item's key,
or index in the case of an array. The iteratee is passed a
callback(err) which must be called once it has completed. If no error
has occurred, the callback should be run without arguments or with an
explicit null argument. Invoked with (item, key, callback).
You get a key, not full object, and MUST call a callback.
You forgot the next() function, You need to call it after the findById returns.Did not run this fix, however it shows where the
problem is
> // code to loop through all records in vehicles collection and add
> makeText vehicle.find({},(err,allVehicles) =>{
>
> console.log("executed vehicle.find()");
> async.eachOfSeries(allVehicles,function(vehicle,next){
> console.log(vehicle.stringId);
> make.findById(vehicle.make.id,function(err,foundMake){
> vehicle.make.makeText = foundMake.name;
> console.log(foundMake.name);
> vehicle.save(function(err,saved){
> if(err){
> console.log(err.message);
> next(err)
> }else{
> next();
> }
>
> })
> })
>
> },function(err){
> if (err){
> console.log(err.message);
> }
>
> }
>
> )});

MongoDB race conditions or concurency issues

I have the following code in my chat application based on NodeJS and MongoDB to change admin for room:
export function setAdmin(room, user) {
const userGuid = getGuid(user);
if (room.users && room.users.length) {
// filter current and new
room.users = room.users.filter(guid =>
guid !== room.adminGuid && guid !== userGuid
);
} else {
room.users = [];
}
room.users.push(userGuid);
room.adminGuid = userGuid;
return roomsCollection.save(room, { w: 1 });
}
Each room have only 2 users: admin and customer.
To update several rooms:
const rooms = await roomsCollection.find({ adminGuid: currentAdminGuid }).toArray();
for (const room of rooms) {
await setAdmin(room, newAdminUser);
}
I had some problems under highload with my application. They were resolved with help of indexes.
But after, working with MongoDB dump I found out that I have rooms with 3 users guids in room.users array. I think that save works as update for exist document, but how it updates array? If $set, why I have such 3 users rooms. Any thoughts how it would be possible?
save() behaves as insert if document does not exist. You have to use update() with {upsert: false}
some resources to update array: Array Query, array Operator
simple tutorial to nest array inside document, here
advanced example of dealing with multiple nested array, here

Trying to understand mongodb's findOne function's behaviour and why it's causing the following issue

What I am trying to do here is find a user by id and if the user is not found then I am adding it to the collection. However, if found, I simply want to push a new product into the existing user's document.
The issue here is the following if statement that is inside a for loop:
for(var i=0; i < data.products.length; i++) {
console.log("Product
found:"+!data.products[i].product.equals(req.body._id));
//if the requested product is not found
if(!data.products[i].product.equals(req.body._id)){
data.products.push({product: req.body._id, isAdded: true,
quantity:1});
}
console.log("Adding product.....");
}//for loop
It seems that the first iteration find the condition true even though I already have a product with the same id exists. It should be false as long as the id exists in my document. Would anyone be able to explain this behavior of Mongoose please?
The console.log inside the for loop is returning the following results:
Product found:true
Adding product.....
Product found:false
Adding product.....
Product found:false
Adding product.....
Product found:false
Adding product.....
api.post('/me/cart', wagner.invoke(function(Cart) {
return function(req, res, next) {
console.log("new post request"+ req.headers['usr'] );
Cart.findOne({userId: req.headers['usr']},
function(err,data){
if(err){
throw err;
}
console.log("found this..."+ (!data));
//if No user is found add a new cart
if(!data && data.length < 0){
var cart = new Cart();
cart.userId = req.headers['usr'];
cart.products.push({product: req.body._id,
quantity:1, isAdded:true});
cart.save(function(error, data) {
if (error){
return res.
status(status.INTERNAL_SERVER_ERROR).
json({ error: error.toString() });
};
return res.json({ product: data });
});
}
//if user is found updated found users cart
else if (data){
for(var i=0; i < data.products.length; i++){
//if the requested product is not found
if(!data.products[i].product.equals(req.body._id)){
data.products.push({product: req.body._id,
isAdded: true, quantity:1});
}
console.log("Adding product.....");
}//for loop
data.save(function(err, updatedCart){
if(err)
throw err;
console.log(updatedCart);
return res.json(updatedCart);
});
}//else if
});
};
I was doing it wrong. Basic for loop fundamentals lol It is obvious that the condition would be true every time it does not find the product id and so will execute the push statement every time it does not find the id. Which will cause it to create duplicate data in the database. The solution was to simply add boolean flag inside the if condition and turn it on if it does not find a value and then use that flag to save and push a new product.
P.s Was stuck with the missing semicolon type of issue for 3 days lol Only because i forgot about minor for loop fundamental. Anyways, mistakes are what teach us the best :D Enjoy.

Resources