Using a Mongoose Schema Function Inside Of An Exported Mongoose Model - javascript

I have been working on an authentication system lately that is supposed to either create a new user if no user with a given ID exists OR loads in a user with an existing ID. I also already figured out that you can return whether or not a user exists with in ID by using
User.count({_id: id}, function (err, count){
if(count > 0){
// User exists, continue with loading data
} else {
// User does not exist, create one
}
});
with User being an exported Mongoose Model mongoose.model('User', UserSchema);
If I created a function existsWithId(id) in my app.js now, it would work perfectly fine (judging by the fact that the previously mentioned code works perfectly fine), but I want to move this function to my user.js Model (which contains the Mongoose / MongoDB Code) instead so that I have it only for that specific model. Whenever I try to do that by using
UserSchema.statics.existsWithId = function(id) {
this.count({_id: id}, function (err, count){
return count > 0;
});
}
or similar methods, though, it will either just return undefined or an error once I run it inside of my app.js.
NOTE: I'm sorry if this is a kind of simple or even foolish question, but I am pretty new considering DB management with Mongoose and while I do have SOME experience with JS, I am not exactly a professional at it.
If any of you need additional code provided to answer the question, feel free to ask me and I will do so. Also, I can't +rep answers yet, but I always appreciate 'em :)

Your existsWithId method returns a value asynchronously so you can give a callback to it to get the result back like this :
UserSchema.statics.existsWithId = function(id, cb) {
this.count({ _id: id }, function(err, count) {
cb(err, count > 0);
});
}
User.existsWithId("5882c3be5aad09028d4d8b46", function(err, res) {
console.log(res);
});

Related

How to rollback when error occurred in the second query execution| NodeJS

I used to use rollback in PHP, I want to implement the same concept in NodeJS. I am calling a function that inserts a data to the database, and on success I am calling another function from another Model to insert data into another table.
My Code
StoreReport
'use strict';
var sql = require('./db.js');
// IMPORT ANOTHER MODEL
var CashDetailModel = require('./cashDetailModel.js');
var StoreReport =function(storeReport){
this.store_id=storeReport.store_id;
this.cash_expense=storeReport.cash_expense_amount;
}
StoreReport.addNewStoreReport = function (report_details,request,result){
sql.query('INSERT INTO store_report SET ?',report_details, function(err,res){
if(err){
result(err,null);
}else{
var new_cash_detail = new CashDetailModel(report_details);
new_cash_detail.store_report_id=res.insertId;
CashDetailModel.addCashDetail(new_cash_detail,function(err,CashDetailModel){
if(err){
// I NEED TO ROLLBACK IF ERROR OCCURED IN THIS PHASE
res.send(err);
}else{
result(null,res.inserted);
}
})
}
})
}
module.exports = StoreReport;
It is explained pretty well here: https://know-thy-code.com/transactions-with-multiple-queries-nodejs-mysql/.
You call sql.beginTransaction() before the first query and write your queries in the callback for that. Then you can rollback your transaction with sql.rollback() or commit the changes with sql.commit().
It depends on which mysql engine you are using, transnational engines like innodb considers each query as separate transaction if you are explicitly not defining the transaction.
So in order to revert the change you have two options :
Revert changes using update or delete query, which is a wrong approch.
Explicitly define transaction using connection.beginTransaction() and commit or rollback accordingly.

Better performance when saving large JSON file to MySQL

I have an issue.
So, my story is:
I have a 30 GB big file (JSON) of all reddit posts in a specific timeframe.
I will not insert all values of each post into the table.
I have followed this series, and he coded what I'm trying to do in Python.
I tried to follow along (in NodeJS), but when I'm testing it, it's way too slow. It inserts one row every 5 seconds. And there 500000+ reddit posts and that would literally take years.
So here's an example of what I'm doing in.
var readStream = fs.createReadStream(location)
oboe(readStream)
.done(async function(post) {
let { parent_id, body, created_utc, score, subreddit } = data;
let comment_id = data.name;
// Checks if there is a comment with the comment id of this post's parent id in the table
getParent(parent_id, function(parent_data) {
// Checks if there is a comment with the same parent id, and then checks which one has higher score
getExistingCommentScore(parent_id, function(existingScore) {
// other code above but it isn't relevant for my question
// this function adds the query I made to a table
addToTransaction()
})
})
})
Basically what that does, is to start a read stream and then pass it on to a module called oboe.
I then get JSON in return.
Then, it checks if there is a parent saved already in the database, and then checks if there is an existing comment with the same parent id.
I need to use both functions in order to get the data that I need (only getting the "best" comment)
This is somewhat how addToTransaction looks like:
function addToTransaction(query) {
// adds the query to a table, then checks if the length of that table is 1000 or more
if (length >= 1000) {
connection.beginTransaction(function(err) {
if (err) throw new Error(err);
for (var n=0; n<transactions.length;n++) {
let thisQuery = transactions[n];
connection.query(thisQuery, function(err) {
if (err) throw new Error(err);
})
}
connection.commit();
})
}
}
What addToTransaction does, is to get the queries I made and them push them to a table, then check the length of that table and then create a new transaction, execute all those queries in a for loop, then comitting (to save).
Problem is, it's so slow that the callback function I made doesn't even get called.
My question (finally) is, is there any way I could improve the performance?
(If you're wondering why I am doing this, it is because I'm trying to create a chatbot)
I know I've posted a lot, but I tried to give you as much information as I could so you could have a better chance to help me. I appreciate any answers, and I will answer the questions you have.

Adding object to PFRelation through Cloud Code

I am trying to add an object to a PFRelation in Cloud Code. I'm not too comfortable with JS but after a few hours, I've thrown in the towel.
var relation = user.relation("habits");
relation.add(newHabit);
user.save().then(function(success) {
response.success("success!");
});
I made sure that user and habit are valid objects so that isn't the issue. Also, since I am editing a PFUser, I am using the masterkey:
Parse.Cloud.useMasterKey();
Don't throw in the towel yet. The likely cause is hinted at by the variable name newHabit. If it's really new, that's the problem. Objects being saved to relations have to have once been saved themselves. They cannot be new.
So...
var user = // got the user somehow
var newHabit = // create the new habit
// save it, and use promises to keep the code organized
newHabit.save().then(function() {
// newHabit is no longer new, so maybe that wasn't a great variable name
var relation = user.relation("habits");
relation.add(newHabit);
return user.save();
}).then(function(success) {
response.success(success);
}, function(error) {
// you would have had a good hint if this line was here
response.error(error);
});

Meteor Leaderboard example: resetting the scores

I've been trying to do Meteor's leaderboard example, and I'm stuck at the second exercise, resetting the scores. So far, the furthest I've got is this:
// On server startup, create some players if the database is empty.
if (Meteor.isServer) {
Meteor.startup(function () {
if (Players.find().count() === 0) {
var names = ["Ada Lovelace",
"Grace Hopper",
"Marie Curie",
"Carl Friedrich Gauss",
"Nikola Tesla",
"Claude Shannon"];
for (var i = 0; i < names.length; i++)
Players.insert({name: names[i]}, {score: Math.floor(Random.fraction()*10)*5});
}
});
Meteor.methods({
whymanwhy: function(){
Players.update({},{score: Math.floor(Random.fraction()*10)*5});
},
}
)};
And then to use the whymanwhy method I have a section like this in if(Meteor.isClient)
Template.leaderboard.events({
'click input#resetscore': function(){Meteor.call("whymanwhy"); }
});
The problem with this is that {} is supposed to select all the documents in MongoDB collection, but instead it creates a new blank scientist with a random score. Why? {} is supposed to select everything. I tried "_id" : { $exists : true }, but it's a kludge, I think. Plus it behaved the same as {}.
Is there a more elegant way to do this? The meteor webpage says:
Make a button that resets everyone's score to a random number. (There
is already code to do this in the server startup code. Can you factor
some of this code out and have it run on both the client and the
server?)
Well, to run this on the client first, instead of using a method to the server and having the results pushed back to the client, I would need to explicitly specify the _ids of each document in the collection, otherwise I will run into the "Error: Not permitted. Untrusted code may only update documents by ID. [403]". But how can I get that? Or should I just make it easy and use collection.allow()? Or is that the only way?
I think you are missing two things:
you need to pass the option, {multi: true}, to update or it will only ever change one record.
if you only want to change some fields of a document you need to use $set. Otherwise update assumes you are providing the complete new document you want and replaces the original.
So I think the correct function is:
Players.update({},{$set: {score: Math.floor(Random.fraction()*10)*5}}, {multi:true});
The documentation on this is pretty thorough.

node.js/javascript/couchdb view to associative array does not seem to work

I am trying to create a webapp on a node/couchdb/windows stack but get terribly stung by what seems to be a lack of experience.
In the database, there is a view that returns all users with passwords. Based on the tutorial for a blog I have tried to access the view through my node code.
Whenever I investigate the structure of the users or users variable, I get an undefined object.
The call to getDatabase() has been tested elsewhere and works at least for creating new documents.
function GetUser(login)
{
var users = GetUsers();
return users[login];
}
function GetUsers() {
var db = getDatabase();
var usersByEmail = [];
db.view("accounts", "password_by_email")
.then(function (resp) {
resp.rows.forEach(function (x) { usersByEmail[x.key] = x.value});
});
//usersByEmail['test'] = 'test';
return usersByEmail;
}
I am aware that both the use of non-hashed passwords as well as reading all users from the database is prohibitive in the final product - just in case anyone wanted to comment on that.
In case something is wrong with the way I access the view: I am using a design document called '_design/accounts' with the view name 'password_by_email'.
Your call to db.view is asynchronous, so when you hit return usersByEmail the object hasn't yet been populated. You simply can't return values from async code; you need to have it make a callback that will execute the code that relies on the result.

Categories