How can I include another parameter in my node.js web service? - javascript

Sorry if it's a tedious question, I'm just at the very beginning of understanding how JS/node.js works with post/get messages.
I'm writing a web service to query data from mongodb database based on the latitude/longitude and distance of the user. I wrote in my frontend code a following function:
// Take query parameters and incorporate into a JSON queryBody
$scope.queryUsersFromLast24Hours = function(){
// Assemble Query Body
queryBody = {
longitude: parseFloat($scope.formData.longitude),
latitude: parseFloat($scope.formData.latitude),
distance: parseFloat($scope.formData.distance)
};
// Post the queryBody to the /query POST route to retrieve the filtered results
$http.post('/queryUsersFromLast24Hours', queryBody)
// Store the filtered results in queryResults
.success(function(queryResults){
console.log(JSON.stringify(queryBody));
// Pass the filtered results to the Google Map Service and refresh the map
gservice.refresh(queryBody.latitude, queryBody.longitude, queryResults);
// Count the number of records retrieved for the panel-footer
$scope.queryCount = queryResults.length;
})
.error(function(queryResults){
console.log('Error ' + queryResults);
})
};
and it calls the method /queryUsersFromLast24Hours from my backed code, which looks as follows:
// Retrieves JSON records for all users who meet a certain set of query conditions
app.post('/queryUsersFromLast24Hours/', function(req, res){
// Grab all of the query parameters from the body.
var lat = req.body.latitude;
var long = req.body.longitude;
var distance = req.body.distance;
var dateNow = new Date(); //right now
// Opens a generic Mongoose Query. Depending on the post body we will...
var query = User.find({});
// ...include filter by Max Distance (converting miles to meters)
if(distance){
// Using MongoDB's geospatial querying features. (Note how coordinates are set [long, lat]
query = query.where('location').near({ center: {type: 'Point', coordinates: [long, lat]},
// Converting meters to miles. Specifying spherical geometry (for globe)
maxDistance: distance * 1609.34, spherical: true});
}
console.log(dateNow);
dateNow.setHours(dateNow.getHours()-24); //24 hours from now
console.log(dateNow);
query = query.where('created_at').gte(dateNow); //gte - greater then equal
// Execute Query and Return the Query Results
query.exec(function(err, users){
if(err)
res.send(err);
// If no errors, respond with a JSON of all users that meet the criteria
res.json(users);
});
});
But now, if I want to make it more universal and return the data not from the last 24 hours, but from the last xx hours given by the user, how should I modify it?
I want to create a webservice that will get not only lat/long/distance data, but also a number of hours as a POST message and returns the json with correct data. So how should I modify my frontend/backend code to include this change?
Also, right now I call it from the gui as follows:
<button type="button" class="btn btn-danger" ng-click="queryUsersFromLast24Hours()">Show data from last hour</button>
So how can I modify it also to pass a specific number of hours that will be included in the backend query search?
Thank you very much for help!

Quick answer:
Front end:
// Take query parameters and incorporate into a JSON queryBody
$scope.queryUsers = function(){
// Assemble Query Body
queryBody = {
longitude: parseFloat($scope.formData.longitude),
latitude: parseFloat($scope.formData.latitude),
distance: parseFloat($scope.formData.distance)
hours : Number($scope.formData.hours)
};
// Post the queryBody to the /query POST route to retrieve the filtered results
$http.post('/queryUsers', queryBody)
// Store the filtered results in queryResults
.success(function(queryResults){
console.log(JSON.stringify(queryBody));
// Pass the filtered results to the Google Map Service and refresh the map
gservice.refresh(queryBody.latitude, queryBody.longitude, queryResults);
// Count the number of records retrieved for the panel-footer
$scope.queryCount = queryResults.length;
})
.error(function(queryResults){
console.log('Error ' + queryResults);
})
};
Back end:
// Retrieves JSON records for all users who meet a certain set of query conditions
app.post('/queryUsers/', function(req, res){
// Grab all of the query parameters from the body.
var lat = req.body.latitude;
var long = req.body.longitude;
var distance = req.body.distance;
var hours = req.body.hours
var dateNow = new Date(); //right now
// Opens a generic Mongoose Query. Depending on the post body we will...
var query = User.find({});
// ...include filter by Max Distance (converting miles to meters)
if(distance){
// Using MongoDB's geospatial querying features. (Note how coordinates are set [long, lat]
query = query.where('location').near({ center: {type: 'Point', coordinates: [long, lat]},
// Converting meters to miles. Specifying spherical geometry (for globe)
maxDistance: distance * 1609.34, spherical: true});
}
console.log(dateNow);
dateNow.setHours(dateNow.getHours()- hours); // XX hours from now
console.log(dateNow);
query = query.where('created_at').gte(dateNow); //gte - greater then equal
// Execute Query and Return the Query Results
query.exec(function(err, users){
if(err)
res.send(err);
// If no errors, respond with a JSON of all users that meet the criteria
res.json(users);
});
});
Sorry if I am misunderstanding your question but just I want to give you some tips, this query looks like a GET instead of a POST, and you can use query params or path params.

Front end:
$http.post('/recentUsers?numHours=24', queryBody)
Back end:
var numberOfHours;
if (req.query.numHours) {
try {
numberOfHours = parseInt(req.query.numHours)
} catch(e) {}
}
numberOfHours = numberOfHours || 24; // default to 24 hours.
dateNow.setHours(dateNow.getHours() - numberOfHours)

Well, if you are going to subtract always a number X of hours, first I would recommend to change the name of your webService like : queryUsersFromLastNHours
Supposing you are going to trigger that request through a button click, then you may use a call like this
<button id='myButton' ng-click="queryUsersFromLast24Hours()">MyButton</button>
<input id="numberOfHours" ng-bind="hoursToSubtract" />
In angularJs, you must bind that type of values to the Controller via $scope. Then you will have in your Controller a fuction like this
$scope.hours = 0;
var $scope.formData.hours = function() {
var hours = $scope.hours;
var queryBody = {
longitude: parseFloat($scope.formData.longitude),
latitude: parseFloat($scope.formData.latitude),
distance: parseFloat($scope.formData.distance),
hoursToSubtract: hours
};
$http.post('/queryUsersFromLast24Hours', queryBody) ....
}
Read the ngBind documentation for a better understanding
And finally in your nodeJs code, change
dateNow.setHours(dateNow.getHours()-24);
for
var n = req.body.hoursToSubtract;
if (n != null) {
dateNow.setHours(dateNow.getHours()-n);
}

Related

Getting JSON data from snowflake stored procedure parameter and inserting it in target table

I have a requirement to receive JSON data in a Stored Proc parameter and insert the same in the snowflake target table (user_json_feedback). JSON Data has three key elements(User, EntityID, and Entity Type), whereas the target table has five columns (User, ID, Entity Type, Region, and Date). The region will have a default value of "NA," and the date will be the current date.
If the inserts are successful, it returns true; otherwise, it returns false.
I am struggling with the syntax and parsing issues here, as I am very new to writing procedures.
Here is what I have been trying to do, which is giving me errors obviously but serves the algorithm of my intent.
CREATE OR REPLACE SP_UPDATE_JSON_DATA (JSON_DATA VARIANT)
RETURNS BOOLEAN
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
//Declare variables
var REGION = 'NA'
var V_DATE = `select current_date;`;
var DATE_STMT= snowflake.createStatement({sqlText: V_DATE });
var curr_date = DATE_STMT.execute();
var src_json = JSON.parse(JSON_DATA);
var sql_command =
`INSERT INTO user_json_feedback (user,id,etype,region ,date)//
select src_json:USER,src_json:ENTITY_ID,src_json:ENTITY_TYPE,REGION,curr_date;`;
try {
snowflake.execute (
{sqlText: sql_command}
);
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err; // Return a success/error indicator.
}
$$;
The function call with parameters will be like
call SP_UPDATE_JSON_DATA ('[{"USER":"XYZ","ENTITY_ID":"BMT0001","ENTITY_TYPE":"BMT"},{"USER":"ABC","ENTITY_ID":"BMT0002","ENTITY_TYPE":"BMT"}]');
Thanks in advance for the help!
theres a few things here.
Firstly the step to get current date. curr_date is a result set object. to extract the value and use it later, you need to read the first row with .next() then GetColumnValue to read the column content. to pass it later as a well formatted string you'll wanna convert with .toISOString().
Secondly the parsed json returns an array in this case so you'll need to iterate over the array to insert the individual records. As it's not known ahead of time if the variant will contain an array you're best checking if the parsed json is an array and handle it accordingly
Last tweak was altering the return type so you get the verbose feedback you're expecting from your return calls.
Updated code:
CREATE OR REPLACE TEMPORARY TABLE user_json_feedback
(
user VARCHAR(100)
,id VARCHAR(100)
,etype VARCHAR(100)
,region VARCHAR(100)
,date TIMESTAMP_NTZ
);
CREATE OR REPLACE TEMPORARY PROCEDURE SP_UPDATE_JSON_DATA(JSON_DATA VARIANT)
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
//Declare variables
var REGION = 'NA'
var V_DATE = `select current_date;`;
var DATE_STMT= snowflake.createStatement({sqlText: V_DATE });
var DATE_STMT_RES = DATE_STMT.execute();
DATE_STMT_RES.next()
var curr_date = DATE_STMT_RES.getColumnValue(1).toISOString();
var src_json = JSON.parse(JSON_DATA);
try {
if (Array.isArray(src_json)){
for (key in src_json){
var sql_command =
`INSERT INTO user_json_feedback (user,id,etype,region,date)//
VALUES(:1,:2,:3,:4,:5)`;
snowflake.execute (
{
sqlText: sql_command,
binds: [src_json[key].USER,src_json[key].ENTITY_ID,src_json[key].ENTITY_TYPE,REGION,curr_date]
}
);
}
}
else {
var sql_command =
`INSERT INTO user_json_feedback (user,id,etype,region,date)//
VALUES(:1,:2,:3,:4,:5)`;
snowflake.execute (
{
sqlText: sql_command,
binds: [src_json.USER,src_json.ENTITY_ID,src_json.ENTITY_TYPE,REGION,curr_date]
}
);
}
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err; // Return a success/error indicator.
}
$$;
--Need to cast variable string as variant.
--ARRAY example
call SP_UPDATE_JSON_DATA ('[{"USER":"XYZ","ENTITY_ID":"BMT0001","ENTITY_TYPE":"BMT"},{"USER":"ABC","ENTITY_ID":"BMT0002","ENTITY_TYPE":"BMT"}]'::VARIANT);
--Single object example
call SP_UPDATE_JSON_DATA ('{"USER":"JST","ENTITY_ID":"BMT0003","ENTITY_TYPE":"BMT"}'::VARIANT);
SELECT *
FROM user_json_feedback;
Result set:
While all this works, you may well be better served just inserting the whole variant into a table and relying on snowflake's significant semi-structured data querying capabilities. Certainly for large payloads you'll find much better performance from bulk loading to a variant column in a table then parsing in a view.

Create 2D Array out of another Array which is casted against an API

I'm working on a project using the opengeodata and some company-internal APIs. Therefore the code I'm using here is slightly changed.
First I'm using the Opencagedata Javascript API to get the lat/lng-Coordinates out of an address.
<script>
function geocode(query) {
$.ajax({
url: 'https://api.opencagedata.com/geocode/v1/json',
method: 'GET',
data: {
'key': 'MyKey',
'q': query,
'no_annotations': 1
},
dataType: 'json',
statusCode: {
200: function(response){ // success
var lat = response.results[0]['geometry']['lat'];
var lng = response.results[0]['geometry']['lng'];
$('#geo_result').text(lat + ' , ' + lng);
After that I'm using another internal API to get some information about places surrounding this location. Lets call them POI.
After getting those POIs I'm using splice to filter the three nearest POIs to the lat/lng Coordinates and filter on some specific keywords.
var radius = 1.5;
var POI = [];
var apiCall = $.get("CoorpAPI/" + radius + "/around/" + lat + "," + lng);
apiCall.done(function(result) {
var myLoc = result.filter(function(loc) {
return loc.id.substr(0, 3) != 'keyword';
});
$.each(myLoc, function(n, loc) {
POI.push(loc.id)
})
var top3 = POI.slice(0,3);
console.log(top3);
Now I want to run those three "top3" POIs ["AB1234", "BC2345", "CD3456"] against a second API which is returns location information.
Those information are supposed to be written into the same array into the second second dimension.
In the end I want to have something Like:
[
0: AB1234
Location Information: A
Location Information: B
1: CD2345
Location Information: C
Location Information: D
...
]
I guess the loop would have to look somewhat like this, but I'm not sure how to call the API and create a 2d-array out of the "top3" locations and the location information returned by the api:
for (var i = 0; i < top3.length; i++) {
var apiCall_2 = $.get("CoorpAPI_2"+top3[i]);
// ???
}
If I understand you correctly, you want to use a nested array inside POI to add more information after a second API call. One thing you can try is to push an array instead of just loc.id in the following manner:
POI.push([loc.id]);
Now you have a nested array in POI instead of a single string ID (I am guessing the ID is string; can be a number as well). For the second API call you can modify your code like this:
var apiCall_2 = $.get('CoorpAPI_2'+top3[i][0]);
In this way, you are using the ID which, is the first element in the nested array. Once you process all your data from API call 2, you can either push each data point separately into the nested array or, as another array like this:
// pushing each data points separately
top3[i].push(dataPoint1);
// ... up to say N data points
top3[i].push(dataPointN);
// adding as an array
top3[i].push([dataPoint1, dataPoint2, dataPointN]);
Your result will be like this:
[ ["AB1234", "dataPoint1", "dataPoint2"] ]
if you use the first approach and like this
[ [ "AB1234", ["dataPoint1", "dataPointN"] ], ]
if you use the second approach. Hope this helps.

Sorting data in Firebase with JavaScript, how to compare all register in an array of objects?

I found this method, with which I have ordered the hours what I store in my records, I wanted them to be sorted from highest to lowest, but when I test the code, I notice that only two values of the array are compared, which are the first registers.
I've seen other methods of comparison and the logic is the same, what am I doing wrong? I group the messages per user, using the id of the user as key of the array , then I save the rest of data. I do this for retrieve the current messages, since I want show a list of the last currently messages sent.
This is the code:
var ref = new Firebase('https://chatfbexample.firebaseio.com/all-messages');
ref.on("value", function (snapshot) {
var Messages = [];
var value = 0;
snapshot.forEach(function (snap) {
value = snap.val().id;
fecha = snap.val().id;
Messages[value] = [];
Messages[value]['fecha'] = snap.val().fechahora; //I receive a date with the format HH:MM:SS
Messages[value]['texto'] = snap.val().texto;
});
function compare(a, b) {
var time1 = a['fecha'].replace(/:/gi, '1');
var time2 = b['fecha'].replace(/:/gi, '1');
var data1 = parseInt(time1);
var data2 = parseInt(time2);
// alert(time1);
if (data1 > data2)
return -1;
if (data1 < data2)
return 1;
return 0;
}
Messages.sort(compare);
for (var i in Messages) {
console.log("Hour: " + Messages[i]['fecha'] + ' ' + ' Message: ' + Messages[i]['texto']);
}
});
the result is something like this
Hour: 12:11:13 Message: whats'up?
Hour: 11:38:44 Message: p
Hour: 11:49:01 Message: hey?
the second and the third messages are not being compared
an image of my Firebase database
First off it probably would be better for you to save a timestamp from Firebase instead of an actual time, which was probably created on the client side, inside your database. This way you could easily let Firebase handle the sorting.
You would need to use firebase.database.ServerValue.TIMESTAMP to save the exact time at which Firebase received your message inside fechahora.
Following you could simply query all of your messages ordered by their child fechahora.
ref.orderByChild("fechahora").on("value", function (snapshot) {
//Do stuff with your already sorted data
}
If you want your query to have a better performance set the .indexOn property of your node containing all the messages inside your Firebase Database rules to fechahora.
Bonus: If you want your data to be ordered newest to oldest instead of oldest to newest, you just need to use the negative value of the timestamp created by Firebase.
Instead of ordering client-side, you'll be better off asking the server to sort the messages:
ref.orderByChild("fechahora").on(...
Make sure that you define the proper index in your Firebase security rules:
{
"rules": {
"all-messages": {
".indexOn": "fechahora"
}
}
}
Without that the sorting will still work, but will happen on the client instead of the server.

Optional search parameters with mongoose?

I'm an inexperienced web developer and I'm having trouble with making an API.
This API should be able to take these parameters; all parameters should be optional (which are title, categories) and I should be able to limit the number of results I get i.e. where to start the results and how many results I should return.
app.get('/images', function(req, res) {
// var searchKey = req.query.keyword;
console.log("hello!");
var regex = new RegExp(req.query.q, 'i');
return Image.find({
title: regex
},function(err, q) {
console.log("do this");
return res.send(q);
});
});
For example, say we have 10 images with title "Cats A", "Cats B", etc and categories as empty ( [ ] ). We then want start from result 3 and display 6 results.
This is my code, and I'm not sure how to add the extra functionalities in.
You can build up your query as needed based on the supplied parameters. I'm guessing a bit with how the parameters are provided, but here's an example:
// Build up the query conditions based on the supplied parameters (if any).
var conditions = {};
if (req.query.q) {
// Match the query string against the either the title or categories fields
var regx = new RegExp(req.query.q, 'i');
conditions.$or = [
{title: regx},
{categories: regx}
];
}
var query = Image.find(conditions);
// Support optional skip and limit parameters.
if (req.query.skip) {
query = query.skip(req.query.skip);
}
if (req.query.limit) {
query = query.limit(req.query.limit);
}
// Execute the assembled query.
return query.exec(function(err, q) {
console.log("do this");
return res.send(q);
});

Parse Multiple doesNotMatchKeyInQuery

I am having a problem using Parse queries in javascript. I want to try and use multiple doesNotMatchKeyInQuery functions but it only allows the last one to be used. Any ideas how I can make code like this work? Ignore the errors that might exist in other parts of the code. I wrote this as an example
//Query 1
var Class1 = Parse.Object.extend("Class1");
var class1Query = new Parse.Query(Class1);
class1Query.equalTo("id", id1);
//Query 2
var Class2 = Parse.Object.extend("Class2");
var class2Query = new Parse.Query(Class2);
class2Query.equalTo("id", id2);
//Query 3
var Class3 = Parse.Object.extend("Class3");
var class3Query = new Parse.Query(Class3);
class3Query.equalTo("id", id3);
//Bringing it all together
var finalQuery = new Parse.Query("User");
//This is the part below I am talking about
finalQuery.doesNotMatchKeyInQuery("objectId", "id1", class1Query);
finalQuery.doesNotMatchKeyInQuery("objectId", "id2", class2Query);
finalQuery.doesNotMatchKeyInQuery("objectId", "id3", class3Query);
finalQuery.find({
success: function (results) {
response.success(results);
},
error: function (error) {
response.error(error);
}
});
It's not possible to do such a complex query in a single request. However, you can fetch the keys you don't want to match ahead of time, and construct a secondary query from that.
I've written up an example based upon your code above:
// Assuming we're actually dealing with 3 different classes,
// and these can't be combined into a single query
var class1Query = new Parse.Query('Class1');
class1Query.equalTo('id', id1);
var class2Query = new Parse.Query('Class2');
class2Query.equalTo('id', id2);
var class3Query = new Parse.Query('Class3');
class3Query.equalTo('id', id3);
// Fetch the results from all three queries simultaneously
Parse.Promise.when([
class1Query.find(),
class2Query.find(),
class3Query.find()
]).then(function(results) {
// results will contain three arrays of results
// We can now build a query where the objectId is not equal
// to any of the objectIds of the results
var ids = [];
results.forEach(function(set) {
set.forEach(function(obj) {
ids.push(obj.id);
});
});
return new Parse.Query('FinalClass').notContainedIn('objectId', ids).find();
})
I want to caution you that this query will not be efficient for large sets of data. "Does not equal" queries are never fast, because they have to loop over every object in the table. If there is another way to get your data, I highly encourage it.

Categories

Resources