node.js / postgres... asynchronous problem? - postgresql

I have a list of types: ["type1", "type2, "type3"], on which I loop. For each type:
- create / execute a query to get the items of a the current type
- run another function when the query if done ( on('end') ).
for ( var i=0; i<types.length; i++ ){
type = types[i];
value = "test";
...
// ADD ITEM: QUERY
var query = client.query(...); // QUERY ITEMS OF A THE CURRENT TYPE
// ADD ITEM: ERROR CHECKING
query.on("error", function (err) {
...
});
with ({ t: type, v: value }) { // I HAD TO DO THAT SO THAT ALL THE TYPES ARE TAKEN INTO ACCOUNT
query.on('end', function() {
my_function(t, v);
});
}
}
my_function is like:
function my_function(type, value){
console.log(type + ',' + value); // CORRECT (I CAN SEE ALL THE TYPES BEEING LISTED)
// QUERY OTHER STUFF BASED ON THE VALUE OF "type"
var query = client.query(...);
// MAIN STUFF
query.on('row', function(row){
console.log(type + ',' + value); // THIS DOES NOT WORK ANYMORE... ONLY THE LAST TYPE IS TAKEN INTO ACCOUNT WHERE I EXPECT TO GET THIS MESSAGE FOR EACH TYPES.
...
}
// FINALIZE
query.on('end', function(){
...
}
}
I guess this is linked to asynchronous process... but cannot figure out where the error is.
UPDATE
I have updated my loop so it looks like:
for ( var i=0; i<types.length; i++ ){
type = types[i];
value = "test";
...
// ADD ITEM: QUERY
var query = client.query(...); // QUERY ITEMS OF A THE CURRENT TYPE
// ADD ITEM: ERROR CHECKING
query.on("error", function (err) {
...
});
// MAIN STUFF GOES HERE
(function(t, v) { query.on("end", function() {
console.log(t + ',' + v); // OK, I CAN SEE THIS DISPLAYED FOR EACH TYPE
my_function(t, v);
}); })(type, value);
}
I modified my_function so it looks like:
function my_function(type, value){
console.log(type + ',' + value); // OK, I CAN SEE THIS DISPLAYED FOR EACH TYPE
// QUERY OTHER STUFF BASED ON THE VALUE OF "type"
var query = client.query(...);
// MAIN STUFF
(function(t, v) {
query.on("row", function(row) {
console.log('TEST:' + t + ',' + v); // KO, I CAN ONLY SEE THIS DISPLAYED FOR ONE TYPE
}); })(type, value);
// FINALIZE
query.on('end', function(){
...
}
}

with ({ t: type, v: value }) { // I HAD TO DO THAT SO THAT ALL THE TYPES ARE TAKEN INTO ACCOUNT
query.on('end', function() {
my_function(t, v);
});
}
Is broken. What you want is this
query.on("end", my_function.bind(null, type, value));
Function.prototype.bind allows you to bind parameters to a function.
Never use with. An alternative that also works would be :
(function(t, v) {
query.on("end", function() { my_function(t, v); });
})(type, value);

Related

Mongoose query hook, modify all incoming dates

I have a MEAN app, and I have lots of dates stored in the mongodb, the clock is changing in the UK on 27th October, so all of the dates stored in the db need to have one hour added.
I wouldn't like to loop through all docs in the db and add an hour to its dates, I'd prefer that to be dynamic, so I'm trying to implement a query hook to check each date on the incoming docs and the timezone offset attached in the doc to add/subtract the timezone offset.
The problem is that I'd like the loop on the incoming docs to dynamically identify where the dates are, which will be sometimes on the root of the doc, buried inside an object or an array, which I'm having a hard time to model the loop to check all of that.
I'm using functions to check if the incoming object is a date, an object or an array, but mongoose is adding a bunch of objects/functions that are hindering the operation, so I'm getting RangeError: Maximum call stack size exceeded
const config = require('../config/environment');
var UK_TIMEZONE_GMT_OFFSET = config.UK_TIMEZONE_GMT_OFFSET || 0; // should check if the offset is the same as in each document in the db, if it doesn't match then I'll subtract the offset.
const mongoose = require('mongoose');
const exec = mongoose.Query.prototype.exec;
const Q = require('Q');
mongoose.Query.prototype.exec = function () {
var d = Q.defer();
var p = exec.apply(this, arguments);
if (p) p.then(function (rs) {
var mod;
try {
mod = fixDates(rs);
} catch (err) {
console.log(err, rs)
};
d.resolve(mod);
}, d.reject);
return d.promise;
}
function fixDates(rs) {
if (isArray(rs)) {
rs.forEach(function (r, i) {
rs[i] = fixDates(r);
})
} else if (isObject(rs)) {
for (var key in rs) {
var val = rs[key];
if (isObject(val)) console.log('isobject', isObject(val), val);
// the '_id' of the document is considered an object
// also some stuff like Schema, NativeCollection ... are objects as well
// rs[key] = fixDates(val); // this line causes problems
}
} else if (isDate(rs)) {
// modify the date if necessary ..
// Check the timezone offset of the document vs the global timezone offset
of the system then add/subtract the difference
}
return rs;
}
function isDate(obj) {
return obj instanceof Date;
}
function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
function isArray(obj) {
return Array.isArray(obj);
}
I need better methodology to deal with mongoose returned object in order to loop through all documents and deeply find the dates and modify them.
I updated the recurring function to invoke model.toObject if exists and apply the update the date fields, while still keeping the model class
function recur(rs, docTimezone) {
if (isArray(rs)) {
rs.forEach(function (r, i) {
rs[i] = recur(r, docTimezone);
})
} else if (isObject(rs)) {
if (rs.toObject) {
return updateModel(rs, recur(rs.toObject(), docTimezone))
}
Object.keys(rs).forEach(function (key) {
var val = rs[key];
rs[key] = recur(val, docTimezone !== undefined ? docTimezone : rs.usedTimezoneGMTOffset); // usedTimezoneGMTOffset if exists on the doc
})
} else if (isDate(rs)) {
// modify the date if necessary ..
rs = alignTimezone(rs, docTimezone);
}
return rs;
}
function updateModel(model, updates) {
var key, val;
for (key in updates) {
val = updates[key];
if (val !== void 0 && key !== '_id') {
model[key] = val;
}
}
return model;
}

Resolving Promise Angular 2

I have the following problem.
In a function I have a promise as a return type. This function is in the class Hierarchy.
updateNodeValues(entity: String, data: {}): Promise<any>{
let jsonBody = JSON.stringify(data);
let url = environment.endpointCore + '/api/' + entity + '/' + data['id'];
return this.http.put(url, jsonBody, this.options)
.toPromise()
.then(response => {
return response;
})
.catch(this.handleError);
}
This function is in class node.
onSubmit(): void{
var currentForm = this.form.value;
var entityName = this.inflection.classify(this.node.type).toLowerCase();
var requiredData = {};
for(var i = 0; i < this.formItems.length; i++){
this.formItems[i].value = currentForm[Object.keys(currentForm)[i]];
}
for(var i=0; i<this.formItems.length; i++){
requiredData[this.globalService.camelize(this.formItems[i].label)] = this.formItems[i].value
}
Promise.resolve(this.hierarchyService.updateNodeValues(entityName, requiredData)).then(response => {
alert(response.ok);
if(response.ok){
this.globalService.showSuccessMessage('Values updated');
this.refreshGui(requiredData);
}
});
this.editMode = false;
}
The problem is that when i try to resolve promise and invoke this.refreshGui(requireddata) nothing is happening. I have read about how the fat arrow is preserving the 'context' of this, and I do not understand why invoking this method is not doing anything, while invoking successMessage produces expected outcome.
The method that I am invoking looks like this, and it is also in the class node.
private refreshGui(data: {}){
this._node.data = data;
this.objectProperties = new Array();
this.nodeChildren = new Array();
for (var property in data) {
var propertyValue = data[property];
if (propertyValue instanceof Array) {
this.nodeChildren.push({label: property, value: "total: ".concat(propertyValue.length.toString())});
} else {
this.objectProperties.push({label: property, value: propertyValue});
}
}
}
The solution that I found to be working was to implement custom event. The problem was that within the async callback resolution, the context of what this is would "get lost". The fat arrow enabled me to invoke class method with this, but the properties within the would be "lost". Because of this reason I have took the logic from the method, and put it in the callback part and set expected and needed results in some variable. This variable was passed to my custom event and set to class variable in the custom event handler appropriately.

Resolving a Promise in Protractor

Strange issue I don't understand yet when trying to resolve (fulfill) my promise in Protractor.
Something is very wrong with the line deferred.fulfill(rowData);, as it's NOT returning the row data as I would expect.
In other words, rowData.count() in the lower function is fine, but when returned row.count() is failing.
this.gridFunction = function (summaryData){
var rowData = getGridRowByText(gridRows, name, text);
rowData.then(function (row) {
// ** THROWS ERROR ** TypeError: row.count is not a function
expect(row.count()).toEqual(3);
row.map(function (cell) {
// iterate cell contents, compare with "summaryData"
});
});
}
function getGridRowByText(gridRows, grid, text) {
var deferred = protractor.promise.defer();
var parentId = getParId();
parentId.getAttribute("id").then(function (parentId) {
// i.e. jquery $('.grid-wrapper.fluid-wrapper #rowId_21')
var sel = '.grid-wrapper.fluid-wrapper #' + parentId;
var rowData = element(by.css(sel)).all(by.repeater('column in vm.sourceData.columns'));
// EXPECT SUCCESSFULL !!!
expect(rowData.count()).toEqual(19);
deferred.fulfill(rowData);
});
return deferred.promise;
};
Main question: am I NOT properly returning the fulfilled promise with the rowData object ?
* UPDATE *
My final solution :
It doesn't actually solve my original problem of working with the Protractor Promise, but rather just a redesign of the logic.
this.gridFunction = function (targetRowText){
var result = gridRows.all(by.cssContainingText('span', targetRowText)).first();
var parentId = result.all(by.xpath("./ancestor::div[starts-with(#id, 'rowId')]"));
parentId.getAttribute("id").then(function (parentId) {
console.log(' (ROW-ID: ', parentId);
// further iterations here...
}
}
thank you,
Bob
You don't actually need a "deferred" object here. Just return the promise from the function:
function getGridRowByText(gridRows, grid, text) {
var parentId = getParId();
return parentId.getAttribute("id").then(function (parentId) {
var sel = '.grid-wrapper.fluid-wrapper #' + parentId;
return element(by.css(sel)).all(by.repeater('column in vm.sourceData.columns'));
});
};
Usage:
var rowData = getGridRowByText(gridRows, name, text);
expect(rowData.count()).toEqual(3);
Or, if further processing needed in the getgridRowByText() function:
function getGridRowByText(gridRows, grid, text) {
var parentId = getParId();
return parentId.getAttribute("id").then(function (parentId) {
var sel = '.grid-wrapper.fluid-wrapper #' + parentId;
var rowData = element(by.css(sel)).all(by.repeater('column in vm.sourceData.columns'));
// further processing here
expect(rowData.count()).toEqual(19);
return rowData;
});
};

How to pass a test if expect fails

I have this code
it('This should pass anyway', function (done) {
testObj.testIt(regStr);
});
testObj
this.testIt = function (regStr) {
selector.count().then(function (orgCount) {
for (var curr = 0; curr < count; curr++) {
checkField(curr, regStr);
}
});
};
function checkField(curr, regStr) {
selector.get(curr).all(by.tagName('li')).get(0).getInnerHtml().then(function (text) {
expect(text).to.match(regStr, curr + '#ERR');
});
}
If one of these expects get a failure, test fails. How can i handle this? I mean - can i somehow count passed and failed expect()ations and return it? or, at least, dont let test break on first error.
I've tried try-catch, but nothing good happened.
it('This should pass anyway', function (done) {
try {
testObj.testIt(regStr);
} catch (e) {
console.log('#err' + e);
}
});
And then i wanted to use done(), but havent found any examples to do the similar. Can u please help me?
Sry for my english
UPD
You can return either null or a string from checkField(), join them up, and expect the array to be empty:
this.testIt = function (regStr) {
selector.count().then(function (orgCount) {
var errors = [];
for (var curr = 0; curr < orgCount; curr++) {
var e = checkField(curr, regStr);
if (e) { errors.push(e); }
}
assert.equal(0, errors.length, errors);
});
};
A cleaner approach would be to use map() to collect the data into an array:
var data = selector.map(function (elm) {
return elm.element(by.tagName('li')).getText();
});
expect(data).toEqual(["test1", "test2", "test3"]);

MongoDB/Mongoose: Can't put simplest MapReduce to work

Hello all I'm trying to do is to get the count of each distinct departmentType:
fnMap = function() {
emit(this.departments.departmentType, {typeCount:1} );
}
fnReduce = function(key, values) {
var result = {typeCount: 0};
values.forEach(function(value) {
result.typeCount += value.brandCount;
});
return result;
};
var command = {
mapreduce : "clients",
query : {"departments.departmentType": {$exists: true}},
map : fnMap.toString(),
reduce : fnReduce.toString(),
//sort: {"departments.departmentType":1},
out: {inline: 1}
};
mongoose.connection.db.executeDbCommand(command, function(err, dbres) {
});
When executing the command, dbres.documents[0].results only contains 1 item with the total number of departmentTypes, instead of several items one for each departmentType with its count.
Any ideas what am I doing wrong?
Also, when I uncomment the SORT line, I get error "db assertion failure: could not create cursor over...", I believe the field name is written correctly.
Mongoose v3 has now a Model.mapreduce() function (see doc).
The full example shown is:
var o = {};
o.map = function () { emit(this.name, 1) }
o.reduce = function (k, vals) { return vals.length }
o.out = { replace: 'createdCollectionNameForResults' }
o.verbose = true;
User.mapReduce(o, function (err, model, stats) {
console.log('map reduce took %d ms', stats.processtime)
model.find().where('value').gt(10).exec(function (err, docs) {
console.log(docs);
});
})
The problem with count i believe is because in your fnReduce() function you are summit the results instead of displaying them in an array.
You can use:
db.clients.distinct("departments.departmentType")
That will give an array with all the distinct departmentType values.
There were two problems in your map/reduce. One is brandCount in reduce rather than typeCount. But more importantly, you are trying to emit once per document, when you need to emit once per department array element. Corrected (and slightly simplified) code:
> fnMap = function () {
this.departments.forEach(
function (d) {
emit(d.departmentType, 1);
}
);
}
> fnReduce = function (key, values) {
var result = 0;
values.forEach(
function (value) {result += value;});
return result;
}