Insert data in a 4D spatial column using Sequelize - postgresql

My application uses Node.js (v9.3.0), PostgreSQL (v9.5.1.0) as database with PostGIS (v2.2.1) installed as an extension and Sequelize (v4.37.6) as ORM. I'm trying to insert a 4D data - with longitude, latitude, altitude and a unix timestamp - in a LineStringZM column but I'm getting the following error:
DatabaseError: Column has M dimension but geometry does not
After some research, I've found out that Sequelize uses the function ST_GeomFromGeoJSON to insert spatial data. However, this function seems to ignore the 4th dimension in my GeoJSON data, which causes the error above.
I suppose that a raw query using ST_MakeLine (with the appropriated input) instead of ST_GeomFromGeoJSON would solve my problem. However, is there an easier way or a more elegant solution to insert a 4D spatial data using Sequelize?
Aditional info:
Model sample:
const GEOGRAPHY = require('sequelize').GEOGRAPHY
module.exports = function (sequelize, DataTypes) {
let Course = sequelize.define('Course', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
allowNull: false,
autoIncrement: true
},
gps: {
type: GEOGRAPHY('LinestringZM', 4326)
}
}, {
tableName: 'courses',
timestamps: false,
paranoid: false
})
return Course
}
Insertion code generated by Sequelize:
INSERT INTO "courses" ("gps")
VALUES (St_geomfromgeojson(
'{"type":"LineString","crs":{"type":"name","properties"{"name":"EPSG:4326"}},"coordinates":[[-44.058367,-19.964709,0,1521552190]]}'
));
The SQL above returns the following exception: DatabaseError: Column has M dimension but geometry does not.
Successful insertion using ST_MakeLine:
INSERT INTO "courses" ("gps")
VALUES (ST_MakeLine(ST_MakePoint(1,2, 10, 1), ST_MakePoint(3,4, 10, 1)));

Related

Geometry type is read as string from the db

I have the following model with a a geometry field :
const soiSchema = {
siteId: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
field: 'site_id'
},
soiId: {
type: DataTypes.BIGINT,
allowNull: false,
field: 'soi_id'
},
geoPoint: {
type: DataTypes.GEOMETRY('POINT'),
allowNull: false,
field : 'geopoint'
}
}
I use this model in a raw query with the following options:
await sequelize.query(query,{
model : Soi,
raw : true,
bind : params,
mapToModel: true
})
My issue: When I query my postgres database I receive the geopoint as a string for exemple '0101000020E610000000A172ECBF844140CCC38A7B66D93F40' and not as a point. The field in the db is indeed of geometry type but sequelize reads it as a string.
Anyone has a workaround ?
I have tried many different sequelize versions but it didnt help solve the issue
Thanks
Try using a proper ST_function for read the geometry data type:
SELECT ST_AsText(geoPoint) FROM your_table
https://postgis.net/docs/PostGIS_Special_Functions_Index.html
You can obtain lng, lt as
SELECT ST_X(geoPoint), ST_Y(geoPoint), ST_AsText(geoPoint)
FROM your_table;

How can I use the Find method to perform a case insensitive search on the database field?

I am unable to get the case insensitive search from the database using Sails.js V1.0 + Waterline ORM. I am using sails-postgresql adapter. The running environment is Heroku + Heroku PostgreSQL.
Is there any way to turn off the following setting in database adapter - For performance reasons, case-sensitivity of contains depends on the database adapter.
Tried the method:
Datastore configuration is:
default: {
adapter: 'sails-postgresql',
url: 'postgres://....',
ssl: true,
wlNext: {
caseSensitive: true
}
}
The code block is:
var meetings = await Meeting.find({
select: ['id', 'uid', 'name', 'deleted', 'complete'],
where: {
owner: user.id,
name: { contains: searchJson.name } : ""
},
skip: (inputs.page > 0) ? (inputs.page) : 0,
limit: (inputs.limit > 0) ? (inputs.limit) : 10,
sort: 'date DESC'
});
The easiest way I've found to handle this and case-insensitive unique indexes with PG/Sails is to use the citext column type instead of text/character varying types (compared to forcing everything to lowercase which stinks).
citext is a case insensitive text datatype. "Essentially, it internally calls lower when comparing values. Otherwise, it behaves almost exactly like text."
An example model attribute pulled from a working app:
username: {
type: 'string',
columnType: 'citext',
required: true,
unique: true,
description: 'A users.. Username',
// ...
},
According to this (somewhat irellevant) Heroku docs page this looks like it should work, but you may need to run create extension citext; on your database first.
For MongoDB https://github.com/balderdashy/sails/issues/7014.
From sails-mongo#1.2.0, you can chain on .meta({makeLikeModifierCaseInsensitive: true}) for a case-insensitive query.
Example
await User.find(criteria).meta({makeLikeModifierCaseInsensitive: true});
You can use native queries, for example:
const eventTypesRows = await Meeting.getDatastore()
.sendNativeQuery(\`SELECT "name" FROM meeting WHERE LOWER(name) = LOWER($1)\`, [searchName]);

Manually insert into Heroku PostgreSQL

I want to add some records in a table on the PostgreSQL db that Heroku offers. I am using Sequelize as ORM.
The query would be this one:
INSERT INTO "Categories" (name) VALUES ('Familie'), ('Liefde'), ('Tienertijd'), ('Kindertijd'), ('Hobbies');
However, I get this error that says I should also specify two more columns that are automatically created by Sequelize, namely createdAt and updatedAt.
ERROR: null value in column "createdAt" violates not-null constraint
DETAIL: Failing row contains (1, Familie, null, null).
How can I manually add these records, without going through Sequelize?
EDIT: this is the Sequelize model for Categories:
module.exports = (sequelize, DataTypes) =>
sequelize.define('Category', {
name: {
type: DataTypes.STRING,
unique: true
}
})
Since I didn't really need the timestamps, I realized you can specify not to use them as shown in the following snippet:
sequelize.define('Category', {
name: { type: DataTypes.STRING , unique: true},
}, {
timestamps: false
});
This way I don't need to specify the createdAt and updatedAt values when doing an INSERT.

Sort populated record in sails waterline

I created a Sails application with two models Publication and Worksheet. They are having a one-to-one relationship. Sails-postgresql is the adapter I'm using. I'm using waterline orm to fire query to the database. I'm When I am trying to load publications data along with worksheet and then sort the records depending on a field in the Worksheet using sort() I'm getting an error.
My model is:
Publication.js
module.exports = {
attributes: {
id: {
type: 'integer'
unique: true
},
worksheetId: {
type: 'integer',
model : 'worksheet'
},
status: {
type: 'string',
defaultsTo: 'active',
in : ['active', 'disabled'],
}
}
}
Worksheet.js
module.exports = {
attributes: {
id: {
type: 'integer',
unique: true
},
name: 'string',
orderWeight: {
type: 'integer',
defaultsTo: 0
}
}
}
So now I want to load all the publication where status is "active" and populate worksheet in the data.
So I'm executing the query:
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').limit(1)
.exec(function (error, publications) {
})
And I'm getting a data like :
{
id : 1,
status : "active",
worksheetId : {
id : 1
name : "test",
orderWeight : 10
}
}
So till now it's all working fine. Now I want to increase the limit to 10 and want to sort the data depending on "orderWeight" which is in the populated data. Initially I sorted the whole data depending on publication id and the query worked.
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').sort('id ASC').limit(10)
.exec(function (error, publications) {
})
So I fired similar query to sort the data on "orderWeight"
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').sort('worksheetId.orderWeight ASC').limit(10)
.exec(function (error, publications) {
})
And this query is giving me error that worksheetId.orderWeight is not a column on the publication table. So I want to fire this sort query on the populated data not on the publication table.
Please let me know how I can get my expected result.
Apart from sort() method I also want to run some find command to the populated data to get those publication where the worksheet name matches with certain key as well.
Basically, what you're trying to do, is query an association's attribute. This has been in the waterline roadmap since 2014, but it's still not supported, so you'll have to figure out a workaround.
One option is to query the Worksheet model, and populate the Publication, since sails doesn't let you query across models without using raw queries (i.e. .sort('worksheetId.orderWeight ASC') doesn't work). Unfortunately, you might have to move the active flag to the Worksheet. For example:
Worksheet.find({
status: 'active'
})
.populate('publication') // you should also add publication to Worksheet.js
.sort('orderWeight ASC')
.limit(10)
Alternatively, you could combine Worksheet and Publication into one model, since they're one-to-one. Probably not ideal, but sails.js and Waterline make it very difficult to work with relational data - I'd estimate that half of the queries in the project I'm working on are raw queries due to sails' poor support of postgres. The framework is pretty biased towards using MongoDB, although it claims to "just work" with any of the "supported" DBs.

Sails.js associations populate with different connections

I have two models
countries - from mysql server
Country = {
tableName: 'countries',
connection: 'someMysqlServer',
schema: true,
migrate: 'safe',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
country_id: {
type: 'integer',
primaryKey: true,
autoIncrement: true
},
....
}
};
User = {
connection: 'somePostgresqlServer',
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true
},
country_id: {
model: 'country'
},
$> User.findOneById(1).populate('country_id').exec(console.log)
and get error
sails> Error (E_UNKNOWN) :: Encountered an unexpected error
: Unable to determine primary key for collection `countries` because an error was encountered acquiring the collection definition:
[TypeError: Cannot read property 'definition' of undefined]
at _getPK (/projects/foturist-server/node_modules/sails-postgresql/lib/adapter.js:923:13)
at StrategyPlanner.__FIND__.Cursor.$getPK (/projects/foturist-server/node_modules/sails-postgresql/lib/adapter.js:504:20)
.....
Details: Error: Unable to determine primary key for collection `countries` because an error was encountered acquiring the collection definition:
[TypeError: Cannot read property 'definition' of undefined]
Why country association uses with postgre-connection ?
Well, since the two models are on different database connections, you're not going to be able to do an actual SQL join. I would think what you'd need is a
User.find({id: 1}).exec(function(user) {
var theUser = user;
Country.find(user.country_id)
.exec(function(country) {
theUser.country = country;
return theUser;
}); });
I'm not sure what specific needs you're trying to address, but since a lookup table of countries is unlikely to frequently change, and is in an entirely different data store, I would suggest caching this data in something like Redis or Memcache. Then on your User find callback you can fetch the country by id from your cache store. This will be much faster unless you expect this data to change on a regular basis. You could write a service that does a lazy lookup in your other database and serves from the cache then on, or cache them all up front when your app launches.