Dynamic queries with knex-postgis - node.js

I am building a simple API that also deal with geometries (store into postGIS).
I am using knex-postgis to access the ST_ spatial functions in postGIS with knex.
I have used this example to insert a point and it work when its hard coded.
But my lack of experience is leave me hanging, how can I make the query dynamic? I want to create a form with input for x and y value and send it to the ST_geomFromText function to save it to the geom type in the db.
Is this when you would use parameters? Could someone point me in the right direction?
// insert a point
const sql1 = db.insert({
id: 1,
geom: st.geomFromText('Point(0 0)', 4326)
}).into('points').toString();
console.log(sql1);
// insert into "points" ("geom", "id") values
(ST_geomFromText('Point(0 0)'), '1')
So far I have tried
router.post('/', (req, res, next) => {
queries.create(req.body).then(poi => {
res.json(poi[0]);
});
});
Insert query
create(poi) {
const sql = db.insert(poi).returning('*').into('poi');
return sql;
},
In Postman I am sending this in its body
{
"place": "test",
"comments": "new",
"numbers": 6,
"geom": "st.geomFromText('Point(-71.064544 44.28787)', 4326)"
}
But get an error "Unhandled rejection error: parse error - invalid geometry"
The hard coded object looks the same and is work fine.
I have a feeling that I am using the st.geomFromText wrong, but i dont I am not sure?
This is what i get if i console.log returned query
insert into "poi" ("comments", "geom", "numbers", "place") values ('new', 'st.geomFromText(''Point(-71.064544 44.28787)'', 4326)', 6, 'test')
(see how it does not change st.geom.. to st_geom..? Also the quotation marks around the ST function and the Point object is not right)
This string work when I run it in pgAdmin
insert into "poi" ("comments", "geom", "numbers", "place") values ('new', st_GeomFromText('Point(-71.064544 44.28787)', 4326), 6, 'test')
EDIT:
I console.log the hardcoded version too. It does no appear to add the extra '' on the geometry value
insert into "poi" ("geom") values (ST_geomFromText('Point(-71.064544 44.28787)', 4326))
Any advice?

You pass coordinates from postman as string value in JSON object
"geom": "st.geomFromText('Point(-71.064544 44.28787)', 4326)"
And expect your code to transform it into a function call
st.geomFromText('Point(-71.064544 44.28787)', 4326)
What knex does in this case. It takes your string and convert it as it is string field (here you get a single quote ' replacement with double '' for Postgres to escape it).
What you can do. Pass your geometry to your API like this
"geom": "Point(-71.064544 44.28787)"
And in your route handler
create(poi) {
const sql = db.insert({
place: poi.place,
comments: poi.comments,
numbers: poi.numbers,
geom: st.geomFromText(poi.geom, 4326)
}).returning('*').into('poi');
return sql;
},

Related

Can't do bulk update using MariaDB, pool.query and Express

I have an API written in Express and MariaDB as DBMS, I'm trying to update multiple records at the same time using an array but it doesn't work.
Example:
const data = [ [ '2', '130' ], [ '4', '10' ] ]
try {
const updatedInventory = await pool.query('UPDATE inventory SET qty_shipped = qty_shipped + VALUES ? WHERE sku_id = VALUES ?', [data])
res.status(201).json(updatedInventory)
} catch (error) {
res.status(500).json({ message: error.message })
console.error(error.message)
}
However I get this error:
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'VALUES ('2', '130'), ('4', '10') WHERE sku_id = VALUES ?' at line 1
I did something similar with the Insert endpoint where I'm doing bulk inserts also using an array and that works just fine:
const newInventory = await pool.query('INSERT INTO inventory (sku_id, qty_received, expiration_date) VALUES ? ON DUPLICATE KEY UPDATE qty_received = qty_received + VALUES(qty_received) ', [data])
Any ideas on how to get the bulk update part working?
According to the error you put in the question, the second "?" isn't grounded. If you look at the error, you see that the first value set is grounded to the data variable, but the second one isn't.
If you expect to ground the data to both "?" in your statement, then you might need to add the data variable to your statement twice.
Also, it might be that you can't ground the data variable twice. I wouldn't expect that; you should be able to use the data variable twice in our statement. If you need to different variables, then create a second variable, copy data into the second variable and then ground both both variables in your statement.

How to insert an integer with nextval function in an multirow insert in pg-promise

Is it possible to use the nextval function in a pg-promise multirow insert?
I have a database (that I sadly can't change) where the id has to be inserted via the client like this:
INSERT INTO some_object (object_id, object_name)
VALUES (nextval('some_object_seq'), ${object_name})
RETURNING object_id;
This works fine for one insert. But now I have to insert multiple rows at once and tried pgp.helpers.insert:
const cs = pgp.helpers.ColumnSet(['object_id', 'object_name'], { table });
const query = pgp.helpers.insert(values, cs) + 'RETURNING object_id';
database.many(query).then(data => {
return data
}).catch(error => {
logger.error(error, query);
});
Is there any way to use nextval('some_object_seq') in this scenario? Sadly I can't change the default value of the id column in the table definition.
Your column should be defined as this:
{
name: `object_id`,
init: () => `nextval('some_object_seq')`,
mod: `:raw`
}
As opposed to the answer by #baal, you do not need to use def, because you are not providing a default value, rather a complete override for the value, which is what init for.
And it can be used within upsert queries too.
As Bergi wrote, it is possible to add a default expression to the column set like this:
const cs = pgp.helpers.ColumnSet(
[{
name: "object_id",
// setting the DEFAULT value for this column
def: "nextval('some_object_seq')",
// use raw-text modifier to inject string directly
mod: "^",
}, 'object_name'], { table });

Express add backslash in insert

I'm working with express and Mysql trying to create a new record in my database but i'm getting one error in my console. This one show the value inside of backslash and i dont know why.
This is my function:
const PostImpuestos = (data,callback) => {
var sql = "INSERT INTO tbImpuesto (nombre, valor) VALUES ?";
connection.query(sql, [data] ,function(error,results){
if (results){
return callback()
} else {
console.log(error);
}
})
}
and this is the error:
sqlMessage:
'You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near \'nombre = \'Impuesto de Tijuana\', valor = \'15%\'\' at line 1',
sqlState: '42000',
index: 0,
sql:
'INSERT INTO tbImpuesto (nombre, valor) VALUES nombre = \'Impuesto de Tijuana\', valor = \'15%\'' }
I was thinking the error could be something about type of the value data, because this is a json. I change it for array and object but the problem persists.
Simple fix you've got your insert query wrong you need to add ? for each variable you insert inside of brackets.
INSERT INTO tbImpuesto (nombre, valor) VALUES (?, ?);
The backslashes are just sanitzation to prevent sql injection.

How to call stored proc in postgres using sequelize passing json parameter

I am new to postgresql and sequelize ORM.
I am trying to call a stored procedure which inserts data to a table.
CREATE OR REPLACE FUNCTION public.sp_eoi(
obj json)
RETURNS void
LANGUAGE 'sql' AS $BODY$
INSERT INTO public."tbl_eoi" ("SE_CUST_ID", "SE_VENDOR_ID", "SE_VENUE_ID")
SELECT (rec->>'custId')::integer, (rec->>'vendorId')::integer, (rec->>'venueId')::integer FROM
json_array_elements(obj-> 'data')rec $BODY$;
Json input passed from node application
{ custId : 1, vendorId : 1, venueId : 1}
I am calling proc
sequelize.query(
'call sp_eoi(:param)',
{replacements: { param: [ { custId:1 , vendorId: 1,venueId : 1 } ] }})
.spread(function(outputValue , records) {
console.log(records);
}).error(function(err){
res.json(err);
});
But there is always the error as "Unhandled rejection Error: Invalid value {custId: ..}
I am totally new to this and have been trying to find the problem for the last two days.
Any help would be really appreciated.
Thanks
A bit late for the party... however:
td; dr;
Postgres uses functions (you can call them stored procedures), but it does not CALL them, it SELECT them
Here's the link to a similar problem: https://stackoverflow.com/a/38115374/1163927

How can I validate if gps coordinates given to a GET web service in node.js/mongodb are correct?

I have a webservice that queries the mongodb (with mongoose) with a given gps data, this is how it looks:
app.get('/queryUsers/:latitude,:longitude,:distance', function(req, res) {
var lat = req.params.latitude;
var long = req.params.longitude;
var distance = req.params.distance;
// Opens a generic Mongoose Query. Depending on the post body we will...
var query = HelpRequest.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
});
}
etc.
When I call it with a correct values, e.g.:
http://localhost:3000/queryUsers/48.77824506989009,19.80828625000005,1691.758035687216
then I'm getting the correct data. But when I call it for example with string:
http://localhost:3000/queryUsers/48.77824506989009,someString,1691.758035687216
then I'm getting this error:
{"stack":"Error\n at MongooseError.CastError
(/project/node_modules/mongoose/lib/error/cast.js:18:16)\n at SchemaArray.SchemaNumber.cast
(/project/node_modules/mongoose/lib/schema/number.js:219:9)\n at SchemaArray.castToNumber
(/project/node_modules/mongoose/lib/schema/array.js:227:38)\n at /project/node_modules/mongoose/lib/schema/array.js:237:29\n at Array.forEach (native)\n at castArraysOfNumbers
(/project/node_modules/mongoose/lib/schema/array.js:233:7)\n at cast$geometry (/project/node_modules/mongoose/lib/schema/array.js:260:7)\n at SchemaArray.cast$near
(/project/node_modules/mongoose/lib/schema/array.js:249:12)\n at SchemaArray.castForQuery
(/project/node_modules/mongoose/lib/schema/array.js:190:19)\n at module.exports
Now, since I'm new to writing such webservices - could you help me and tell me how should I modify my code to return a nicer message to the end user in case of giving the wrong data?
Maybe you can validate each of the numbers and return errors if they are not what you expect them to be, that way you can intercept the error before it happens in Mongoose which will be much faster since it doesn't hit the DB to do validation there.
Since lat and long are float numbers, you can validate if they are in fact, float numbers. The following link can help you with that.
How do I check that a number is float or integer?
To actually validate, you could use middleware or do it in the same method you're using but middleware is more "node.js".
app.get('/queryUsers/:latitude,:longitude,:distance', validateLocation, function(req, res) {
// data is correct here
});
and then create a new method to do the validation:
function validateLocation(req, res, next) {
var lat = req.params.latitude;
if(!isFloat(lat)) { return res.status(406).send("Please send a valid latitude"); }
// ... do the same for each variable. you can also use arrays to return multiple errors
else {
return next();
}
}

Resources