Sequelize - How do I seed database with Geometry value? - node.js

I use Sequelize to connect to my PostgreSQL database and during development, I use seed files to populate database with example data. I recently installed PostGIS for my database and wanted to use the GEOMETRY('POINT') type to describe the latitude/longitude position.
However, I have no idea how to put some GEOMETRY data using seeders. I tried following the examples in Sequelize docs:
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.bulkInsert('Vets', [{
title: 'Centrum Zdrowia Małych Zwierząt',
city: 'Poznań',
googleMapsID: 'ChIJQ8EgpGpDBEcR1d0wYZTGPbI',
position: {
type: 'Point',
coordinates: [52.458415, 16.904740]
},
rodents: true
}], {}),
down: (queryInterface, Sequelize) =>
queryInterface.bulkDelete('Vets', null, {})
};
but when I run the sequelize db:seed:all command, following error occurs:
ERROR: Invalid value [object Object]
I guess I just need to specify the position in some other way, but the Sequelize docs don't mention any for seeds. Can anyone help me with this problem?
The migration file for the Vets database is as follows:
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Vets', {
...
rodents: {
type: Sequelize.BOOLEAN
},
position: {
type: Sequelize.GEOMETRY
},
websiteUrl: {
type: Sequelize.STRING
},
...
}, {
indexes: [
{
unique: true,
fields: ['title', 'city']
}
]
}),
down: (queryInterface, Sequelize) =>
queryInterface.dropTable('Vets')
};
And the model definition:
module.exports = (sequelize, DataTypes) => {
const Vet = sequelize.define('Vet', {
...
rodents: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
position: DataTypes.GEOMETRY('POINT'),
websiteUrl: DataTypes.STRING,
...
}, {
indexes: [
{
unique: true,
fields: ['title', 'city']
}
]
});
Vet.associate = (models) => {
Vet.belongsTo(models.User, { as: 'suggestedBy' });
Vet.belongsTo(models.User, { as: 'acceptedBy' });
};
return Vet;
};

I found a solution (or rather a workaround), turns out I needed to use Sequelize.fn():
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.bulkInsert('Vets', [
{
...
position: Sequelize.fn('ST_GeomFromText', 'POINT(52.458415 16.904740)'),
...
}
], {}),
down: (queryInterface, Sequelize) =>
queryInterface.bulkDelete('Vets', null, {})
};

I would like to add to your answer, that if you want to add a type: 'Polygon' and as a geoJson you can do the following:
up: (queryInterface, Sequelize) => {
const polygon = { type: 'Polygon', coordinates: [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0] ]
]};
const cityCreated = {
....
boundaries: Sequelize.fn('ST_GeomFromGeoJSON', JSON.stringify(polygon)),
....
}
return queryInterface.bulkInsert('cities', [cityCreated], {});
},
in this case, the column boundaries is the one defined as GEOMETRY
this is good for adding boundaries, and geoJson is a more common dataType than text for this purpose.

Related

complicated query using sequelize

i have 3 tables .. profile, player, garage
profile has winCount , loseCount
player has username
garage has carNumber
they are all connected with each other using the id from player table (playerId in table garage and profile)
i want to get the
top 20 winCount , username , carnumber
i am trying this code
let racers = await Profile.findAll({
where: {
carRaceWinCount: {
[Op.gt]: 0
}
},
limit: 20,
order: [
['carRaceWinCount', 'DESC']
]
})
.then((races: any) => {
Garage.findAll({
where: {
playerId: races.id
},
attributes : ['carNum']
})
Player.findAll({
where :{
id : races.id
},
attributes : ['username']
})
})
and it is not working
what is the best way to get this query
If you have these associations:
Player.hasMany(Profile, { foreignKey: 'playerId' })
Player.hasMany(Garage, { foreignKey: 'playerId' })
Profile.belongsTo(Player, { foreignKey: 'playerId' })
then the query might look like this
await Profile.findAll({
where: {
winCount: {
[Op.gt]: 0
}
},
limit: 20,
include: [{
model: Player,
include: [{
model: Garage,
// this is important option to query all garames as separate queries, Otherwise you will get wrong count of profiles.
separate: true
}]
}]
order: [
['winCount', 'DESC']
]
})

Node.js sequelize model - can I define order of records on the model?

I am newish to node, and can't figure out how to only return 1 record of a hasMany relationship, based on an attribute.
There's a user, level, and levels_user table.
On user, I want to include 1 levels_user where level_id is highest.
Can I put a condition on this model file, something like:
order_by: ['level_id', 'DESC']
LevelsUsers Model
'use strict';
module.exports = (sequelize, DataTypes) => {
let LevelsUsers = sequelize.define('LevelsUsers', {
user_id: DataTypes.INTEGER,
level_id: DataTypes.INTEGER,
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE,
},{
timestamps: false,
freezeTableName: true,
schema: "public",
tableName: "levels_users"
});
return LevelsUsers;
};
Users model association:
Users.hasMany(models.LevelsUsers, {
as: 'levels_users',
targetKey: 'id',
foreignKey: 'user_id',
});
This is my call:
users.getProfileByUserId = (req, res) => {
models.Users.findOne({
where: {id: req.params.userid},
include: [
{
model: models.LevelsUsers,
as: 'levels_users',
limit: 1,
}
]
order: []
}).then(user ....
I tried adding:
`order: [
[ models.LevelsUsers , 'level_id', 'DESC']
]`
Did not work and I think it's evaluated after limit: 1 anyways.
Can I put order_by on the model, to return highest to lowest by level_id each time? If not, what's a better way to accomplish returning only the highest levels_users record where level_id is highest?
The hasMany property is more suited when you want to include all the levels in the user object. In your case, I would advise to just pull the user without it's levels, and then do a second request to pull the higher level linked to that user by querying directly the LevelsUsers model :
models.LevelsUsers.findAll({
where: {
user_id: user.id,
},
order: ['level_id', 'DESC'],
limit: 1,
});
Well i reading over the documentation of sequelize for working with ordering and limit inside the includes tag.
Update query
users.getProfileByUserId = (req, res) => {
models.Users.findOne({
where: {id: req.params.userid},
include: [
{
model: models.LevelsUsers,
as: 'levels_users',
order: [
[ { model: models.LevelsUsers }, 'level_id', 'DESC']
],
limit: 1,
}
]
}).then(user ....
For references go over the following links - https://github.com/sequelize/sequelize/issues/4553#issuecomment-341261957

Unable to execute the raw query in Sequelize migrations

I am trying to update my database using Sequelize migrations so I have tried to write a Sequelize migrations like this
'use strict';
module.exports = {
up: (queryInterface, Sequelize, migration) => {
queryInterface.addColumn('coaching_class_entries', 'bill_cycle', {
type: Sequelize.INTEGER(4),
allowNull: false,
defaultValue: '0',
field: 'bill_cycle',
after: 'fees'
})
.then(() => queryInterface.addColumn('coaching_classes', 'bill_plans', {
type: Sequelize.JSON,
allowNull: false,
defaultValue: 'NULL',
field: 'bill_plans',
after: 'bill_cycle'
}))
.then(() =>
migration.migrator.Sequelize.query('UPDATE coaching_classes SET bill_plans = JSON_ARRAY(JSON_OBJECT("cycle", bill_cycle, "fee", fees));'));
},
down: (queryInterface, Sequelize) => {
let migrations = [];
migrations.push(queryInterface.removeColumn('coaching_class_entries', 'bill_cycle'))
migrations.push(queryInterface.removeColumn('coaching_classes', 'bill_plans'))
return Promise.all(migrations);
}
};
But it is always giving me error in raw query line
Cannot read property 'Sequelize' of undefined
What is the correct syntax for this?
I found it myself only we have to use simple queryInterface.sequelize.query

CHECK constraints in Seqeulize PostgreSQL ORM (Node.js)

I'm using Sequelize as ORM with a PostgreSQL engine. When using raw queries I can create a table and have columns with 'CHECK' constraints such as
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
In the docs I cannot find a way to do this in Sequelize when defining models. Is there any way of doing this? I don't want to reinvent the wheel ;)
Thanks!!
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Products', {
product_no: {
type: Sequelize.INTEGER
},
price: {
type: Sequelize.NUMERIC
},
name: {
type: Sequelize.TEXT
}
}).
then(() => queryInterface.addConstraint('Products', ['price'], {
type: 'check',
where: {
price: {
[Sequelize.Op.gt]: 0
}
}
}));
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Products');
}
};
Take a look at the Validations section.
var Product = sequelize.define('product', {
price: {
validate: {
min: 0 // Only allow values >= 0
}
}
});
You can also set custom validation rules:
var Product = sequelize.define('product', {
price: {
validate: {
isPositive: function (value) {
return value > 0; // Don't allow 0.
}
}
}
});

How to insert initial data using sequelize migrations/seeds?

I'm trying to create my initial migration to populate the test database but I can't get it working. This is what I have in my migration:
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
return [
queryInterface.bulkInsert('Users', [
{ username: "user1" },
{ username: "user2" }
])];
},
down: function (queryInterface, Sequelize) {
return queryInterface.dropTable('Users');
}
};
And I get this error:
== 20151024144833-create-conjugation: migrating =======
{ [SequelizeUniqueConstraintError: Validation error]
name: 'SequelizeUniqueConstraintError',
message: 'Validation error',
errors: [],
fields: [] }
There must be an easier way to do this. I've checked other SO questions, but the syntax has changed in the current version of sequelize.
UPDATE
Ok, I realized my mistake: I was assuming that sequelize would take care of the timestamps. This fixes the problem:
up: function (queryInterface, Sequelize) {
console.log(User);
return [
queryInterface.bulkInsert('Users', [
{ username: "user1", createdAt: Date.now(), updatedAt: Date.now() },
{ username: "user2", createdAt: Date.now(), updatedAt: Date.now() }
])
];
}
But I'm still wondering if this is the right way to seed my database. Is there a way to do it using User.create({})?
new Date()
also required for mysql, i.e.
return queryInterface.bulkInsert('users', [
{
"Forename":"A",
"Surname": "User",
"UserType":"1",
"Email":"auser#gmail.com",
"Password":"password",
"LastLogin":0,
"Tokens": JSON.stringify({"tokens":[]}),
"CreatedBy": 999,
"CreatedAt": new Date(),
"UpdatedAt": new Date()
}]);
You can use next:
const City = sequelize.define('city', {
name: { type: Sequelize.STRING },
order_: { type: Sequelize.INTEGER }
});
City.sync().then(() => {
City.create({
name: 'Neuquen',
order_: 0
});
City.create({
name: 'General Roca',
order_: 1
});
});
Or read about "migrations" at http://docs.sequelizejs.com/en/latest/docs/migrations/
An alternative could be use : sequelize fixtures , you could init your tables with default data declared as a json file or other format.
For a quick and easy way (without seeds or migrations) on sequelize v6:
I modified my sequelize.sync() call:
import { Sequelize } from 'sequelize';
// This will create an in-memory sqlite db
const sequelize = new Sequelize('sqlite::memory:', {
logging: sequelizeLogger
});
await sequelize
.sync({ force: true })
.then(() => {
// seed db
Users.create({ username: 'user1' })
});

Resources