Arangodb: Can I call a user function from a user function? - arangodb

Since user-functions are not allowed to have side-effects with respect to the data, I wondered if it's possible to call one function from the other.
I want to factor out common calculations.
One possible way I see is to use
var result = db._query("RETURN myfuncs::func(#a1, #a2)",
{'#a1': 'val1', '#a2': 'val2}, null, null).next();
But I would like another way if possible.

Each user function is completely standalone, meaning it is not possible to call a user function from within another.
This might change in the future, but this is the situation at the moment (ArangoDB 2.3/2.4/2.5).

I found the way how to do this. You can use db._query in user function, i.e.
var db = require('internal').db;
var resultFromOtherUserFunc = db._query('RETURN userFunction::MyFunction(#param)', {param: true}).toArray();

Related

Ensure a Callback is Complete in Mongo Node Driver

I am a bit new to JavaScript web dev, and so am still getting my head around the flow of asynchronous functions, which can be a bit unexpected to the uninitiated. In my particular use case, I want execute a routine on the list of available databases before moving into the main code. Specifically, in order to ensure that a test environment is always properly initialized, I am dropping a database if it already exists, and then building it from configuration files.
The basic flow I have looks like this:
let dbAdmin = client.db("admin").admin();
dbAdmin.listDatabases(function(err, dbs){/*Loop through DBs and drop relevant one if present.*/});
return await buildRelevantDB();
By peppering some console.log() items throughout, I have determined that the listDatabases() call basically puts the callback into a queue of sorts. I actually enter buildRelevantDB() before entering the callback passed to listDatabases. In this particular example, it seems to work anyway, I think because the call that reads the configuration file is also asynchronous and so puts items into the same queue but later, but I find this to be brittle and sloppy. There must be some way to ensure that the listDatabases portion resolves before moving forward.
The closest solution I found is here, but I still don't know how to get the callback I pass to listDatabases to be like a then as in that solution.
Mixing callbacks and promises is a bit more advanced technique, so if you are new to javascript try to avoid it. In fact, try to avoid it even if you already learned everything and became a js ninja.
Dcumentation for listDatabases says it is async, so you can just await it without messing up with callbacks:
const dbs = await dbAdmin.listDatabases();
/*Loop through DBs and drop relevant one if present.*/
The next thing, there is no need to await before return. If you can await within a function, it is async and returns a promise anyway, so just return the promise from buildRelevantDB:
return buildRelevantDB();
Finally, you can drop database directly. No need to iterate over all databases to pick one you want to drop:
await client.db(<db name to drop>).dropDatabase();

How to implement different middlewares for different monk's Manager instance?

I'm using monk (https://automattic.github.io/monk/) in order to do mongodb data manipulation.
Monk has a middleware mechanism, and we can simply invoke addMiddleware to add as many middleware as needed (https://automattic.github.io/monk/docs/middlewares.html)
It works well until I create two manager (https://automattic.github.io/monk/docs/manager/) instance. The middlewares of the two manager seems to polute each other.
So, I create a simple test in order to make sure the hypothesis.
Here is the test:
let db = monk('mongodb://localhost/test')
let softDb = monk('mongodb://localhost/other')
console.error(db._collectionOptions.middlewares === softDb._collectionOptions.middlewares)
And as my prior hypothesis, it yield true. Eventhough db and softDb are different objects, they seems to share the same _collectionOptions.middlewares. Probably monk's developer implement singleton pattern.
My question is: how could I make this softDb and db have different set of middlewares?
Thanks in advance.
I find a workaround. This is not an officially approved solution and I'm still seeking for a better solution. So, if any of you find a better solution, I'll approve your answer.
Since both softDb and db share the same set of middleware, then I deep-clone and re-assign both db._collectionOptions.middlewares. For deep-cloning purpose, I use this package: https://www.npmjs.com/package/clone.
The solution looks as follow:
let clone = require('clone')
let db = monk('mongodb://localhost/test')
db._collectionOptions.middlewares = clone(db._collectionOptions.middlewares)
let softDb = monk('mongodb://localhost/other')
softDb._collectionOptions.middlewares = clone(softDb._collectionOptions.middlewares)
// Here I can add different set of middlewares to db and softDb
// The middlewares of both manager will be independent to each other
console.error(db._collectionOptions.middlewares === softDb._collectionOptions.middlewares)
// Now, this will yield `false`
It is important to note that you have to copy the middlewares immediately after creating a manager instance.
I'm aware that some people might hate this solution. So, rather than down-voting this answer, please provide a better solution instead.

Node js - overall structure of a program

Hope you are well.
I need your help to understand how to logically organize a program in Node JS to avoid repetition of code given its asynchronous property (as a beginner ..). Let's take an example to make it easier to explain.
One has some data in a mongo database (let's say a list of name). This list of name can be access thanks to the function readData as below
function readData(criteriaRead,callback) {
mongodb.stuff(..)
callback('data read on mongodb')
}
I have two actions in my program: one is to print out the list of name, the other is to check if a name is in the list.
For the first case, it's simple, I just need to have a function like this
function printout(data) {console.log(data)}
and to do this
readData(criteriaRead,printout)
In the second case, let's say I have a function like this
checkIfInIt(array,dataToCheck) {//stuff to check console.log(results)}
Now, I have an issue because if I doreadData(criteriaRead,checkIfInIt) it won't work as checkIfInIt requires two parameters.
I would need a function like this
function readDataBis(criteriaRead,dataToCheck,callback) {
mongodb.stuff(..)
callback('data read on Mongodb','dataToCheck')
}
and then readDataBis(criteriaRead,dataToCheck,checkIfInIt) would work but I have a huge repetition in my code.
How to avoid that?
There are several solutions for this type of issue, but here's an easy one for your case
Declare your function with the three parameters as such
function readData(callback, criteriaRead, dataToCheck) { ...
Inside, check if dataToCheck is undefined, and continue with the flow of the second function you had if that's the case. (Otherwise just do the read function)
Call them like so
readData(callback, criteriaRead); // Third parameter missing, will be undefined
readData(callback, criteriaRead, dataToCheck);
You could also pass in an object for your parameters like this, if it would make it simpler
function readData(callback, params) { ...
And call like this
readData(callback, { criteriaRead: criteriaRead, dataToCheck: dataToCheck });

javascript variable value in asynchronous function called simultaneously by multiple clients

I am putting up a web server in node.js,
in particular I am developing a module for orders management.
the module is wrapped inside an anonymous function
(function(){})();
if the "insertOrder" function I declare the variable order like this:
var order = {
user_id: '',
address_id: '',
payed: false,
accepted: false,
shipped: false
};
Then it gets populated with the values "returned" from the asynchronous functions i am calling that interact with the database.
This application is going to be used simultaneously by multiple clients.
Now, assuming that two users want to make an order, is the variable going to be re-initialized to the starting object every time the function get's called, overwriting the changes made during the first execution? Or is a context going to be spawned every time a client makes a call to the server?
I know this is not the case for node.js but still can't figure this one
out.
I.E.
is the variable value of the previous iteration gonna be kept somehow and used until the end of the first function call or lost as soon as the function gets called again?
Thank you very much.
EDIT: further explaination of the problem.
The user_id is is going to be used to retrieve the address that the order is going to be shipped to. A wrong user_id is going to result in the item shipped to the wrong address
If var order = { ... } is inside the insertOrder function, then every time the insertOrder function is called order will be reinitialized. The scope is isolated, so there should not be any mingling of local variables even in an asynchronous situation.
jsFiddle

How to think asynchronously with nodejs?

I just started developing nodejs. I'm confused to use async model. I believe there is a way to turn most of SYNC use cases into ASYNC way. Example, by SYNC, we load some data and wait until it returns then show them to user; by ASYNC, we load data and return, just tell the user data will be presented later. I can understand why ASYNC is used in this scenario.
But here I have a use case. I'm building an web app, allowing user to place a order (buying something). Before saving the order data into db, I want to put some user data together with order data (I'm using document NoSql db by the way). So I think by SYNC, after I get order data, I make a SYNC call to database and wait for its returned user data. After I get returned data, integrate them together and ingest into db.
I think there might be an issue if I make ASYNC call to db to query user data because user data may be returned after I save data to db. And that's not what I want.
So in this case, how can I do this thing ASYNCHRONOUSLY?
Couple of things here. First, if your application already has the user data (the user is already logged in), then this information should be stored in session so you don't have to access the DB. If you are allowing the user to register at the time of purchase, you would simply want to pass a callback function that handles saving the order into your call that saves the user data. Without knowing specifically what your code looks like, something like this is what you would be looking for.
function saveOrder(userData, orderData, callback) {
// save the user data to the DB
db.save(userData, function(rec) {
// if you need to add the user ID or something to the order...
orderData.userId = rec.id; // this would be dependent on your DB of choice
// save the order data to the DB
db.save(orderData, callback);
});
}
Sync code goes something like this. step by step - one after other. There can be ifs and loops (for) etc. all of us get it.
fetchUserDataFromDB();
integrateOrderDataAndUserData();
updateOrderData();
Think of async programming with nodejs as event driven. Like UI programming - code (function) is executed when an event occurs. E.g. On click event - framework calls back registered clickHandler.
nodejs async programming can also be thought on these lines. When db query (async) execution completes, your callback is called. When order data is updated, your callback is called. The above code goes something like this:
function nodejsOrderHandler(req,res)
{
var orderData;
db.queryAsync(..., onqueryasync);
function onqueryasync(userdata)
{
// integrate user data with order data
db.update(updateParams, onorderudpate);
}
function onorderupdate(e, r)
{
// handler error
write response.
}
}
javascript closure provides the way to keep state in variables across functions.
There is certainly much more to async programming and there are helper modules that help with basic constructs like chain, parallel, join etc as you write more involved async code. but this probably gives you a quick idea.

Resources