Calling forEach on Dictionary in Node.js - javascript

I am trying to loop over a Firebase reference. It works but for some reason the forEach loop runs one more time than there are objects in the reference. This causes the Promise.all() function to fail and the whole promise to fail. Here is my code. I have no Idea what I'm doing wrong.
return userReceiptMetrics.child(userID).child('postItemIDs').orderByChild('itemID').equalTo(oldProductID).once('value', function(oldSnapshot) {
var oldPostItemIDs = [];
var metrics = oldSnapshot.val()
if (oldSnapshot.val() != null) {
return Promise.all(oldSnapshot.forEach(function(record) {
console.log(record.val());
var oldKey = record.key;
var newKey = oldKey.replace(oldProductID, newProductID);
var data = record.val();
oldPostItemIDs.push(oldKey);
data.itemID = newProductID;
updateObject['userReceiptMetrics/'+userID+'/postItemIDs/'+newKey] = data;
updateObject['userReceiptMetrics/'+userID+'/postItemIDs/'+oldKey] = null;
})).then(function() {
return Promise.all(oldPostItemIDs.map(function(oldPostItemID) {
return userReceiptMetrics.child(userID).child('postItems').child(oldPostItemID).then(function(oldPostItem) {
var oldKey = oldPostItem.key
var newKey = oldKey.replace(oldProductID, newProductID)
var data = record.val()
updateObject['userReceiptMetrics/'+userID+'/postItems/'+newKey] = data;
updateObject['userReceiptMetrics/'+userID+'/postItems/'+oldKey] = null;
progress(38);
});
}))
}).catch(function(error) {
console.log('fudge louise');
});
}
});
Here is the console output:
App listening on port 8080
Press Ctrl+C to quit.
FIREBASE WARNING: Using an unspecified index. Consider adding ".indexOn": "_state" at /productUpdateQueue/tasks to your security rules for better performance
FIREBASE WARNING: Using an unspecified index. Consider adding ".indexOn": "_state" at /productUpdateQueue/tasks to your security rules for better performance
{ date: '2016-12-21 22:05:03',
itemID: 'Macys-EReceipts-MENS HOSIERY-Size(No size provided)-Color(No color provided)-786888403743',
postID: '-KZbmaThvxNmrvHwh_mc' }
{ date: '2016-12-21 22:05:03',
itemID: 'Macys-EReceipts-MENS HOSIERY-Size(No size provided)-Color(No color provided)-786888403743',
postID: '-KZbxAUcwzcP28C91EZA' }
FIREBASE WARNING: Using an unspecified index. Consider adding ".indexOn": "itemID" at /userReceiptMetrics/HeQST8hSkoPUmkBiVDR0tpSPo0x2/postItemIDs to your security rules for better performance
fudge louise

So it looks like the Promise.all() call failed because there is an empty object at the end of the list of objects. Here is the corrected code.
return userReceiptMetrics.child(userID).child('postItemIDs').orderByChild('itemID').equalTo(oldProductID).once('value', function(oldSnapshot) {
var oldPostItemIDs = [];
var metrics = oldSnapshot.val()
if (oldSnapshot.val() != null) {
return Promise.all(oldSnapshot.forEach(function(record) {
console.log(record.val());
var oldKey = record.key;
var newKey = oldKey.replace(oldProductID, newProductID);
var data = record.val();
oldPostItemIDs.push(oldKey);
data.itemID = newProductID;
updateObject['userReceiptMetrics/'+userID+'/postItemIDs/'+newKey] = data;
updateObject['userReceiptMetrics/'+userID+'/postItemIDs/'+oldKey] = null;
})).then(function() {
return oldPostItemIDs.map(function(oldPostItemID) {
return userReceiptMetrics.child(userID).child('postItems').child(oldPostItemID).then(function(oldPostItem) {
var oldKey = oldPostItem.key
var newKey = oldKey.replace(oldProductID, newProductID)
var data = record.val()
updateObject['userReceiptMetrics/'+userID+'/postItems/'+newKey] = data;
updateObject['userReceiptMetrics/'+userID+'/postItems/'+oldKey] = null;
progress(38);
});
})
}).catch(function(error) {
console.log('fudge louise');
});
}
});

Related

Javascript: Why object is not getting initialise on new api call however the string variable is?

I may be missing something basic as why is it happening.
GET: example.com/users
//gives all data
GET: example.com/users?status=1
//gives data with status = 1
GET: example.com/users // this does not work
gives same data as pervious API condition with status=1
On third hit, self.whereObj is not initialising to default empty object instead it takes previous value of {'status' = '1'}, however self.page and self.limit is taking default value if no query parameter is provided in query string.
example.com/users?limit=3, // takes override to 3 form default value of 5
example.com/users // self.limit takes default 5 and this works fine
So my question is why the self.limit (simple string variable) is initialising however self.whereObj is not ?
var Bookshelf = require('../../dbconfig').bookshelf;
Bookshelf.Collection = Bookshelf.Collection.extend({
limit: 5,
page: 1,
whereObj: {}
myFetch: function (query_params,expectedWhereFields) {
var self = this;
var whereObj = self.whereObj ; // this is not initializing
// var whereObj = {}; this is initialising
var page = self.page;
var limit = self.limit; //this is not showing nay initialisation error
for (var x in query_params) {
if (expectedWhereFields.includes(x)) {
whereObj[x] = query_params[x];
}
if (x === 'page') {
page = query_params[x];
}
if (x === 'limit') {
limit = query_params[x];
}
}
var offset = (page - 1) * limit;
function fetch() {
return self.constructor.forge()
.query({where: whereObj})
.query(function (qb) {
qb.offset(offset).limit(limit);
})
.then(function (collection) {
return collection;
})
.catch(function (err) {
return err
});
}
return new fetch();
}
});
module.exports = Bookshelf;
UPDATED
service.js
var Model = require('./../models/Users');
var express = require('express');
var listUsers = function (query_params, callback) {
var expectedWhereFields = ["type", "status", "name"];
Model.Users
.forge()
.myFetch(query_params, expectedWhereFields)
.then(function (collection) {
return callback(null, collection);
})
.catch(function (err) {
return callback(err, null);
});
};
module.exports = {
listUsers: listUsers
};
model/Users.js
var Bookshelf = require('../../dbconfig').bookshelf;
var Base = require('./base');
// Users model
var User = Bookshelf.Model.extend({
tableName: 'user_table'
});
var Users = Bookshelf.Collection.extend({
model: User
});
module.exports = {
User: User,
Users: Users
};
So my question is why the self.limit (simple string variable) is initialising however self.whereObj is not?
Because objects are reference values. When you set var whereObj = self.whereObj;, both refer to the same object, and when you copy the query parameters into the object properties you are effectively writing into your defaults instance. This does not happen with primitive values such as strings - they don't have mutable properties.

Return a promise with a model and a service

I have an error with a promise.
I try to use angular-tree-dnd , but I have a problem with a promise.
from my controller :
project.getAll().then(function(results) {
var projects = results;
$scope.results = [];
angular.forEach(projects, function(result) {
$scope.results.push(project.build(result));
});
return $scope.results;
});
$scope.tree_data = $TreeDnDConvert.line2tree($scope.results, 'id', 'ParentId');
my model :
var Project = function(properties) {
// Model
this.description = null;
this.file = null;
this.name = null;
this.ParentId = null;
this.path = null;
angular.extend(this, properties);
};
Project.prototype.setModel = function(obj) {
angular.extend(this, obj);
};
Project.prototype.getAll = function() {
return ProjectService.getAll();
};
Project.prototype.build = function(data) {
var project = new Project(data);
return project;
};
my service (with $webSql) :
this.getAll = function(params) {
var projects = [];
return db.selectAll('projects').then(function(results) {
for (var i = 0; i < results.rows.length; i++) {
projects.push(results.rows.item(i));
}
return projects;
});
};
I have no error but my home is empty.
I tried this :
project.getAll().then(function(results) {
var projects = results;
$scope.results = [];
angular.forEach(projects, function(result) {
$scope.results.push(project.build(result));
});
return $scope.results;
}).then(function(array) {
$scope.tree_data = $TreeDnDConvert.line2tree(array, 'id', 'ParentId');
});
But I have this error :
angular.min.js:13550 TypeError: Cannot read property '__children__' of undefined
at Object._$initConvert.line2tree (ng-tree-dnd.js:1456)
I think that my array is empty
I suspect that this is the root of your problem:
"ParentId":"null"
Both of the items on your tree have a ParentId with the string value "null". This probably means that the dnd library is looking for a node with an ID of "null", finding nothing, and trying to access the __children__ property on that undefined value.
The solution: fix the data in your DB so that the parent IDs are actually null values and not the value "null".
Good evening)
Please set a breakpoint, or use debugger, alert, console.log with JSON.stringify(array), before your last operation:
$scope.tree_data = $TreeDnDConvert.line2tree(array, 'id', 'ParentId')
(last version with then and error).
I think you got correct array and trouble not in promises, but in line2tree syntax.

IndexedDB: How to use multiple indexes along with 'multiEntry: true'

If I got these data in an indexedDB:
{
name:"Ray",
age:20,
tags:["apple","banana","beer"]
}
{
name:"Scott",
age:25,
tags:["beer"]
}
{
name:"Marc",
age:28,
tags:["mongo","jenkins"]
}
Then I want to find persons who have tag 'beer' and ordered the result by age, what should I do?
According to this article http://www.raymondcamden.com/2012/8/10/Searching-for-array-elements-in-IndexedDB, 'multiEntry: true' should be applied to query array field, but it'll show an error if I use it with multiple indexes. So what query can achieve the goal? Thanks.
In onupgradeneeded callback function:
store.createIndex('tagsIndex','tags', {multiEntry: true});
In your query section, do
var tx = db.transaction('store');
var tagsIndex = tx.objectStore('store').index('tagsIndex');
var beerQuery = tagsIndex.openCursor(IDBKeyRange.only('beer'));
var people = [];
beerQuery.onsuccess = function(event) {
var cursor = this.result;
if(!cursor) return;
people.push(cursor.value);
cursor.continue();
};
tx.oncomplete = function() {
onGetPeopleWhoLikeBeerSortedByAgeAsc(people.sort(function(p1, p2) {
if(p1.age > p2.age) return -1;
if(p1.age == p2.age) return 0;
return 1;
}));
};

IndexedDB via Lawnchair gets larger with every save

I am using Lawnchair.js on a mobile app I am building at work targeting iOS, Android, and Windows phones. My question is I have a relatively simple function(see below), that reads data from an object and saves it in the indexeddb database. It's about 4MB of data and on the first go round when I inspect in Internet explorer(via internet options), I can see the database is about 7MB. If I reload the page and re-run the same function with the same data, it increases to 14MB and then 20MB. Im using the same keys so my understanding is that this should just update the record but it's almost as if it's just inserting all new records every time. I have also had similar behavior using Lawnchair on mobile safari using websql adapter. Has anyone seen this before or have any suggestions as to why this might be ??.
The following code is from a function I am using to populate the database.
populateDatabase: function(database,callback) {
'use strict';
var key;
try {
for(key in MasterData){
if(MasterData.hasOwnProperty(key)){
var itemInfo = DataConfig.checkForDataUpdates[DataConfig.keyMap[key]];
database.save({key:itemInfo["name"],hash:itemInfo["version"],url:itemInfo["url"],data:MasterData[key]});
}
}
callback(true);
} catch(e){
callback(false);
}
}
MasterData is the large data file and itemInfo contains the key name, a hash that is later used to check an api for updates, and the relative url of where to update from. After I create the database I pass it into this function and then pass back true if the inserts are successful and false otherwise.
As previously mentioned, I have seen similar issues in iOS where calling database.save() was allocating a lot of memory but not releasing it and eventually causing a crash if it populated the database and then tried to update some records. Removing Lawnchair from the equation has kept it from crashing but it is still allocating a lot of memory when saving data. Not sure if this is normal for persistent storage on mobile devices, a bug in Lawnchair, or me being a noob and doing something terribly wrong but I could use some pointers on this as well as why indexeddb just keeps getting larger and larger on every save (at least during initial testing in IE10)??
EDIT: Source Code for indexed-db adapter is here:
https://github.com/brianleroux/lawnchair/blob/master/src/adapters/indexed-db.js
and here is the code for the save function I am using:
save:function(obj, callback) {
var self = this;
if(!this.store) {
this.waiting.push(function() {
this.save(obj, callback);
});
return;
}
var objs = (this.isArray(obj) ? obj : [obj]).map(function(o){if(!o.key) { o.key = self.uuid()} return o})
var win = function (e) {
if (callback) { self.lambda(callback).call(self, self.isArray(obj) ? objs : objs[0] ) }
};
var trans = this.db.transaction(this.record, READ_WRITE);
var store = trans.objectStore(this.record);
for (var i = 0; i < objs.length; i++) {
var o = objs[i];
store.put(o, o.key);
}
store.transaction.oncomplete = win;
store.transaction.onabort = fail;
return this;
},
When Creating a new instance, Lawnchair uses the init function from the indexed-db adapter which is the following.
init:function(options, callback) {
this.idb = getIDB();
this.waiting = [];
this.useAutoIncrement = useAutoIncrement();
var request = this.idb.open(this.name, STORE_VERSION);
var self = this;
var cb = self.fn(self.name, callback);
if (cb && typeof cb != 'function') throw 'callback not valid';
var win = function() {
// manually clean up event handlers on request; this helps on chrome
request.onupgradeneeded = request.onsuccess = request.error = null;
if(cb) return cb.call(self, self);
};
var upgrade = function(from, to) {
// don't try to migrate dbs, just recreate
try {
self.db.deleteObjectStore('teststore'); // old adapter
} catch (e1) { /* ignore */ }
try {
self.db.deleteObjectStore(self.record);
} catch (e2) { /* ignore */ }
// ok, create object store.
var params = {};
if (self.useAutoIncrement) { params.autoIncrement = true; }
self.db.createObjectStore(self.record, params);
self.store = true;
};
request.onupgradeneeded = function(event) {
self.db = request.result;
self.transaction = request.transaction;
upgrade(event.oldVersion, event.newVersion);
// will end up in onsuccess callback
};
request.onsuccess = function(event) {
self.db = event.target.result;
if(self.db.version != (''+STORE_VERSION)) {
// DEPRECATED API: modern implementations will fire the
// upgradeneeded event instead.
var oldVersion = self.db.version;
var setVrequest = self.db.setVersion(''+STORE_VERSION);
// onsuccess is the only place we can create Object Stores
setVrequest.onsuccess = function(event) {
var transaction = setVrequest.result;
setVrequest.onsuccess = setVrequest.onerror = null;
// can't upgrade w/o versionchange transaction.
upgrade(oldVersion, STORE_VERSION);
transaction.oncomplete = function() {
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
};
};
setVrequest.onerror = function(e) {
setVrequest.onsuccess = setVrequest.onerror = null;
console.error("Failed to create objectstore " + e);
fail(e);
};
} else {
self.store = true;
for (var i = 0; i < self.waiting.length; i++) {
self.waiting[i].call(self);
}
self.waiting = [];
win();
}
}
request.onerror = function(ev) {
if (request.errorCode === getIDBDatabaseException().VERSION_ERR) {
// xxx blow it away
self.idb.deleteDatabase(self.name);
// try it again.
return self.init(options, callback);
}
console.error('Failed to open database');
};
},
I think you keep adding data instead of updating the present data.
Can you provide some more information about the configuration of the store. Are you using an inline or external key? If it's an internal what is the keypath.

Convert a text from text file to array with fs [node js]

I have a txt file contains:
{"date":"2013/06/26","statement":"insert","nombre":1}
{"date":"2013/06/26","statement":"insert","nombre":1}
{"date":"2013/06/26","statement":"select","nombre":4}
how I can convert the contents of the text file as array such as:
statement = [
{"date":"2013/06/26","statement":"insert","nombre":1},
{"date":"2013/06/26","statement":"insert","nombre":1},
{"date":"2013/06/26","statement":"select","nombre":4}, ];
I use the fs module node js. Thanks
Sorry
I will explain more detailed:
I have an array :
st = [
{"date":"2013/06/26","statement":"insert","nombre":1},
{"date":"2013/06/26","statement":"insert","nombre":5},
{"date":"2013/06/26","statement":"select","nombre":4},
];
if I use this code :
var arr = new LINQ(st)
.OrderBy(function(x) {return x.nombre;})
.Select(function(x) {return x.statement;})
.ToArray();
I get the result I want.
insert select insert
but the problem my data is in a text file.
any suggestion and thanks again.
There is no reason for not to do your file parser yourself. This will work on any size of a file:
var fs = require('fs');
var fileStream = fs.createReadStream('file.txt');
var data = "";
fileStream.on('readable', function() {
//this functions reads chunks of data and emits newLine event when \n is found
data += fileStream.read();
while( data.indexOf('\n') >= 0 ){
fileStream.emit('newLine', data.substring(0,data.indexOf('\n')));
data = data.substring(data.indexOf('\n')+1);
}
});
fileStream.on('end', function() {
//this functions sends to newLine event the last chunk of data and tells it
//that the file has ended
fileStream.emit('newLine', data , true);
});
var statement = [];
fileStream.on('newLine',function(line_of_text, end_of_file){
//this is the code where you handle each line
// line_of_text = string which contains one line
// end_of_file = true if the end of file has been reached
statement.push( JSON.parse(line_of_text) );
if(end_of_file){
console.dir(statement);
//here you have your statement object ready
}
});
If it's a small file, you might get away with something like this:
// specifying the encoding means you don't have to do `.toString()`
var arrayOfThings = fs.readFileSync("./file", "utf8").trim().split(/[\r\n]+/g).map(function(line) {
// this try/catch will make it so we just return null
// for any lines that don't parse successfully, instead
// of throwing an error.
try {
return JSON.parse(line);
} catch (e) {
return null;
}
// this .filter() removes anything that didn't parse correctly
}).filter(function(object) {
return !!object;
});
If it's larger, you might want to consider reading it in line-by-line using any one of the many modules on npm for consuming lines from a stream.
Wanna see how to do it with streams? Let's see how we do it with streams. This isn't a practical example, but it's fun anyway!
var stream = require("stream"),
fs = require("fs");
var LineReader = function LineReader(options) {
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
this._buffer = "";
};
LineReader.prototype = Object.create(stream.Transform.prototype, {constructor: {value: LineReader}});
LineReader.prototype._transform = function _transform(input, encoding, done) {
if (Buffer.isBuffer(input)) {
input = input.toString("utf8");
}
this._buffer += input;
var lines = this._buffer.split(/[\r\n]+/);
this._buffer = lines.pop();
for (var i=0;i<lines.length;++i) {
this.push(lines[i]);
}
return done();
};
LineReader.prototype._flush = function _flush(done) {
if (this._buffer.length) {
this.push(this._buffer);
}
return done();
};
var JSONParser = function JSONParser(options) {
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
};
JSONParser.prototype = Object.create(stream.Transform.prototype, {constructor: {value: JSONParser}});
JSONParser.prototype._transform = function _transform(input, encoding, done) {
try {
input = JSON.parse(input);
} catch (e) {
return done(e);
}
this.push(input);
return done();
};
var Collector = function Collector(options) {
options = options || {};
options.objectMode = true;
stream.Transform.call(this, options);
this._entries = [];
};
Collector.prototype = Object.create(stream.Transform.prototype, {constructor: {value: Collector}});
Collector.prototype._transform = function _transform(input, encoding, done) {
this._entries.push(input);
return done();
};
Collector.prototype._flush = function _flush(done) {
this.push(this._entries);
return done();
};
fs.createReadStream("./file").pipe(new LineReader()).pipe(new JSONParser()).pipe(new Collector()).on("readable", function() {
var results = this.read();
console.log(results);
});
fs.readFileSync("myfile.txt").toString().split(/[\r\n]/)
This gets your each line as a string
You can then use UnderscoreJS or your own for loop to apply the JSON.parse("your json string") method to each element of the array.
var arr = fs.readFileSync('mytxtfile', 'utf-8').split('\n')
I think this is the simplest way of creating an array from your text file

Categories

Resources