I have an array of objects with addresses with the four field, field1 is the current location(store) and the rest are address info.
My problem is how do I make sure that I have an result with key:value (store:cords) at the end so I know which store has which cords.
This is the code I have written so far:
var NodeGeocoder = require('node-geocoder')
const csv=require('csvtojson')
const csvFilePath='location.csv'
csv({noheader:true})
.fromFile(csvFilePath)
.then((jsonObj)=>{
convertAddressesToCoords(jsonObj, function(coords){
console.log('converting finished.');
console.log(coords.length);
console.log(coords);
});
});
function convertAddressesToCoords(addresses, callback) {
var coords = [];
var options = {
provider: 'google',
httpAdapter: 'https',
apiKey: 'xxxxxxxxxxxxxxxxxxxxxxxx',
formatter: null
};
var geocoder = NodeGeocoder(options);
for(var i = 0; i < addresses.length; i++) {
currAddress = addresses[i].field2 + ' ' + addresses[i].field3 + ' ' + addresses[i].field4;
geocoder.geocode(currAddress, function(err, results) {
coords.push(results);
if(coords.length == addresses.length) {
if( typeof callback == 'function' ) {
callback(coords);
}
}
});
}
}
Current code works fine, I can get for each address the coordinates but the problem is at the end I dont know which store has which coords, since the google geocoding cal is asynchronous so I cannot find a way how to make this work.
Looping over an array and then calling a callback at the end will work, but its hard to read and can cause some problems, therefore i would use Promises here:
const getCoords = geocoder => address => new Promise((resolve, reject) => {
address = [address.field2, address.field3, address.field4].join(" ");
geocoder.geocode(address, function(err, coords) {
if(err) reject(err) else resolve({ address, coords });
});
});
So now to get a certain coordinate, you can just do:
getCoords(NodeGeocoder(options))({field2: "Unknown Location"})
.then(({ address, coords }) => console.log(adress + " is at " + coords));
Now gettig that from your csv is easy too:
csv({noheader:true})
.fromFile(csvFilePath)
.then(data => Promise.all(data.map(getCoords(NodeGeocoder(options)))
.then(positions => {
//...
})
And positions is now an array of coord / address pairs.
If you want to go with your code instead, just add
const currAddress = addresses[i].field2 + ' ' + addresses[i].field3 + ' ' + addresses[i].field4;
to keep it in the closure, then you can push it to the results:
coords.push({ coords: results, address: currAddress });
Use Promise. Something like
function convertAddressesToCoords(addresses, callback) {
var coords = [];
var options = {
provider: 'google',
httpAdapter: 'https',
apiKey: 'xxxxxxxxxxxxxxxxxxxxxxxx',
formatter: null
};
var geocoder = NodeGeocoder(options);
return Promise.all(
adresses.map(
(address)=>new Promise((resolve,reject)=>{
geocoder.geocode(addresses[i].field2 +
' ' + addresses[i].field3 +
' ' + addresses[i].field4,
function(err, results) {
if (err) {
return reject(err);
}
return resolve(results);
}
);
})
)
);
}
Then you can use it like
csv({noheader:true})
.fromFile(csvFilePath)
.then((jsonObj)=>convertAddressesToCoords(addresses))
.then((coords)=>console.log(coords))
Related
I have a function that connect to a web service in SOAP. Unfortunately the web service only support a very limited connections. I have an array of items to search in the web service, if i do a for or a foreach loop, the 70% of cases complete with no error, but in the 30% the web service response a error. This occurs when the max connections is overflow. This happens because the loop is no waiting the response of the webservice and the loop cotinues creating a lot of connections.
Here's my code:
var promiseArray = [];
for (var i = 0; i < result.length; i++) {
let m = result[i].id
let xml = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">' +
'<soapenv:Header/>' +
'<soapenv:Body>' +
'<tem:EjecutarConsultaXML>' +
'<!--Optional:-->' +
'<tem:pvstrxmlParametros>' +
'<![CDATA[' +
'<Consulta><NombreConexion>USERNAME</NombreConexion>' +
'<IdConsulta>QUERY</IdConsulta>' +
'<Parametros>' +
'<doc>' + m + '</doc>' +
'</Parametros>' +
'</Consulta>' +
']]>' +
'</tem:pvstrxmlParametros>' +
'</tem:EjecutarConsultaXML>' +
'</soapenv:Body>' +
'</soapenv:Envelope>';
const options = {
explicitArray: true
};
promiseArray.push(new Promise(async(resolve, reject) => {
await axios.post(url, xml, {
headers: {
'Content-Type': 'text/xml;charset=UTF-8'
}
})
.then((data) => {
xml2js.parseString(data.data, options, (err, result) => {
var temp = (result['soap:Envelope']['soap:Body'][0]['EjecutarConsultaXMLResponse'][0]['EjecutarConsultaXMLResult'][0]['diffgr:diffgram'][0]['NewDataSet'][0]['Resultado'])
resolve({
doc: m,
state: temp[0].f430_ind_estado[0]
})
});
})
.catch((err) => {
console.log(err)
});
}))
}
res.send(await Promise.all(promiseArray))
There are several issues with your code within the call to promiseArray.push().
There is no need to create a new Promise() since axios already provides one
This is actually and antipattern
There is no need for async/await in that call for the same reason.
Mixing Promises and functions that use callbacks usually doesn't turn out too well
You have no error checking in your code if the XML parser fails
The option object is not required as explicitArray: true is the default
Changes:
Removed all the extra/uneeded Promise code
Replaced xml2js.parseString with xml2js.parseStringPromise
Changed resolve to return
Since you're simply console.log() the error, removed unecessary boilerplate
Everything else is OK as written. Please let me know if I've missed something.
promiseArray.push(
axios.post(url, xml, {
headers: {
'Content-Type': 'text/xml;charset=UTF-8'
}
})
.then(data=>data.data)
.then(xml2js.parseStringPromise)
.then(result => {
var temp = result['soap:Envelope']['soap:Body'][0]['EjecutarConsultaXMLResponse'][0]['EjecutarConsultaXMLResult'][0]['diffgr:diffgram'][0]['NewDataSet'][0]['Resultado'];
return {
doc: m,
state: temp[0].f430_ind_estado[0]
};
});
.catch(console.log)
);
Just do it one by one, using async/await to do that, this means you have to use parseStringPromise instead.
var response = [];
for (var i = 0; i < result.length; i++) {
let m = result[i].id
let xml = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">' +
'<soapenv:Header/>' +
'<soapenv:Body>' +
'<tem:EjecutarConsultaXML>' +
'<!--Optional:-->' +
'<tem:pvstrxmlParametros>' +
'<![CDATA[' +
'<Consulta><NombreConexion>USERNAME</NombreConexion>' +
'<IdConsulta>QUERY</IdConsulta>' +
'<Parametros>' +
'<doc>' + m + '</doc>' +
'</Parametros>' +
'</Consulta>' +
']]>' +
'</tem:pvstrxmlParametros>' +
'</tem:EjecutarConsultaXML>' +
'</soapenv:Body>' +
'</soapenv:Envelope>';
const options = {
explicitArray: true
};
try {
var { data } = await axios.post(url, xml, { // extract data from data.data
headers: {
'Content-Type': 'text/xml;charset=UTF-8'
}
})
var xmlObject = await xml2js.parseStringPromise(data)
var temp = (xmlObject['soap:Envelope']['soap:Body'][0]['EjecutarConsultaXMLResponse'][0]['EjecutarConsultaXMLResult'][0]['diffgr:diffgram'][0]['NewDataSet'][0]['Resultado'])
response.push({
doc: m,
state: temp[0].f430_ind_estado[0]
}) // push item to result array
} catch (error) {
console.log(error);
}
}
res.send(result) // send the result to client
My goal is to wrap MySQL queries, pass the parameters to a function and another function does the MySQL job, returning the results.
Here's my code so far:
//mysql lib
var mysql = require('mysql');
//database credentials
exports.pool = mysql.createPool({
connectionLimit: 50,
host: 'localhost',
user: 'root',
password: 'password',
database: '_app',
debug: false
});
//my wrapper =(
var returnResultset = exports.returnResultset = function (qry) {
return new Promise(function (resolve, reject) {
try {
mysql_.pool.getConnection(function (err, connection) {
if (err) {
console.log("Error on function returnResultset - MYSQL ERROR: " + err);
return reject(err);
}
connection.query(qry, [], function (error, results, fields) {
connection.release();
if (error) {
console.log("Error on function returnResultset - MYSQL ERROR: " + error);
return reject(error);
}
return resolve(results);
});
});
}
catch (e) {
console.log('error:' + e);
}
});
};
//wrapper function for testing purposes
var selectOneField = exports.selectOneField = function (tbl, field, pk, pkval) {
var qry_ = "SELECT " + field + " FROM " + tbl + " WHERE " + pk + " = '" + pkval + "'";
returnResultset(qry_).then(function (results) {
return results;
}, function (error) {
console.log("Error: " + error);
})
};
//...and on another page I want to be able to receive the results from the function above:
var isExpired = exports.isExpired = function (cod) {
var rtf = db_.selectOneField('view_expiredusers', 'cod', 'cod', cod);
console.log(rtf);
return rtf;
};
The code above returns undefined. I can't get to make this function working properly.
I have tried console.log(results). The query works like a charm. Only thing I can't get to work is to catch the result from an external function.
Any thoughts? Thanks in advance!
You should return the promise and chain it inside isExpired function.
//wrapper function for testing purposes
var selectOneField = exports.selectOneField = function (tbl, field, pk, pkval) {
var qry_ = "SELECT " + field + " FROM " + tbl + " WHERE " + pk + " = '" + pkval + "'";
return returnResultset(qry_);
};
//...and on another page I want to be able to receive the results from the function above:
var isExpired = exports.isExpired = function (cod) {
return db_.selectOneField('view_expiredusers', 'cod', 'cod', cod)
};
When you call the isExpired in other files you should use the then method of the promise and return the results. do it as follows
var cod_customer = 1;
var isexpired;
isExpired(cod_customer).then(function (results) {
isexpired = results;
console.log(isexpired);
}, function (error) {
console.log("Error: " + error);
});
you are not returning the promise in selectOneField function it must return the promise and also you cant simply do
rtf = db_.selectOneField('view_expiredusers', 'cod', 'cod', cod);
.you will have to use async-await or then
Must be handled this way
//wrapper function for testing purposes
var selectOneField = exports.selectOneField = function (tbl, field, pk, pkval) {
var qry_ = "SELECT " + field + " FROM " + tbl + " WHERE " + pk + " = '" + pkval + "'";
return returnResultset(qry_).then(function (results) {
return results;
}).catch(error) {
console.log("Error: " + error);
})
};
//...and on another page I want to be able to receive the results from the function above:
var isExpired = exports.isExpired = function (cod) {
var rtf = db_.selectOneField('view_expiredusers', 'cod', 'cod', cod).then(rtf => {
console.log(rtf);
return rtf;
});
};
var latLon = "40.8,-77.8"; //Lat/lon
var cityCode = ""; //City code
var cityName = "";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + weatherKey + "&q=" + latLon);
//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
//12 hour forecast Conditions Vars
//5 day forecast conditions Vars
//Get city code
http.get(latLongCityCodeURL, (resp) => {
var that = this;
resp.on("data", (chunk) => {
var result = JSON.parse(chunk);
var cityCode = result.Key;
var cityName = result.EnglishName;
console.log(cityCode + " " + cityName);
that.cityName = cityName;
that.cityCode = cityCode;
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
console.log(cityCode + " " + cityName);
So my issue is, I am making an http request using require('http'), what I want to do is parse the data and store it in my global variables so that I can use them for other requests. I have tried using var that=this and I have tried just assigning my global variables to the data. I am not sure how to do it, i just keep getting undefined. I know it has something to do with ASYNC and also something to do with the scope. Please help
You can save your result to a variable at various levels of scope.. just remember that most i/o calls in Node.js are asynchronous.
Here's an example:
var latLon = "40.8,-77.8"; //Lat/lon
var cityCode = ""; //City code
var cityName = "";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + weatherKey + "&q=" + latLon);
//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
var savedResult = null;
//Get city code
http.get(latLongCityCodeURL, (resp) => {
var jsonData = '';
resp.on("data", (chunk) => {
jsonData += chunk;
});
resp.on("end", () => {
savedResult = JSON.parse(jsonData);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
// Display saved result once available.
setTimeout(displaySavedResult, 2000);
function displaySavedResult() {
if (!savedResult) {
console.log('Last result is null!');
} else {
console.log('Last result: City Code: ' + savedResult.Key + " Name" + savedResult.EnglishName);
console.log('Last result (all properties): ', JSON.stringify(savedResult, null, 2));
}
}
You can use Promise to make http request, here is code that may help you
const httpGet = url => {
return new Promise((resolve, reject) => {
http.get(url, res => {
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => {
try {
body = JSON.parse(body);
} catch (err) {
reject(new Error(err));
}
resolve({
cityCode: body.Key,
cityName: body.EnglishName
});
});
}).on('error', reject);
});
};
httpGet(latLongCityCodeURL).then(data => {
console.log(data.cityCode + " " + data.cityName);
}).catch(err => console.log('Got error ', err));
var latLon = "40.8,-77.8";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + weatherKey + "&q=" + latLon);
//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
//12 hour forecast Conditions Vars
//5 day forecast conditions Vars
//Get city code
function getCityCode(latLongCityCodeURL){
return new Promise((resolve, reject) => {
http.get(latLongCityCodeURL, (resp) => {
resp.on("data", (chunk) => {
var result = JSON.parse(chunk);
var cityCode = result.Key;
var cityName = result.EnglishName;
resolve({cityCode, cityName});
});
}).on("error", (err) => {
reject(err);
console.log("Error: " + err.message);
});
})
}
getCityCode(latLongCityCodeURL)
.then((result) => {
console.log(result.cityCode, result.cityName)
}).catch((err) => console.log(err))
Another way is to use async-await API interface which is supported in node 8.
async function getCityCode(latLongCityCodeURL){
const result = await http.get(latLongCityCodeURL, (resp) => {
resp.on("data", (chunk) => {
var result = JSON.parse(chunk);
var cityCode = result.Key;
var cityName = result.EnglishName;
return {cityCode, cityName};
});
}).on("error", (err) => {
return err;
console.log("Error: " + err.message);
});
return result;
}
getCityCode(latLongCityCodeURL)
.then((res) => {
console.log(res.cityCode, res.cityName)
})
Your code block runs in a synchronized way and console.log part hits just after http.get call. The thing is http.get is an async function and its callback part will be called future ticks of NodeJS when your response has arrived.
var http = require('http');
var latLon = "40.8,-77.8"; //Lat/lon
var cityCode = ""; //City code
var cityName = "";
var latLongCityCodeURL = ("http://dataservice.accuweather.com/locations/v1/cities/geoposition/search?apikey=" + "fmKWSgaG5EAA0diCP2lSREEOYG6PC5q9" + "&q=" + latLon);
var that;
//Current Conditions Vars
var ccWeatherText = ""; //Text for weather at location
var ccTemp = 0; //Degrees Farenheit
var ccIcon = 0; //weather icon number https://developer.accuweather.com/weather-icons
var ccURL = "test"; //URL for get
//12 hour forecast Conditions Vars
//5 day forecast conditions Vars
//Get city code
getWeather = url => {
return new Promise((resolve, reject) => {
http.get(url, res => {
let body = '';
res.on('data', chunk => resolve(JSON.parse(chunk)));
}).on('error', reject);
});
};
getWeather(latLongCityCodeURL).then( weather => {
console.log(weather.Key + " " + weather.EnglishName);
})
You must wait for the response, then send the other requests or log into. The scope isn't really the issue here, it's the order of operations that you're missing. You define a variable, then wait for a HTTP request, meanwhile you're immediately logging a value, then the request finishes (or times out or other error), and you can log again the value - at which point you see data.
In other words, to fix the issue, other requests must be made from within a function of resp.on("end", which says when you've finished getting all data chunks
And by "within", the code can still be in a separate method, outside those brackets, but you must call that particular function from within that response body.
You should pass such asynchronous returned data through parameter variables, not update some external, global state in most cases
Background
I'm just learning node js and have run into a situation where I need to make up to two back to back calls to my redis database, depending on the results of the first query.
The code I have right now works.. but it's very ugly. I wrote it this way because I'm not good with async 'stuff'. But now that it's working... I want to refactor in a way that is readable and of course, in a way that works.
Here's the code, along with an explanation of what I'm trying to do:
Code
router.get('/:ip', function(req, res, next) {
var ip = req.params.ip;
if ( ! validate_ipV4(ip) ) {
res.status(400).send("Invalid IP");
return;
}
var three_octets = extract_octets(ip, 3);
var two_octets = extract_octets(ip, 2);
if (debug) { winston.log('info', 'emergency router.get() attempting hget using :' + three_octets); }
redis.hget("e:" + three_octets, 'ccid', function (e, d) {
if (e){
winston.log('error', 'hget using key: ' + octets + ' failed with error: ' + e);
res.status(500).send("Database query failed");
return;
}
if (d) {
if (debug) { winston.log('info', 'HGET query using ip: ' + ip + ' returning data: ' + d ) };
res.status(200).send(JSON.stringify(d));
return;
} else {
//retry using only 2 octets
redis.hget("e:" + two_octets, 'ccid', function (e, d) {
if (e){
winston.log('error', 'hget using key: ' + octets + ' failed with error: ' + e);
res.status(500).send("Database query failed");
return;
}
if (d) {
if (debug) { winston.log('info', 'HGET query using ip: ' + ip + ' returning data: ' + d ) };
res.status(200).send(JSON.stringify(d));
return;
}else {
res.status(404).send("Unknown IP");
return;
}
});//end hget
}
});//end hget
});
Explanation:
Accept an ip address as input. 10.1.1.1
Try to query the database for a hash that matches the first three octets. For example: "hget e:10.1.1 ccid"
If i have a match, I can return the db results and exit. otherwise, if the query came back with no results, then I need to retry using the first two octets: "hget e:10.1 ccid"
if that returns nothing, then i can exit the GET method.
ASYNC
I know that there is an async module... and i've tried to use MAP before. But from what I understand, you cannot force MAP to exit early.
So for example, if I did something like this:
async.map(ipOctets, hash_iterator, function (e, r) {
})
where ipOctets was an array with both 10.1.1. and 10.1 in it, if the first query found a match in the database, there's no way I can stop it from running the second query.
Can you give me some pointers on how to improve this code so that I don't have to repeat the same code twice?
I also thought of putting the redis.hget call into a separate function... like this:
var hash_get = function (hash, key, field) {
if (debug) { winston.log('info', 'hash_get() invoked with : ' + hash + ' ' + key + ' ' + field);}
redis.hget(hash + key, field, function (e, d) {
if (e){
winston.log('hash_get() failed with: ' + e);
return 500;
}
if (d) {
return (d);
}else {
return 404;
}
});
}
But again, I'm not sure how to do the following in a synchronous way:
call it from router.get
check results
repeat if necessary
Sorry for the noob questions.. but any pointers would be appreciated.
EDIT 1
Since posting, i found this http://caolan.github.io/async/docs.html#some
and I'm currently testing to see if this will work for me.
But please comment if you have some suggestions!
Thanks.
You could use the waterfall method which cascades functions into each other. I really only like to use it when I have 3 nested callbacks or more, otherwise I don't feel like it simplifies it enough.
After looking at your code and seeing how much you can reuse I think I would use async.until though.
router.get('/:ip', function(req, res, next) {
var ip = req.params.ip;
if (!validate_ipV4(ip)) {
res.status(400).send("Invalid IP");
return;
}
let success = false;
let octets_num = 3;
async.until(
// Test this for each iteration
function() { return success == true || octets < 2}, // You would adjust the test to set limits
// Do this until above
function(callback) {
let octets = extract_octets(ip, octets_num);
redis.hget("e:" + octets, 'ccid', function(e, d) {
if(e) {
winston.log('error', 'hget using key: ' + octets + ' failed with error: ' + e);
res.status(500).send("Database query failed");
}
else if(id) {
if (debug) { winston.log('info', 'HGET query using ip: ' + ip + ' returning data: ' + d ) };
res.status(200).send(JSON.stringify(d));
success == true;
}
else
{
octects_num--;
}
callback(null);
});
}
// After success or not found within 3 or 2 octets
function(err, result) {
if(success == false) {
res.status(404).send("Unknown IP");
return;
}
}
...
}
This permits you to reuse the same chunk of code with minimal variation. It's rough and I don't have the rest of your application to test it, but I hope you get the idea.
Maybe like this:
router.get('/:ip', function (req, res, next) {
var ip = req.params.ip;
if (!validate_ipV4(ip)) {
res.status(400).send("Invalid IP");
return;
}
var three_octets = extract_octets(ip, 3);
var two_octets = extract_octets(ip, 2);
//if (debug) { winston.log('info', 'emergency router.get() attempting hget using :' + three_octets); }
var hash = "e:"
var field = 'ccid';
async.waterfall([
function (callback) {
hash_get(hash, three_octets, field, callback)
},
function (d, callback) {
if (d) {
callback(null, d);
return;
}
hash_get(hash, two_octets, field, callback)
}
], function (err, result) {
if (err) {
winston.log('error', err.message);
res.status(err.status).send(err.message);
return;
}
if (result) {
res.status(200).send(JSON.stringify(result));
return;
}
res.status(404).send("Unknown IP");
return;
});
});
var hash_get = function (hash, key, field, callback) {
if (debug) { winston.log('info', 'hash_get() invoked with : ' + hash + ' ' + key + ' ' + field); }
redis.hget(hash + key, field, function (e, d) {
if (e) {
callback({ status: 500, message: 'hget using key: ' + key + ' failed with error: ' + e });
return;
}
if (d) {
if (debug) { winston.log('info', 'HGET query using ip: ' + ip + ' returning data: ' + d) };
callback(null, d);
} else {
callback(null, null);
}
});
}
Check Async.waterfall() for this as you want the result of one callback into another (http://caolan.github.io/async/docs.html#waterfall).
Async.map could not be used as it will hit both the octets at the same time
which you don't want .
Code
router.get('/:ip', function(req, res, next) {
var ip = req.params.ip;
if ( ! validate_ipV4(ip) ) {
res.status(400).send("Invalid IP");
return;
}
var three_octets = extract_octets(ip, 3);
var two_octets = extract_octets(ip, 2);
var redis_hget=function(octets){
redis.hget("e:"+octets,'ccid',function(e,d){
callback(null,d)
})
}
if (debug) { winston.log('info', 'emergency router.get() attempting hget using :' + three_octets); }
async.waterfall([
function(callback){
redis_hget(three_octets)
},
function(d,callback){
if(d)
callback(d)
else
redis_hget(two_octets)
}
],function(err,result){
if(err){
winston.log('error', 'hget using key: ' + octets + ' failed with error: ' + e);
res.status(500).send("Database query failed");
return;
}else{
if(result){
if (debug) { winston.log('info', 'HGET query using ip: ' + ip + ' returning data: ' + d ) };
res.status(200).send(JSON.stringify(d));
return;
}else{
res.status(404).send("Unknown IP");
return;
}
}
})
}
This is very early in my Node and JavaScript learning. Ideally, what I am attempting to do is create a small module querying a specific type of rest endpoint and returning a specific feature based on an attribute query. The module is correctly logging out the result, but I am struggling to get the .findById function to return this result. Although aware it has something to do with how the callbacks are working, I am not experienced enough to be able to sort it out yet. Any help, advice and direction towards explaning the solution is greatly appreciated.
// import modules
var restler = require('restler');
// utility for padding zeros so the queries work
function padZeros(number, size) {
var string = number + "";
while (string.length < size) string = "0" + string;
return string;
}
// create feature service object
var FeatureService = function (url, fields) {
// save the parameters
this.restEndpoint = url;
this.fields = fields;
var self = this;
this.findById = function (idField, value, padZeroLength) {
var options = {
query: {
where: idField + '=\'' + padZeros(value, padZeroLength) + '\'',
outFields: this.fields,
f: "pjson"
},
parsers: 'parsers.json'
};
var url = this.restEndpoint + '/query';
restler.get(url, options).on('complete', function(result){
if (result instanceof Error){
console.log('Error:', result.message);
} else {
console.log(result); // this log result works
self.feature = JSON.parse(result);
}
});
return self.feature;
};
};
var restEndpoint = 'http://services.arcgis.com/SgB3dZDkkUxpEHxu/ArcGIS/rest/services/aw_accesses_20140712b/FeatureServer/1';
var fields = 'nameRiver,nameSection,nameSectionCommon,difficulty,diffMax';
var putins = new FeatureService(restEndpoint, fields);
var feature = putins.findById('awid_string', 1143, 8);
console.log(feature); // this log result does not
//console.log('River: ' + feature.attributes.nameRiver);
//console.log('Section: ' + feature.attributes.nameSection + ' (' + feature.attributes.nameSectionCommon + ')');
//console.log('Difficulty: ' + feature.attributes.difficulty);
So, I sorted out how to insert a callback from a previous thread. It appears it is just passed in as a variable and called with expected parameters. However, I now wonder if there is a better way to accept parameters, possibly in the form of options. Any advice in this regard?
// import modules
var restler = require('restler');
// utility for padding zeros so the queries work
function padZeros(number, size) {
var string = number + "";
while (string.length < size) string = "0" + string;
return string;
}
// create feature service object
var FeatureService = function (url, fields) {
// save the parameters
this.restEndpoint = url;
this.fields = fields;
var self = this;
// find and return single feature by a unique value
this.findById = function (idField, value, padZeroLength, callback) {
// query options for
var options = {
query: {
where: idField + '=\'' + padZeros(value, padZeroLength) + '\'',
outFields: this.fields,
f: "pjson"
},
parsers: 'parsers.json'
};
var url = this.restEndpoint + '/query';
restler.get(url, options)
.on('success', function(data, response){
var dataObj = JSON.parse(data).features[0];
console.log(dataObj);
callback(dataObj);
})
.on('fail', function(data, response){
console.log('Error:', data.message);
});
return self.feature;
};
};
var restEndpoint = 'http://services.arcgis.com/SgB3dZDkkUxpEHxu/ArcGIS/rest/services/aw_accesses_20140712b/FeatureServer/1';
var fields = 'nameRiver,nameSection,nameSectionCommon,difficulty,diffMax';
var putins = new FeatureService(restEndpoint, fields);
putins.findById('awid_string', 1143, 8, function(dataObject){
console.log('River: ' + dataObject.attributes.nameRiver);
console.log('Section: ' + dataObject.attributes.nameSection + ' (' + dataObject.attributes.nameSectionCommon + ')');
console.log('Difficulty: ' + dataObject.attributes.difficulty);
});