-
Notifications
You must be signed in to change notification settings - Fork 376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unique together #288
Comments
You can do this by specifying multiple primary keys, which will cause the database engine to treat their combination as a unique primary key. db.define('model', {
field1: String,
field2: String
}, {
id: ['field1', 'field2']
}); |
I've also just improved the unique validator to support scope. See the wiki. We should also at some point add the following syntax: db.define('item', {
name: { type: 'string', unique: 'name_cat' },
category: { type: 'string', unique: 'name_cat' }
});
// CREATE UNIQUE INDEX item_name_cat ON item (name, category); |
Thank you @dxg , that idea is exactly what I was thinking when I read this issue. |
Shouldn't check undefined and null values, since ORM uses these as defaults and it can lead to failed validations regardless of passed values.
Surely it should be possible to use the suggested new syntax to generate something along the lines of INDEX IDX_scope (`name`, `category`) Thus ensuring the uniqueness on the database itself... Disclaimer: I haven't actually checked the syntax there, just guessing at it from memory. |
Question is though, should adding those |
I think, if possible, we should avoid client side validators and rather rely on the database engine (since it will be a factor faster). When we add I'd recommend that if the database is able to, we avoid adding the validator and instead work with appending unique index statements to the database instantiation SQL. (Obviously we'd need to mention this, since some people with pre-written databases without these indexes would not experience any difference between |
The question is though, do the supported database servers return parseable enough errors for that to be possible? There's another problem; |
I'm not sure about how parsable the errors would be, but I don't think we necessarily have to wrap them in anything special. Ideally the user shouldn't be running queries which violate the DB schema in the first place. Maybe we can have both the validator and the Essentially we could have the following db.define('model', {
id: { type: 'text', required: true, unique: true },
first_name: { type: 'text', required: true, unique: 'name' },
last_name: { type: 'text', required: true, unique: 'name' },
email: { type: 'text', unique: true }
}, {
id: 'id',
validations: {
first_name: [orm.enforce.required(), orm.enforce.unique('name')],
last_name: [orm.enforce.required(), orm.enforce.unique('name')],
email: [orm.enforce.patterns.email(), orm.enforce.unique().orUndefined()]
}
}); It should then generate the following MySQL table creation query, using the model name and CREATE TABLE model (
id VARCHAR(255) NOT NULL,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
email VARCHAR(255),
PRIMARY KEY(id),
UNIQUE INDEX IX_model_name (first_name, last_name),
UNIQUE INDEX IX_model_email (email)
); We can then keep the enforce validator for How does that sound to you guys? |
So the idea is to stick with what we have, and add the |
I think the required option of a property, although it's validated in the database, it never gets there because there's code to check this. I think this is good if we can avoid the database (because the link can be a lot slower than an
The unique option should then follow the same pattern. |
I think the difference with the It also introduces the issue of handling NoSQL datastores, or databases which don't support whatever query we use to check for uniqueness - which then requires each database driver to define their own implementation of an My vote would be to split database logic and application logic up into the |
I agree with you that |
Certainly is an option, maybe have a central error type wrapper within node-sql-query or ORM which translates, on a per driver basis, error codes into a number of standardized forms. Would allow us to easily interop for other features in the future, and allow the database engines to handle what they're best at while ORM acts as an interface layer (which it's best at). As for NoSQL, at the moment I've added a bit of code which disables the |
Ok, thank you for your effort on this :) |
No worries, actually using this module in a project for one of my other courses - most of the functionality I end up adding is stuff I need there as well, so not really any extra effort :) |
That all sounds wonderful, however we need a reality check: var should = require('should');
var helper = require('../support/spec_helper');
var ORM = require('../../');
describe("unique messages", function() {
var db = null;
var Person = null;
var setup = function () {
return function (done) {
Person = db.define("person", {
name: { type: 'text' },
email: { type: 'text', unique: true },
someId: { type: 'text', unique: true }
});
return helper.dropSync(Person, done);
};
};
before(function (done) {
helper.connect(function (connection) {
db = connection;
done();
});
});
after(function () {
db.close();
});
before(setup());
it("should be enumerated", function(done) {
Person.create({ email: 'e@e.com', someId: 4 }, function (err, person) {
should.not.exist(err);
Person.create({ email: 'e@e.com', someId: 4 }, function (err, person) {
should.exist(err);
console.log('\n'+JSON.stringify(err, null, 2));
return done();
});
});
});
}); Now let's see if we have enough data to construct meaningful validation messages: Postgres{
"length": 161,
"name": "error",
"severity": "ERROR",
"code": "23505",
"detail": "Key (email)=(e@e.com) already exists.",
"file": "nbtinsert.c",
"line": "396",
"routine": "_bt_check_unique",
"index": 0,
"instance": {
"email": "e@e.com",
"someId": 4,
"name": null
}
} Raw sql response: ERROR: duplicate key value violates unique constraint "person_email_key"
DETAIL: Key (email)=(e@e.com) already exists. Mysql{
"code": "ER_DUP_ENTRY",
"index": 0,
"instance": {
"email": "e@e.com",
"someId": 4,
"name": null
}
} Raq sql response: ERROR 1062 (23000): Duplicate entry 'e@e.com' for key 'email' SQLite{
"errno": 19,
"code": "SQLITE_CONSTRAINT",
"index": 0,
"instance": {
"email": "e@e.com",
"someId": 4,
"name": null
}
} I believe the answer is no. Responses from sql servers don't enumerate all errors with the insert query; They return the first one whereas there should be two. In some cases, the error messages are very cryptic and as in the case of the mysql driver, are incomplete. |
Wow, thanks for looking into that. I agree that the errors returned by the database certainly are lacking the details required to truly use them to generate validation messages for unique keys, but that takes us back to the issue of how to handle the validation in a performance friendly way - since in your demo case there would be 2 additional queries for each attempt to insert data (one for It does look like it may be possible to at least extract the first error from SQLite (assuming var field = Object.keys(Model.fields)[error.index];
var value = Instance[field];
return new Error('Duplicate entry for unique field \'' + field '\': \'' + value.toString + '\''); An alternative is to eagerly assume that the inserted data will pass validation, and if we get a validation error from the database we run the queries required to determine all the validation errors? That way we don't have the additional overhead for successful queries but are still able to get the information we need. There is also the option of having validators which are executed only after a query has completed, so having Person = db.define("person", {
name: { type: 'text' },
email: { type: 'text' },
someId: { type: 'text', unique: true }
}, {
validations: {
// Executes pre-query, making a request to the DB to check for uniqueness (current behaviour)
email: orm.unique(),
// Executes post-query to check for a validation error, and if one is present - queries the DB for better info
someId: orm.unique()
}
}); We should be able to check whether Obviously I'm rather opposed to the idea of running additional queries against the database for every insert or update operation - for performance reasons - and in my case I'd be more than happy to settle for less information from a |
Also keep in mind that mysql returns the errors differently in different versions. And if we change the table to I'm also unsure about the order in which hooks will be executed - Whilst I completely understand wanting to reduce the number of SQL queries, I'm not sure It's worth paying the price; significantly more complicated code. Generally apps have frequent reads, and infrequent writes. And if I had a write-heavy table, I'd could manually optimize - by not adding the traditional 'slow' unique validator - and just handle the errors straight from the database. I'm not sure if this is a problem that needs solving, especially given the increased complexity. |
I was thinking more about looking into error codes, not descriptions. Error 1062 (in http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html#error_er_dup_entry |
good job. index together: �set unique together: set |
It would be nice to specify that two or more columns have to be unique together, but not unique themselves.
The text was updated successfully, but these errors were encountered: