Sequelize allownull constraint conditionally - orm

I need a migration in sequelize to change a column. How can I use allowNull constraint on a column based on the value of another column?
For example consider I have columns A and B. In the migration I wanna have something like below:
queryInterface.changeColumn('book', ['A'], {
allowNull: false,
where: { B: true }
});
But as I see in the examples, we can't use 'where' in changeColumn.

I think you must use customValidator for this problem like this :
queryInterface.changeColumn('book', ['A'], {
allowNull: true,
validate: {
customValidator(value) {
if (value === null && this.B) {
throw new Error("A not be null if B === true");
}
}
}
});

Related

sqlMessage: "Column 'updated_at' in where clause is ambiguous" - NodeJS Sequelize

created_at and updated_at
my code :
if(keyword){
let keywordSplit = keyword.split('&');
console.log('split', keywordSplit);
keywordSplit.map(el => {
const data = el.split("=");
advanceSearchCondition.push({
[`$${data[0]}$`]: { // here is column
[Op.substring]: `${data[1]}` // here is '%keyword%'
// [Op.like]: Sequelize.literal(`\'%${data[1]}%\'`)
}
});
})
}
did you know why ? because all of my data have created_at and updated_at also column name and description is ambiguous, i have tried refactor all name and its work , but i think how about created_at and updated_at ? should i refactor ???
how to ignore the ambiguous for specify for example created_at for nodeJS ,
i really neeeded the column but i cant query operation for the column ,
oh ya , i make these project to snake_case on configuration sequelize.
also when i tried remove $ on column its not counter the error
but when i tried like these
updated_at: {
[Op.substring]: `${data[1]}`
// [Op.like]: Sequelize.literal(`\'%${data[1]}%\'`)
}
its work no ambiguous
updated_at: // not ambiguous
vs
[`$${data[0]}$`]: // ambiguous
vs
[`${data[0]}`]: // ambiguous
i know i can handle with if condition
if (data[0] == 'created_at') {
advanceSearchCondition.push({
created_at: {
[Op.substring]: `${data[1]}`
}
});
} else if (data[0] == 'updated_at') {
advanceSearchCondition.push({
updated_at: {
[Op.substring]: `${data[1]}`
}
});
} else {
advanceSearchCondition.push({
[`$${data[0]}$`]: {
[Op.substring]: `${data[1]}`
}
});
}
but i looking for the best practice any other solution ?

Sequelize Parens in WHERE Clause

I have a WHERE clause that looks like this.
WHERE (`version` = FALSE
OR (name LIKE '%8%')
OR (name LIKE '%9%')
AND (sId IN (1)))
I need it to look like this.
WHERE (`version` = FALSE
OR (name LIKE '%8%')
OR (name LIKE '%9%'))
AND (sId IN (1))
I'm using where.push and I can't seem to figure it out.
Sequelize code.
where.push(version: false)
where.push(Sequelize.literal(`OR (name LIKE '%${id1}%')`)
where.push(Sequelize.literal(`OR (name LIKE '%${id2}%')`)
where.push({sId: {$in: sIds}})
Don't use Sequelize.literal (and definitely don't try to compose such conditions yourself in a string to avoid SQL injections) if you can write such conditions using some Op operators.
In your case you can use Op.or and Op.like operators:
where = {
[Op.or]: [{
version: false,
}, {
name: {
[Op.like]: `%${id1}%`
}
}, {
name: {
[Op.like]: `%${id2}%`
}
},
],
sId: {
[Op.in]: sIds
}
}

Is case-insensitive comparison of string possible for 'where' clause in vuex ORM?

While filtering data from the store, I need to check whether 'name' field of the data is 'stackoverflow'. So I use:
data() {
myname: 'stackoverflow'
},
computed: {
user() {
return this.$store.getters['entities/users/query']().where('name', myname).first();
}
}
It works perfectly if the name is given as 'stackoverflow', but not for 'StackOverflow'. Can the 'where' clause be modified so that it checks case insensitive?
I have never used the vuex-orm but i think this should work, according to the docs
https://vuex-orm.github.io/vuex-orm/guide/store/retrieving-data.html#simple-where-clauses
computed: {
user() {
return this.$store.getters['entities/users/query']().where(user => user.name.toUpperCase() === this.myname.toUpperCase()).first();
}
}
Or even
computed: {
user() {
return this.$store.getters['entities/users/query']().where('name', value => value.toUpperCase() === this.myname.toUpperCase()).first();
}
}

Waterline ORM equivalent of insert on duplicate key update

I have a table user_address and it has some fields like
attributes: {
user_id: 'integer',
address: 'string' //etc.
}
currently I'm doing this to insert a new record, but if one exists for this user, update it:
UserAddress
.query(
'INSERT INTO user_address (user_id, address) VALUES (?, ?) ' +
'ON DUPLICATE KEY UPDATE address=VALUES(address);',
params,
function(err) {
//error handling logic if err exists
}
Is there any way to use the Waterline ORM instead of straight SQL queries to achieve the same thing? I don't want to do two queries because it's inefficient and hard to maintain.
The answer above is less than ideal. It also has the method as part of the attributes for the model, which is not correct behavior.
Here is what the ideal native solution looks like that returns a promise just like any other waterline model function would:
module.exports = {
attributes: {
user_id: 'integer',
address: 'string'
},
updateOrCreate: function (user_id, address) {
return UserAddress.findOne().where({user_id: user_id}).then(function (ua) {
if (ua) {
return UserAddress.update({user_id: user_id}, {address: address});
} else {
// UserAddress does not exist. Create.
return UserAddress.create({user_id: user_id, address: address});
}
});
}
}
Then you can just use it like:
UserAddress.updateOrCreate(id, address).then(function(ua) {
// ... success logic here
}).catch(function(e) {
// ... error handling here
});
Make a custom model method that does what you want using Waterline queries isntead of raw SQL. You will be doing two queries, but with Waterline syntax.
Example below (if you don't know about deferred objects then just use callback syntax, but the logic is the same):
var Q = require('q');
module.exports = {
attributes: {
user_id: 'integer',
address: 'string',
updateOrCreate: function (user_id, address) {
var deferred = Q.defer();
UserAddress.findOne().where({user_id: user_id}).then(function (ua) {
if (ua) {
// UserAddress exists. Update.
ua.address = address;
ua.save(function (err) {deferred.resolve();});
} else {
// UserAddress does not exist. Create.
UserAddress.create({user_id: user_id, address: address}).done(function (e, ua) {deferred.resolve();});
}
}).fail(function (err) {deferred.reject()});
return deferred.promise;
}
};
#Eugene's answer is good but it will always run 2 operations: findOne + update or create. I believe we can optimize it further because if the record exists we just need to run update. Example:
module.exports = {
attributes: {
user_id: 'integer',
address: 'string'
},
updateOrCreate: function (user_id, address) {
return UserAddress.update({user_id: user_id}, {address: address})
.then(function(ua){
if(ua.length === 0){
// No records updated, UserAddress does not exist. Create.
return UserAddress.create({user_id: user_id, address: address});
}
});
}
}
BTW, there is an open request to implement .updateOrCreate in waterline: #790

breezejs addEntityType issue

I'm new to breezejs. I am trying to define my entity type in the client without getting metadata from the server. I have a property called ID in the server entity.
I've defaulted the naming convention in the client side to camel case using the following code.
breeze.NamingConvention.camelCase.setAsDefault();
so, I started to map the entity as follows
store.addEntityType({
shortName: "Photo",
namespace: "MyProj.Models",
dataProperties: {
id: {
dataType: DataType.Guid,
isNullable: false,
isPartOfKey: true
},
title: {
dataType: DataType.String
},
description: {
dataType: DataType.String
},
createdDate: {
dataType: DataType.DateTime
},
}
});
This worked all fine, except the id field is not getting the proper value. instead, it has the default value set by the breeze datatype ctor which is equals to Guid.Empty.
by stepping through breezejs debug script, I found out that it looks for a property name called Id in the data that comes from the ajax request. But it can't find it as the property is ID so it initialize it to empty guid string. I assumed that by setting nameOnServer property of the dataProperty id, I will be able to fix it.
store.addEntityType({
shortName: "Photo",
namespace: "MyProj.Models",
dataProperties: {
id: {
dataType: DataType.Guid,
isNullable: false,
nameOnServer: 'ID',
isPartOfKey: true
},
title: {
dataType: DataType.String
},
description: {
dataType: DataType.String
},
createdDate: {
dataType: DataType.DateTime
},
}
});
But it didn't work.
Further digging through the breez.debug.js code, in the method updateClientServerNames on line 7154, it seems it ignores the nameOnServer that I have defined.
Am I missing something here?
Okay, Feel like I spent my whole life through breeze documentation. Anyways, Finally solved the issue. To be honest, this wasn't a problem in breeze (but I wonder why it doesn't override the actual nameOnServer when I provide one). It's an error made by one of the developers in the early stage of the database implementation (probably 6 years ago). If the database adhered to Pascal Case naming convention, things would have worked perfectly fine.
As a solution I wrote a custom naming convention which corrects the naming convention error when it has ID in the name and combines it with camelCase naming convention.
var createInconsistenIDConvention = function () {
var serverPropertyNameToClient = function (serverPropertyName, prop) {
if (prop && prop.isDataProperty && (prop.nameOnServer && prop.nameOnServer === "ID")) {
return "id";
} else {
var firstSection = serverPropertyName.substr(0, 1).toLowerCase();
var idSection = "";
if (serverPropertyName.substr(1).indexOf("ID") != -1) {
firstSection += serverPropertyName.substr(1, serverPropertyName.substr(1).indexOf("ID")).toLowerCase() + "Id";
} else {
firstSection += serverPropertyName.substr(1);
}
return firstSection;
}
}
var clientPropertyNameToServer = function (clientPropertyName, prop) {
if (prop && prop.isDataProperty && (prop.nameOnServer && prop.nameOnServer.indexOf("ID") != -1)) {
return prop.nameOnServer;
} else {
return clientPropertyName.substr(0, 1).toUpperCase() + clientPropertyName.substr(1);
}
}
return new breeze.NamingConvention({
name: "inconsistenID",
serverPropertyNameToClient: serverPropertyNameToClient,
clientPropertyNameToServer: clientPropertyNameToServer
});
};
Not sure if the way I've used nameOnServer property is not correct. I couldn't find any documentation on that in breeze website.
please note that the above code only consider situations like ID, CountryID, GameID, PersonID etc.
Problem solved for now.