Sails.js one to many association with postgreSQL: column does not exist - sails.js

I need some help with associations in sails 0.12.13 with postgresql.
I have an "App" model and a "Membership" model. Relation should be one to many (one app can be associated with many relationships).
This is the App model db table schema (table is called "apps"):
Table "public.apps"
Column | Type | Modifiers
------------+-----------------------------+---------------------------------------------------
id | integer | not null default nextval('apps_id_seq'::regclass)
name | character varying | not null
Indexes:
"apps_pkey" PRIMARY KEY, btree (id)
"apps_name_key" UNIQUE CONSTRAINT, btree (name)
Referenced by:
TABLE "memberships" CONSTRAINT "app_fk" FOREIGN KEY (app_id) REFERENCES apps(id) ON UPDATE RESTRICT ON DELETE CASCADE
And this is memberships:
Table "public.memberships"
Column | Type | Modifiers
------------+-----------------------------+----------------------------------------------------------
id | integer | not null default nextval('memberships_id_seq'::regclass)
app_id | integer | not null
Foreign-key constraints:
"app_fk" FOREIGN KEY (app_id) REFERENCES apps(id) ON UPDATE RESTRICT ON DELETE CASCADE
in my user model, i have this:
module.exports = {
tableName: 'apps',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: { type: 'string', unique: true, required: true, alphanumericdashed: true },
memberships: { collection: 'memberships', model: 'Membership' },
}
}
And this is the Membership model:
module.exports = {
tableName: 'memberships',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
app: { model: 'app', columnName: 'app_id' },
},
};
When I try to query an app and get its memberships:
App.find({ id: 1 }).populate('memberships').exec((err, app) => {
if (err) throw err;
console.log(app.memberships);
});
I get this error:
Error (E_UNKNOWN) :: Encountered an unexpected error
error: column apps.memberships does not exist
at Connection.parseE (/usr/src/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:539:11)
at Connection.parseMessage (/usr/src/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:366:17)
at Socket.<anonymous> (/usr/src/app/node_modules/sails-postgresql/node_modules/pg/lib/connection.js:105:22)
at emitOne (events.js:115:13)
at Socket.emit (events.js:210:7)
at addChunk (_stream_readable.js:252:12)
at readableAddChunk (_stream_readable.js:239:11)
at Socket.Readable.push (_stream_readable.js:197:10)
at TCP.onread (net.js:589:20)
Looks like the association is not "enabled" and waterline is searching for an actual column "membership" in my model. Can anybody explain me what I am doing wrong? thx

According to the documentation, I would guess that you have a bad association.
// App.js
module.exports = {
tableName: 'apps',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: {
type: 'string',
unique: true,
required: true,
alphanumericdashed: true
},
memberships: {
collection: 'membership', // <-- changed to singular (as your model should be)
via: 'app' // <-- use "via" instead of "model"
},
}
}
// Membership.js
module.exports = {
tableName: 'memberships',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
app: {
model: 'app'
// <-- removed the "columnName" here
},
},
};
Also, convention generally says name your model as a singular instance. For example, its "User.js" and not "Users.js". It's valid to refer to the collection as a plural. I made some changes in your naming, but you'll have to see how that affects your files (since you didn't give those names).

Related

TypeOrm updating geography column error: unknown GeoJSON type

I have NestJs+TypeOrm+PostgreSQL project with a table column that is defined like so:
"area" GEOGRAPHY(POLYGON,4326) DEFAULT NULL
My entity column is defined like this:
#Column({
type: 'geography',
spatialFeatureType: 'Polygon',
srid: 4326,
nullable: true,
default: null,
transformer: {
from: (dbValue) => {...},
to: (entityValue: Position[]) => {
const polyObj: Polygon = {
type: 'Polygon',
coordinates: [entityValue]
}
return JSON.stringify(polyObj)
}
}
}
area: Position[]
When I try to update an entry with a new value I get this error:
error: error: unknown GeoJSON type
The TypeOrm logs show this as the parametrized query:
query failed: UPDATE "<tablename>" SET "uuid" = $1, "area" = ST_SetSRID(ST_GeomFromGeoJSON($2), 4326)::geography WHERE "uuid" IN ($3)
-- PARAMETERS: ["<uuid>","\"{\\\"type\\\":\\\"Polygon\\\",\\\"coordinates\\\":[[[10.053713611343388,57.20829976160476],[10.052780202606208,57.20646356881912],[10.054282239654546,57.206306674693764],[10.055151275375371,57.20820098140615],[10.053713611343388,57.20829976160476]]]}\"","<uuid>"]
Does anyone know what I might be doing wrong?

ER_TOO_LONG_KEY in SailsJS 1.0 with sails-mysql

I got this error with sails when I try to sails lift:
info: ·• Auto-migrating... (drop)
error: A hook (`orm`) failed to load!
error:
error: Error: ER_TOO_LONG_KEY: Specified key was too long; max key length is 767 bytes
I just have one model for now: 
module.exports = {
datastore: 'default',
tableName: 'sci_user',
attributes: {
email: {
type: 'string',
required: true,
unique: true
},
password: {
type: 'string',
required: true
}
}
It's really simple and I got it from the documentation. I don't understand. It seems it's because of the unique: true.
This is due to a combination of factors, but the most pertinent one is that sails-mysql currently defaults to using the utf8mb4 character set for string attributes, to allow the use of emojis and other extended characters. We're working on a patch to make this configurable rather than the default, but in the meantime the quickest workaround is to declare the columnType for your attributes directly:
module.exports = {
datastore: 'default',
tableName: 'sci_user',
attributes: {
email: {
type: 'string',
required: true,
unique: true,
columnType: 'varchar'
},
password: {
type: 'string',
required: true,
columnType: 'varchar'
}
}

Doctrine2 ManyToMany relation using PostgreSQL: "SQLSTATE[42P01]: Undefined table: 7 ERROR: the relation does not exist"

When I make an INNER JOIN between two entities (connected by a ManyToMany relationship) doctrine says that cannot find the relation machine_table_for_base_model: SQLSTATE[42P01]: Undefined table: 7 ERROR: the relation "machine_table_for_base_model" does not exist. Why this happens?
I have an associative table defined as follows:
<!-- language: lang-sql -->
CREATE TABLE machine_table_for_base_model (
id_machine_table integer NOT NULL,
id_base_model integer NOT NULL,
);
ALTER TABLE ONLY machine_table_for_base_model
ADD CONSTRAINT machine_table_for_base_model_pkey PRIMARY KEY (id_machine_table, id_base_model);
ALTER TABLE ONLY machine_table_for_base_model
ADD CONSTRAINT machine_table_for_base_model_ibfk_1 FOREIGN KEY (id_machine_table) REFERENCES machine_table(id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE ONLY machine_table_for_base_model
ADD CONSTRAINT machine_table_for_base_model_ibfk_2 FOREIGN KEY (id_base_model) REFERENCES base_model(id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
When I execute a doctrine mapping of the associated tables I have something like that:
Package\MyBundle\Entity\MachineTable:
type: entity
schema: techguide
table: machine_table
indexes:
IDX_8386B61E3C5569AA:
columns:
- edit_user
id:
id:
type: integer
nullable: false
options:
unsigned: false
id: true
generator:
strategy: SEQUENCE
fields:
description:
type: string
nullable: false
length: 200
options:
fixed: false
labelsDimensions:
type: text
nullable: false
length: null
options:
fixed: false
column: labels_dimensions
editDate:
type: datetime
nullable: false
options:
default: now
column: edit_date
manyToOne:
editUser:
targetEntity: Package\SupportBundle\Entity\Account
cascade: { }
fetch: LAZY
mappedBy: null
inversedBy: null
joinColumns:
edit_user:
referencedColumnName: id
orphanRemoval: false
manyToMany:
idBaseModel:
targetEntity: BaseModel
cascade: { }
fetch: LAZY
mappedBy: null
inversedBy: idMachineTable
joinTable:
name: machine_table_for_base_model
joinColumns:
-
name: id_machine_table
referencedColumnName: id
inverseJoinColumns:
-
name: id_base_model
referencedColumnName: id
orderBy: null
lifecycleCallbacks: { }
At last I found a solution to this problem. You need to edit (manually) the joinTable name on the ORM configuration, which in my case is a Yaml file, adding the Postgres schema the associative table belongs to:
Entity.orm.yml
manyToMany:
idBaseModel:
targetEntity: BaseModel
cascade: { }
fetch: LAZY
mappedBy: null
inversedBy: idMachineTable
joinTable:
name: <NAME_OF_YOUR_SCHEMA>.<NAME_OF_THE_TABLE>
joinColumns:
-
name: id_machine_table
referencedColumnName: id
inverseJoinColumns:
-
name: id_base_model
referencedColumnName: id
orderBy: null
lifecycleCallbacks: { }

Sequelize.js - How to create non-trivial associations without raw SQL?

Here is my situation:
I'm using postgres 9.4, Sequelize ORM and have following models:
Service
serviceCode - primary key, string of 6 characters
serviceTitle - string
ServiceGroup
serviceCodePrefixes - array of strings that are prefixes for Service.serviceCode
serviceGroupTitle - string
Task
serviceCode - reference to Service
I need to build Task object populated with Service and ServiceGroup objects. Example:
In database:
Service {
serviceCode: '123232',
serviceTitle: 'svc title #1',
}
ServiceGroup {
serviceCodePrefix: ['12', '13', '92', ...],
serviceGroupTitle: 'svc grp title #1',
}
Task {
serviceCode: '123232',
}
Result:
Task {
service: {
serviceTitle: 'svc title #1',
},
serviceGroup: {
serviceGroupTitle: 'svc grp title #1',
},
}
The problem is that serviceCodePrefix contains not simple IDs, which can be used to create association using hasOne/belongsTo/etc., but prefix for ID.
So questions is: how this can be done without raw sql?
Turns out that right now Sequelize has experimental feature: 'on' option for 'include'. This option allows users to customize joining conditions. So my problem can be solved this way:
const Service = sequelize.define('service', {
serviceTitle: Sequelize.STRING,
serviceCode: Sequelize.STRING,
});
const ServiceGroup = sequelize.define('service_group', {
serviceGroupTitle: Sequelize.STRING,
// Array of prefixes (e.g. ['01%', '023%'])
serviceCodePrefix: Sequelize.ARRAY(Sequelize.STRING),
});
const Task = sequelize.define('task', {
taskTitle: Sequelize.STRING,
serviceCode: Sequelize.STRING,
});
Task.belongsTo(Service, { foreignKey: 'serviceCode' });
// Hack needed to allow 'include' option to work
Task.hasMany(ServiceGroup, { foreignKey: 'serviceCodePrefix', constraints: false });
// And finally
Task.findAll({
include: [
{ model: Service },
{
model: ServiceGroup,
on: [' "task"."serviceCode" LIKE ANY("serviceGroup"."serviceCodePrefix") '],
},
],
});
Not sure about the performance though.

testing sails/mysql with fixtures primary key issue

I have a sails app working against a legacy database (MySQL) and I would like to perform integration tests. I am using fixtures to load data into a separate test database using barrels. When I run my tests I get an error:
[Error (E_UNKNOWN) Encountered an unexpected error] Details: Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
Interview.js api/models/Interview.js:
module.exports = {
tableName: 'interview',
attributes: {
interviewId: {
type: 'integer',
columnName: 'interview_id',
primaryKey: true,
unique: true
},
...
}
};
interview.json tests/fixtures:
[
{
"interviewId": 1,
"title": "some string",
"createdDate": "2015-11-23T09:09:03.000Z",
"lastModified": "2015-11-23T09:09:03.000Z"
},
{...}
]
test environment config/env/test.js :
models: {
connection: 'test',
migrate: 'drop',
autoPK: false,
autoCreatedAt: false,
autoUpdatedAt: false
}
The problem seems to lie in defining a primary key in the schema rather than letting sails create one automatically. If I remove the interviewId field from the model and set autoPK: true it works. But this does not accurately represent my data structure.
App info:
sails#0.11.2 sails-mysql#0.11.2 waterline#0.10.27 barrels#1.6.2 node v0.12.7
Many thanks,
Andy