Closed over value not picked up in Mongoose Map function - node.js

I am trying to create a dynamic map function - ie the use an arbitrary field to aggregate on. I thought I would be able to use a closure for this but it does not work - I get an error stating blah is not defined.
My test code -
o.map = (function(){
var blah = 'skill';
var mapIt = function() {
for (var idx = 0; idx < this[blah].length; idx++) {
var key = this.skill[idx];
var val = 1;
emit(key, val);
}
}
return mapIt
})()
Regards,
Sean

So the map function is actually getting sent over the wire via function toString (in source code form) to mongodb for execution inside mongodb itself (not node). Thus, this doesn't work. This is what the scope option is for. Any data you need to supply as context/arguments/scope to the map/reduce job needs to be set in the scope object.

Looks like you have to set scope manually -
o.scope = {'blah': blah};

Related

Asynch, await, callback - What exactly is the execution context of a callback function?

So, I have previous programming experience in numerous languages: assembly(s), c, c++, basic(s), page description language(s), etc.
I am currently learning node, js, puppeteer and have run into something I can not quite make sense of.
I have read various things that seem explain various limitations of the callback execution context, but I have not found anything specifically that explains this.
I am attempting to call functions or reference variables (defined in the current module) from within a callback function. I have tried a number of variations, I have tried with variables of assorted types defined in assorted locations - but this one demonstrates the problem and I expect the solution to this will be the solution for all the variants. I am getting errors that "aFunction is not defined".
Why can't the callback function see the globally defined function "aFunction()"
function aFunction(parm)
{
return something;
}
(async () => {
let pages = await browser.pages();
// array of browser titles
var titles = [];
// iterate pages extracting each title using forloop because foreach can not contain await.
for (let index = 0; index < pages.length; index++) {
const pagex = pages[index]
const title = await pagex.title();
titles.push(title);
}
//chopped and edited a bunch to keep it simple
// here is the home of my problem.
foundAt = 0;
const container = await pages[foundAt].evaluate(() => {
let elements = $('.classiwant').toArray();
// this is the failing call
var x = aFunction(something);
for (i = 0; i < elements.length; i++) {
$(elements[i]).click();
}
})

Calling a method from another method in Node.js

I am trying to make some modifications to a Node.js app that I forked, what I am trying to do is call a function within another one.
I have attempted a crack at this by simply calling the method as follows but I'm not really that familiar with Node.js so I'm not sure I'm doing it right.
'use strict';
/*
* initalize the class
*/
function MessageBot(bot, user, cache) {
this.bot = bot;
this.user = user;
this.cache = cache;
}
/*
* perform commands
*/
MessageBot.prototype.librarySearch = function(searchText) {
var self = this;
// call SOS function - this is the bit that doesn't work
MessageBot.prototype.callSos(somenumbervar);
}
MessageBot.prototype.callSos = function(number) {
// do something else here
var self = this;
var commandList = self.cache.get('commandList');
}
Remember that this ostensibly inherits the prototype (directly or indirectly). Therefore, you can just do this.callSos(somenumbervar).
If you want to reach the method through the prototype, you have to tell it what this is. With your current code, this in callSos() will be MessageBot.prototype -- certainly not what you want. So you can also do MessageBot.prototype.callSos.call(this, somenumbervar).
Which approach to take depends on how dynamic you want your objects to be. For example, if you want consumers of MessageBot to be able to "override" callSos() by installing their own method, then you should take the first approach (this.callSos()) as it will look up callSos in the object's inheritance chain. This process will only reach the method you've installed on the prototype if the method hasn't been overridden. IMO this is the approach you should take unless you have a very good reason not to.
See this example, which demonstrates how the two approaches differ regarding overriding, while also showing that both work with regards to passing the correct this value (since both methods can retrieve the expected value from this.data):
function ClassA(data) {
this.data = data;
}
ClassA.prototype.foo = function () {
console.log("this.bar() returned: " + this.bar());
console.log("ClassA.prototype.bar.call(this) returned: " + ClassA.prototype.bar.call(this));
};
ClassA.prototype.bar = function () {
return 'in the ClassA prototype, our data is ' + this.data;
};
console.log('--obj1-- (no override)');
var obj1 = new ClassA(3.14);
obj1.foo();
console.log('--obj2-- (override)');
var obj2 = new ClassA(42);
obj2.bar = function () {
return 'in an overriding method, our data is ' + this.data;
};
obj2.foo();

Chaining nested asynchronous finds with Node.js monk and MongoDB

Using Node.js monk and MongoDB, I want to mimic a table join:
Do a find on collection A
For each result X, do a find in collection B, and update X
Return updated list of results
The asynchronous nature of database commands in monk is giving me trouble.
This is my initial code. It doesn't work because the second call to find returns a promise immediately,
and the results in xs are sent in the response before they can be updated.
var db = require('monk')('localhost/mydb');
db.get('collection').find({}, function(e,xs) {
xs.forEach(function(x){
coll_b.find({a_id:x._id}, function(e,bs) {
a['bs'] = bs;
});
});
res.json({'results':as});
});
I feel like I should use promise chaining here, but I cannot figure out how to do it.
Any help would be greatly appreciated.
I think I solved it in this way, inspired by this answer:
var db = require('monk')('localhost/mydb');
// Initial find
db.get('collection').find({}, function(e,xs) {
// Get inner finds as a list of functions which return promises
var tasks = xs.map(function(x){
return function() {
return coll_b.find({a_id:x._id}, function(e,bs) {
a['bs'] = bs;
});
}
});
// Chain tasks together
var p = tasks[0](); // start the first one
for(var i = 1; i < tasks.length; i++) p = p.then(tasks[i]);
// After all tasks are done, output results
p.then(function(_x){
res.json({'results':xs});
});
});
I still feel like this code could be minimised by using chain(), but at least this works as expected.
Note: I realise that performing a second find for each result is not necessarily efficient, but that's not my concern here.

How to use chrome.storage in a chrome extension using a variable's value as the key name?

I'm trying to use chrome's local storage / sync storage (chrome.storage) for an extension, to store data entries, for many different entries. I can't seem to figure out the correct syntax for it. I only want to simply store the information as strings. I have searched and can't find anything yet that works.
This is what works for me at the moment using the normal localStorage technique:
var imageName = "Red Cat 5";
var myDescription = "A nice kitty";
localStorage.setItem (imageName, myDescription);
console.log(localStorage[imageName]);
This works and lets me set the key from an existing variable.
How can I do it using chrome.storage.local.set?
I have been trying this without any success:
var imageName = "Red Cat 5";
var myDescription = "A nice kitty";
chrome.storage.local.set({imageName: myDescription}, function()
{console.log('success?');});
chrome.storage.local.set({imageName: myDescription}, function()
{chrome.storage.local.get(imageName, function(r){console.log(r.imageName);});});
Any help is much appreciated. Thanks!
----- UPDATE BELOW -----
Thanks for the explanation with the code. I hope it helps anyone else. There seems to be little information available on doing this! Your answer helped me come up with this:
var nameOne = "al";
var nameTwo = "bob";
var nameThree = "carl";
var nameFour = "dan";
var dataObj = {};
dataObj[nameOne] = nameTwo;
dataObj[nameThree] = nameFour;
storage.set(dataObj);
storage.get(dataObj, function(result)
{
console.log(result[nameOne]);
console.log(result[nameThree]);
});
Use a named object, not an anonymous object, and set a member variable using square brackets:
var dataObj = {};
dataObj[imageName] = myDescription;
chrome.storage.local.set(dataObj, function() { /*...*/ });
It's not the most elegant looking code, but it's the only way to do it.
In ES6, a slightly shorter approach is to use an object literal with a dynamic property name:
chrome.storage.local.set({
[imageName]: myDescription
}, function() { /*...*/ });
the set method accepts object items, and an optional callback, the get accepts optional string or array or object keys and if you passed the first argument, you got to pass the second argument as well.
Example:
// To set
chrome.storage.local.set({'testKey':'Test Value'});
// To get
chrome.storage.local.get('testKey', function(data){
console.log(data);
// logs out "Object {testKey: "Test Value"}"
})

copy values from sqlite3 db to a global array in node.js

i have use the node_sqlite3 module and i have try the following example:
var sqlite = require('sqlite3').verbose();
var db = new sqlite.Database("/var/www/signals/db/app3.db");
var matrixSignals = new Array();
var i;
i = 0;
db.all('SELECT * FROM tbl_signals', function(err,rows){
rows.forEach(function(row) {
matrixSignals[i] = new Object();
matrixSignals[i].signalID = row.signalID;
matrixSignals[i].connID = row.connID;
i++;
});
db.close();
console.log('1:' + matrixSignals.length);
});
console.log('2:' + matrixSignals.length);
in the console output 1 the length is correct but in the console output 2 the length is always 0. How i will set the matrixSignals as a global variable?
The reason this doesn't work has to do with how Node.js operates in general. In node, all code is executed asynchronously; at the time you are logging output 2, matrixSignals still has a length of 0. This is because after you fire off the database query, the code continues to execute. Logging output 1 is only executed after the database query has finished, which is why it returns the correct results.
For this reason, the simple answer to your question is that there is no way to set matrixSignals to be a global variable. If all of your logic is truly dependent on the values in that array, then your logic should be in the callback to the database call - so that it only executes that code once the data has been retrieved from the database. If you just want the syntax to be cleaner, you could potentially use something like node-promise (https://github.com/kriszyp/node-promise) but I think that's probably more effort than its worth.

Resources