nodejs Multiple Async operations on a collection - node.js

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);
> }
>
> }
>
> )});

Related

Correct approach in Mongoose - validating my code

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.

How do I copy entries from one collection to another using mongoose?

I'm trying to create a little task management site for a work project. The overall goal is here is that the tasks stay the same each month (their status can be updated and whatnot), and they need to be duplicated at the start of each new month so they can be displayed and sorted by on a table.
I already figured out how to schedule the task, I have the table I need set up. A little explanation before the code - the way I'm planning on doing this is having two different task collections - one I've called "assignments", will have the tasks that need to be duplicated (with their description, status and other necessary data) and another collection, which I called "tasks", will have the exact same data but with an additional "date" field. This is where the table will get it's data from, the date is just for sorting purposes.
This is what I have so far -
Index.js: gets all the assignments from the database, and sends the object over to the duplicate function.
router.get('/test', async function(req, res, next) {
let allTasks = await dbModule.getAllAssignments();
let result = await dbModule.duplicateTasks(allTasks);
res.json(result);
});
dbmodule.js:
getAllAssignments: () => {
allAssignments = Assignment.find({});
return allAssignments;
},
duplicateTasks: (allTasksToAdd) => {
try {
for (let i = 0; i < allTasksToAdd.length; i++) {
let newTask = new Task({
customername: allTasksToAdd.customername,
provname: allTasksToAdd.provname,
description: allTasksToAdd.description,
status: allTasksToAdd.status,
date: "07-2020"
})
newTask.save();
}
return "Done"
} catch (error) {
return "Error"
}
}
The issue arises when I try and actually duplicate the tasks. For testing purposes I've entered the date manually this time, but that's all that ends up being inserted - just the date, the rest of the data is skipped. I've heard of db.collection.copyTo(), but I'm not sure if it'll allow me to insert the field I need or if it's supported in mongoose. I know there's absolutely an easier way to do this but I can't quite figure it out. I'd love some input and suggestions if anyone has any.
Thanks.
The problem is that allTasksToAdd.customername (and the other fields your trying to access) will be undefined. You need to access the fields under the current index:
let newTask = new Task({
customername: allTasksToAdd[i].customername,
provname: allTasksToAdd[i].provname,
description: allTasksToAdd[i].description,
status: allTasksToAdd[i].status,
date: "07-2020"
})
Note that you can simplify this by using a for .. of loop instead:
for (const task of allTasksToAdd) {
const newTask = new Task({
customername: task.customername,
provname: task.provname,
description: task.description,
status: task.status,
date: "07-2020"
});
newTask.save();
}

Mongoose - Modle.update() updates wrong document - Cast Error

I need some help to clear some things up.
I have a Model:
var Event = new Schema({
event_code: String
, segments: [Segment]
});
The creation of new documents work very well like perfect. When it comes to update certain documents I ran into some troubles.
When I do this (code below): = it only updates the first document, even if the id does not match
function edit_event (id, new_name, callback) {
Event.update(id, {$set:{event_code: new_name}}, function(err, doc) {
if (err) throw err;
callback();
});
}
When I do this (code below): = it gives me an Error (see below)
function edit_event (id, new_name, callback) {
Event.findByIdAndUpdate(id, {$set:{event_code: new_name}}, function(err, doc) {
if (err) throw err;
callback();
});
}
Error when using findByIdAndUpdate: Cast to ObjectId failed for value ""58fdbde31bff83141b376508"" at path "_id" for model "Event"
Please, i'm desperate :! :/
UPDATE
I figured out that the id that i'm trying to pass get stored with "" around it, so when i am looking for document with matching ID it puts an extra pair of "" around it so it ends up like ""id""
UPDATE 2
When I am listing all my documents, it returns:
{ _id: 58fdbde31bff83141b376508,
event_code: 'TestABC',
__v: 0,
segments: [] }
Then when i store the id in an HTML form it adds extra pair of "" around it ... that's not the case with event_code. Why is that ?
Looks like you performed unneeded JSON.stringify and that's why you get quotes around the id. Calling JSON.parse should solve the issue:
Event.findByIdAndUpdate(JSON.parse(id), {$set:{event_code: new_name}}, ...

Mongoose: Push data to array in for loop

i have small problem i have this code:
Messages.findOne({id_user: to}, function (err, toUser) {
//dopisujemy wiadomość w tablicy użytkownika
var messArray = toUser.messagesArray;
//przeszukujemy tablice z wiadomościami aby znaleźć odpowiedniego użytkownika
for(var i=0;i<messArray.length; ++i) {
if(messArray[i].id_converser = who) {
// console.log(messArray[i]);
toUser.messagesArray[i].messages.push({id_converser: who,message: message,date: Date()});
toUser.save(function (err) {
if(err) console.log(err);
console.log('Wiadomość dodana');
})
return;
}
}
}) //end zapisujemy wiadomość w miejscu przeznaczenia
and everything seems fine, in console i have message "wiadomosc dodana", but when i check it in database i not see this added data, why.
Thanks for reply.
In order to push to the array in the actual database you need to use $push or something like $addToSet operator
When you modify any deep property or array property of an object in JavaScript you need to use isModified method in order to tell Mongoose that a property of the object is modified and update it in the database.
http://mongoosejs.com/docs/api.html#document_Document-isModified
So you need to execute it after the push function like that:
toUser.isModified('messagesArray')

NodeJS + Mongo native – check if collection exists before query

I've got a function, trying to get a specific value from settings collection in MongoDB. The marker for settings object, containing settings values, in settings collection is {'settings':'settings'}. The schema is:
collection:setting
|--object
|--{'settings':'settings'}
|--{'valueA':'valueA'}
|--...
The problem is when I first time query settings object, the collection 'settings' simply does not exists. So,
exports.getInstruments = function (callback) {
db.collection("settings", function(error, settings) {
settings.find({ "settings" : "settings" }), (function(err, doc) {
callback(doc.instruments);
});
]);
}
just hangs and callback is not invoked. If collection does not exist, I should return "" or undefined, else - doc.instrumens.
There's an exists() function that you could use to determine whether or not to execute the code that hangs.
> db.getCollection('hello').exists()
null
> db.getCollection('world').exists()
{ "name" : "testdb.world" }
You could potentially take advantage of db.createCollection which explicitly creates a collection:
> db.createCollection("asd")
{ "ok" : 1 }
> db.createCollection("asd")
{ "errmsg" : "collection already exists", "ok" : 0 }
Just check if the command succeeded based on the ok field.
You shouldn't need to specially handle the new collection case, I think the problem is with your code.
Aside from some syntax problems, the main problem is that find passes a Cursor to your callback function, not the first matching document. If you're expecting just one doc, you should use findOne instead.
This should work:
exports.getInstruments = function (callback) {
db.collection("settings", function(error, settings) {
settings.findOne({ "settings" : "settings" }, function(err, doc) {
callback(doc && doc.instruments);
});
});
};

Resources