Define server function for mongo based on pure javascript function - javascript

Suppose I have a pure javascript file that has several function definitions. It does not contain anything mongo-specific - these functions parse and evaluate a query on a document. I would like to use these functions for server queries (e.g. db.collection.find(...) or map-reduce). How may I do this so that the server functions can essentially invoke what's in the pure javascript file?
Example: In foobar.js -
function foo(query, doc) {
// some predicate, for now lets simulate by evaluating a boolean field called 'available'
return doc.available;
}
According to https://docs.mongodb.org/manual/tutorial/store-javascript-function-on-server/
I would have to do this:
db.system.js.save(
{
_id : "bar" ,
value : function (query, doc){
return doc.available;
}
}
);
Then invoke db.loadServerScripts();
Running the following gives me the desired results:
db.myCollection.find(function() { return bar("some query", this) ;})
However I'd like to avoid inlining the functions in this db.system.js.save call. These functions belong to a library that I do not maintain. If there are any changes to the javascript library, I would have to manually convert them to db.system.js.save calls.
I would like to run something like this in mongo shell:
load("foobar.js")
db.system.js.save(
{
_id : "baz" ,
value : function (query, doc){
return foo(query, doc);
}
}
);
Then:
db.myCollection.find(function() { return baz("some query", this); })
But I get a "ReferenceError: foo is not defined" in my queries that invoke baz. Function "foo" is clearly not in scope. Suggestions or alternatives?
Disclaimer: I understand that using native operations are preferred, in addition to moving complexity out of the javascript into my application. However I am not at the point where I have the resources to essentially fork the logic from javascript to application logic that decomposes to native ops

Related

How to change json value in a nested (mongoose) callback

I'd like to change a json array within a nested function that I created as a callback within a mongoose query.
I am puzzled why it is not possible to change a json array within this scope. Any attempt results in the original value of 'default' which can be shown by the last console.log(answer)
As I am an Embedded C developer with newbie skills in javascript (at most), I'd like to know how to alter any value in a nested scope. And please share some background why my function shows this type of behaviour.
Any help is warmly welcome...
function pwdGen(body) {
var answer = [{
error: errors["E004"],
user: '',
password: 'default',
timeout: 0,
active: false
}];
user.find().where("usertype").equals("superuser").select("username password").exec(
function (err, users) {
if(users.isArray && users.length==0) {
console.log("exists");
answer[0].password="exist_pwd";
} else {
console.log("empty");
answer[0].password="empty_pwd";
}
}
);
answer[0].user="asdave"
answer[0].timeout=1;
console.log(answer);
return answer;
}
Problem
Javascript is async in nature means it does not execute instructions such as network access synchronously like C, C++ or Java.
Solution
Promises are here for rescue.
You need to do something like this:
var users = user.find().where("usertype").equals("superuser").select("username password").exec();
users.then(function(user) {
if(users.isArray && users.length==0) {
console.log("exists");
answer[0].password="exist_pwd";
} else {
console.log("empty");
answer[0].password="empty_pwd";
}
})
EDIT
It's just that the JS is single threaded means one thing at a time but when it sees any network access request like API call, it moves that instruction to somewhere else in the browser where its gets executed and JS continue with the following instructions when all instructions are executed, the moved part which was executed somewhere else, it's result get merged.
Excellent resource to get the idea.

NodeJS : how to use arguments like req, res, result in functions?

I'm fairly new to JS especially Node and Express. I am following some tutorials on how to build an API and at the same time learning about JS special features such as let/const/var, arrow functions etc.
In many tutorials I have seen things likes this :
somecode.then((result) => {someothercode})
With: "somecode" being for example a get request
Is "result" the name of the returned value or is it a convention that JS developper use?
By that I mean, does this for example work?
somecode.then((foo) => {someothercode})
Also for req, res variables what does this mean?
app.get("/users/:userId", [
usersController.getById
]);
Here is the getById function (using once again the "result"):
exports.getById = (req, res) => {
userModel.findById(req.params.userId).then((result) => {
res.status(200).send(result);
});
};
the getById method defined in the controller needs (req, res), does that mean, when i call it like the code above, the req and res arguments are implicitly used?
Also it needs a parameter :
req.params.userId
which is in the url of the route, how does it pass to another file?
I have a route.js file that uses a controller.js file that uses a model.js. How does the param go from route to controller?
And it won't work if I change the param name right? for example:
req.params.id
Sorry for long post, I'm trying to understand JS logic to get some good habits and write clean code.
Thanks!
Is "result" the name of the returned value or is it a convention that JS developper use? By that I mean, does this for example work?
From my experience, yes - result is often used. Often times you'll see thing like value, response, but ultimately it can be whatever you define. I would recommend sticking to convention, and also check out the MDN Promise tutorial if you are starting out with understanding NodeJS asynchronous operations.
Also for req, res variables what does this mean?
app.get("/users/:userId", [
usersController.getById
]);
That is a middleware chain. Check out the Express docs for more information.
the getById method defined in the controller needs (req, res), does that mean, when i call it like the code above, the req and res arguments are implicitly used? Also it needs a parameter :
req.params.userId
which is in the url It won't work if I change the param name right? for example:
req.params.id
Yes, that is using a named parameter. Without the full router code, it is hard to know how the getById method is linked to the defined route. The Express routing documentation will likely be a good start on that.
Is "result" the name of the returned value or is it a convention that JS developper use?
result is the name of a new variable you are creating to represent the value passed in from the Promise resolution. Yes, your foo example will work.
(req, res) => {} is the same (mostly) as a function that looks like this:
function getById(req, res) {...}
req, and res are just representational of the values that will be passed to this function. They could just as easily have been called (foo, bar).
It looks like you're struggling with understanding callback functions. Consider the following code then please crack open the source code for the packages that you are using. and it looks like you are using express.js
function something(callback) {
var x = 5;
var y = 'anything';
callback(x, y);
}
something(function(req, res) {
console.log(req);
console.log(res);
});
the something function is created and inside of that function scope, var x and y are created with any type. then when we invoke or use something function we are passing a function as a variable that gets passed in as variable callback then it can be used since it is a function so we call callback with x and y which can literally be any value and for effect, I am passing back a number and a string as req and res.
It's just a convention. Note that the code:
somecode.then((result) => {someothercode});
Is actually:
somecode.then(myFunction);
Since somecode is a Promise, your function may be called with zero or one argument. It is up to you to name this argument:
function myFunction (foo) {
// use foo here
}
somecode.then(myFunction);
Of course, unlike some other languages, javascript does not force you to name your function. You can just use a nameless (anonymous) function:
somecode.then(function(mango) { /* use mango here */ })
Arrow functions is a new syntax allowing you to write anonymous functions in a shorter style (it also behaves slightly differently with regards to scope and the value of this)
Express.js and http.Server
In node's http.Server library and Express.js framework, each server request will call a function you define and pass it two arguments: the request object and the response object. The variables req and res are just conventions people use when writing their own request handler functions. You can name them anything you like. For example you may prefer to use request and response instead or rx and tx:
app.get('/say/hello', (rx, tx) => tx.send('Hello'));
How many arguments do I write a callback function with??
The best way to know is to read the documentation of the module you are using. It is not the only way to know - you can of course read the source code instead. But it is often easier to read the documentation. Because of this, javascript modules tend to have really good documentation (otherwise they would be unusable and ignored by the community).
Express.js will actually pass three arguments to your callback (not two!!) - request, response and next where next is a function you can call if you want Express to continue processing instead of replying to the request. One interesting feature of javascript is that you are allowed to call functions with fewer or more arguments and it is not considered a syntax error:
function example (x) {}
example(); // not an error
example(1); // not an error
example(1,2,3,4); // also not an error
Express uses this feature by always calling your callback with three arguments while allowing you to declare said callback with only two arguments if you don't need the third, next argument.

How to make api request with callback as argument in node.js?

I want to use method getPlayers(callback) which is defined as:
getPlayers(callback)
callback - Required. Called with an object of players
players - An object containing all the players connected to the server, with their name as the key
Retrieve all players connected to the server.
Here is the link to complete module for further details :
https://www.npmjs.com/package/hltv-livescore#getplayerscallback
If you want to use it and access the data, you'll need to do something like this:
getPlayers(function(players) {
// Here your players will be available
console.log(players)
})
Bonus: If you're using ES6, you can use Arrow functions, that are more elegant, like this (single line):
getPlayers(players => console.log(players))
or (multi line):
getPlayers(players => {
console.log(players)
})
You can read more about the async nature of Javascript here
If you refer source code of npm package you can see this code
https://github.com/andrewda/hltv-livescore/blob/master/lib/index.js#L78
Livescore.prototype.getPlayers = function(callback) {
callback(self.players);
};
You can use getPlayers like this :
Livescore.getPlayers(function(players){
// you will get players here
});

MongoDb - Is $where more performant when using functions stored in db.system.js?

MongoDb recommends that "In general, you should use $where only when you can’t express your query using another operator" due to perframce reasons. However, it appears that we can store Javascript functions server-side using the special table system.js.
So instead of doing this:
db.myCollection.find( { $where: function() { return this.credits == this.debits; } } );
Do this instead:
db.system.js.save({ _id : 'queryFunction` , value : function() { return this.credits == this.debits; } });
...
db.myCollection.find( { $where: 'queryFunction' ); //not sure about syntax
Would the second variant reap any performance benefits; and if so, are they significant?
References:
$where
db.system.js
It won't help.
In either case the function will be loaded as string and compiled to a javascript function before it is executed by MongoDBs Javascript engine for every single document. Whether you load it via network or via a query to system.js won't make much of a difference, and even when it would, the bulk of the runtime would be the execution of the function in most cases.

How to use closures in mongodb shell?

I'm kind of confused on how to use closures in mongodb shell.
I want to create a function that i can use exclusively during development to quickly look up a document by a part of it's _id.
The function should return a $where selector that does the necessary matching.
I wanted to write it like this:
var id = function(pattern, selector) {
return Object.extend({
$where: function() {return (this._id + "").indexOf(pattern) != -1;}
}, selector);
};
But when i try it i get the following error:
db.mycollection.find(id("ab1"));
error: {
"$err" : "JavaScript execution failed: ReferenceError: pattern is not defined near ').indexOf(pattern) ' ",
"code" : 16722
}
Invoking $where manually does seem to work oddly enough:
id("ell").$where.call({_id: "hello"}); // true
I could only think of this as a solution to make it work, but this is obviously terrible.
To clarify: This method with new Function works fine, but i don't like it as the above method should work as well.
var id = function(pattern, selector){
return Object.extend({
$where: new Function("return (this._id + '').indexOf('" + pattern + "') != -1;")
}, selector);
};
Why is my closure not working, do closures work in mongodb shell ?
Bonus question: Can i register this function somewhere in a user profile to load automatically ?
It appears that the MongoDB shell serializes (in this case, string-ifies) $where function objects in order to send them to the server.
A function in JavaScript is much more than its functional code -- it's part of a closure, which includes both the function code itself and the function's variable referencing environment for accessing non-local variables. After the function is serialized, it gets unserialized and executed with different referencing environment. When the function tries to reference pattern, it's asking for pattern in a totally different referencing environment, which does not have a pattern variable.
That's why your literal string function works. That function contains the actual value of pattern, not the variable identifier pattern.
See this MongoDB bug, which explains:
That would work if the function was being evaluated locally, but the closure isn't sent over automatically.
In babble it used to be, but not in the shell.
This suggests that your code may work outside of the shell, but I don't know enough about Mongo internals to say for sure.

Categories

Resources