Unique doesn't work on Node.js Sails.js "sails-mysql" - mysql

I've just started to get into the framework of Sails for Node. But it seems like I can't get the unique- requirements to work when adding for example users to the sails-mysql database. I can atm add unlimited number of new users with the same username and email.
From what I have read it should work, I did also try with sails-memory and there this exact code did work. Is it something I have missed out?
module.exports = {
attributes: {
username: {
type: 'string',
required: true,
unique: true
},
firstname: {
type: 'string',
required: true
},
lastname: {
type: 'string',
required: true
},
password: {
type: 'string',
required: true
},
birthdate: {
type: 'date',
required: true
},
email: {
type: 'email',
required: true,
unique: true
},
phonenumber: 'string',
// Create users full name automaticly
fullname: function(){
return this.firstname + ' ' + this.lastname;
}
}
};
As I mentioned above, this does work with the memory-storage. And now I have also tried with mongodb where it does work fins as well.

Got support from Sails.js on twitter: "it uses the db layer- suspect it's an issue with automigrations. Would you try in a new MySQL db?"
This answer did work, a new db and everything was just working :)

Just to add to this, since sails uses auto-migrations, if you initially start the server and your model does not have an attribute as unique, the table is built without the unique (index) switch. If you then change an existing attribute in the model to unique, the table will not be rebuilt the subsequent times you start the server.
One remedy during development is to set migrations in your model to drop like this:
module.exports = {
migrate: 'drop' // drops all your tables and then re-create them Note: You loose underlying.
attributes: {
...
}
};
That way, the db would be rebuilt each time you start the server. This would of course drop any existing data as well.

Related

Why MYSQL column values gets modified/deleted issue?

I am building a React Nativeapp with MYSQL as the dabase and I am using SequelizeORM on Node.js. The problem is that I have a table called Like and there is a column field called userId and I simply store the ID of the users there. But the userId field gets cleared randomly. Like when I am restarting the database, or when there is an error and I need to restart the database or I am restarting the Android Emulator.
Here is how it looks:
CreateLikeModel.init({
id:{
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
userId:{
type: DataTypes.STRING,
allowNull: true
},
food_Name:{
type: DataTypes.STRING,
allowNull: false
},
food_id:{
type: DataTypes.INTEGER,
allowNull: false
}
},
And this is part of the function that saves the user Id when the user carry's out a like functionality:
const user = await CreateCustomer.findOne({where: {id: req.user.id}});
const likeObj ={
user_Id: user?.dataValues?.id?.toString(),
food_Name: food?.dataValues?.food_Name,
food_id: food.dataValues.id as number
}
//check if this user has liked this food
const checkUser = checkLike.find((single)=> single?.dataValues.userId ==
user.dataValues.id);
if(checkUser){
const getLike = await CreateLikeModel.findOne({where: {food_Name:
food.dataValues.food_Name, userId: user?.dataValues.id}});
await getLike?.destroy();//delete the like from the record if the user already liked;
return res.status(200).json('unliked');
}
await CreateLikeModel.create({...likeObj});
return res.status(200).json('liked');
And I connected to the database like this:
sequelizeDB.sync({alter: true}).then(()=>{
console.log('connected to datatbase')
})
I tried the save the userId as a string because it was a number before. Initially, when I used number, it usually reset the userId values to 0.
It still didn't solve the problem.
This was not happening before when I was using user_name instead of userId. What could be causing the issue? For now, I usually manually input the values back in the database when they get deleted.
Inside of likeObj, which you are using as the creation attributes for your like object, you have your User ID field as snake case user_Id, which in your model, I see it in camelCase userId.
I suspect this is the root of why it isn't populating the data correctly, however keep in mind that .sync({ alter: true }) can be a destructive operation, as state in the Sequelize documentation.

Unable to create BLOB/Binary types with LoopBack 4

I'm trying to use Loopback for my new projects, but I've been facing some problems...
I have the habit of storing my UUIDs ID in a binary format at my databases, here's an example:
#model({
settings: { mysql: { table: 'application' } },
})
export class Application extends Entity {
#property({
type: 'buffer',
required: true,
generated: false,
id: true,
dataLength: 16,
})
id: BinaryType;
[...]
}
But when I try to do the migration, I've been receiving that error from mysql:
"BLOB/TEXT column 'id' used in key specification without a key length"
I really tried everything and nothing works. Hope that you'll be able to help me!
Thanks a lot!
I'll show the answer for this question that I made.
Just define your model with the following info:
#property({
required: true,
mysql: {
columnName: 'application_id',
dataType: 'VARBINARY',
dataLength: 16,
nullable: 'NO'
}
})
application_id: string;
It worked like a charm for me :)
Thank you all!

NodeJS sequelize timestamps disabled but still getting Unknown column 'createdAt' error

I am building a new NodeJS application with MySQL. I need to use the existing database from the original version of the application. I am using a mysql dump file from the old database to create the new database.
I generated the models automatically based on the existing database using sequelize-auto module.
The createdAt field does not exist in the database and timestamps are specifically disabled in all of the models. However, I am still seeing this error when running a query such as models.people.findAll() or models.people.findOne():
"SequelizeDatabaseError: Unknown column 'createdAt' in 'field list'"
I followed the instructions in this other post to disable timestamps globally, however it does not work to solve the issue.
Sequelize Unknown column '*.createdAt' in 'field list'
Here are the relevant versions:
"mysql": "^2.17.1",
"mysql2": "^1.6.5",
"sequelize": "^5.8.5",
"sequelize-auto": "^0.4.29",
"sequelize-auto-migrations": "^1.0.3"
Here is the people model. Timestamps have been disabled explicitly in this and all other models globally. Also, I have read that timestamps : false is the default in the latest version of sequelize, so I am confused as to how this is an issue at all.
module.exports = function(sequelize, DataTypes) {
return sequelize.define('people', {
PersonID: {
type: DataTypes.INTEGER(10).UNSIGNED,
allowNull: false,
primaryKey: true
},
FirstName: {
type: DataTypes.STRING(50),
allowNull: false
},
LastName: {
type: DataTypes.STRING(50),
allowNull: false
},
Username: {
type: DataTypes.STRING(50),
allowNull: true
},
EmailAddress: {
type: DataTypes.STRING(255),
allowNull: true
}
}, {
tableName: 'people',
timestamps: 'false'
});
};
I see that you use sequelize-auto-migrations in your dependencies and I found an opened issue in their repo linked to your problem.
You maybe made a mistake setting the timestamps or the migrations are executed in the server after the disabling of the timestamps so the created_at columns are being added again to your tables.
I hope this helps you.

MySQL FullText Search with Sequelize

I want to implement MySQL full text search with sequelize. The version "sequelize": "^3.23.6". I tried to research about this but could not find =the documentation that guides how to implement this.
Here is the link that says FullText is supported by sequelize:
https://github.com/sequelize/sequelize/issues/2979
But there is not exact documentation on how to do it and how to do a full text search query with sequelize.
Any links advice would be helpful
Thanks !
Since we now have the error message in recent Sequelize that looks like this:
Unhandled rejection Error: Support for literal replacements in the where object has been removed.
The solution would be to provide replacements manually
Payments.findAll({
where: Sequelize.literal('MATCH (SomeField) AGAINST (:name)'),
replacements: {
name: 'Alex'
}
});
Use arrays for more complex conditions:
Payments.findAll({
where: [
{ State: 'Paid' },
Sequelize.literal('MATCH (SomeField) AGAINST (:name)')
],
replacements: {
name: 'Alex'
}
});
Sequelize doesn’t fully support the full-text search feature. We can add a FULLTEXT index as easy as any other index. But operators supporting the MATCH (column) AGAINST (value) syntax haven’t been implemented.
My current solution to the problem consists of creating a regular model:
module.exports = (sequelize, DataTypes) => {
const Book = sequelize.define('Book', {
title: DataTypes.STRING,
description: DataTypes.TEXT,
isActive: DataTypes.BOOLEAN
}, {
indexes: [
// add a FULLTEXT index
{ type: 'FULLTEXT', name: 'text_idx', fields: ['description'] }
]
});
return Book;
};
And using a raw query for querying:
const against = 'more or less';
models.Book.find({
where: ['isActive = 1 AND MATCH (description) AGAINST(?)', [against]]
}).then((result) => {
console.log(result.title);
});
Using only MySQL it's not possible to get correct results if you trying to search for inflectional words, synonyms etc. MySQL developers consider adding dictionaries for full-text search (https://dev.mysql.com/worklog/task/?id=2428), but who knows when we will see it.
If you have to stick with MySQL, I suggest to take a look at Sphinx. It works properly with synonyms and inflectional words.
Since Sequlize does not support for fullText search.
Here is another approach for searching a string from a table
models.sequelize.query(
"SELECT * FROM tableName WHERE CONCAT(field1, '', field2, '', field3, '', field4 ) LIKE \"%" + keyword + "%\"",
{type: models.sequelize.QueryTypes.SELECT}).then(res => {
})
another approach, using single migration file
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable(
'Products',
{
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
allowNull: false,
primaryKey: true,
},
name: {
type: Sequelize.STRING,
},
},
).then(() => queryInterface.addIndex('Products', ['name'], { type: 'FULLTEXT' })),
down: (queryInterface) => queryInterface.dropTable('Products'),
};
New updated Answer, Here I am searching for Locations that match my searchString.
Location.findAll({
where: {
name: sequelize.where(
sequelize.fn("LOWER", sequelize.col("name")),
"LIKE",
"%" + "You search text here"+ "%". <==== add your searchString Here
),
},
})

A Sequelize column that cannot be updated

Is it possible to create a column on a MySQL table using Sequelize that can be initialized when creating a new row, but never updated?
For example, a REST service allows a user to update his profile. He can change any field except his id. I can strip the id from the request on the API route, but that's a little redundant because there are a number of different models that behave similarly. Ideally, I'd like to be able to define a constraint in Sequelize that prevents the id column from being set to anything other than DEFAULT.
Currently, I'm using a setterMethod for the id to manually throw a ValidationError, but this seems hackish, so I was wondering if there's a cleaner way of doing this. Even worse is that this implementation still allows the id to be set when creating a new record, but I don't know a way around this as when Sequelize generates the query it calls setterMethods.id to set the value to DEFAULT.
return sequelize.define('Foo',
{
title: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
notEmpty: true
}
}
},
{
setterMethods: {
id: function (value) {
if (!this.isNewRecord) {
throw new sequelize.ValidationError(null, [
new sequelize.ValidationErrorItem('readonly', 'id may not be set', 'id', value)
]);
}
}
}
}
);
Look at this Sequelize plugin:
https://www.npmjs.com/package/sequelize-noupdate-attributes
It adds support for no update and read-only attributes in Sequelize models.
In your specific case, you could configure the attribute with the following flags:
{
title: {
type: DataTypes.STRING,
allowNull: false,
unique : true,
noUpdate : true
}
}
That will allow the initial set of the title attribute if is null, and then prevent any further modifications once is already set.
Disclaimer: I'm the plugin author.