omitNull not working on primaryKey column in Sequelize - sql-server

I have a SQL server database with a table which auto inserts a guid in each row when a record is added. This column is the primary key for the table and never needs a value to be supplied to it as it's automatic.
The trouble is that Sequelize is sending NULL for this column every time I do a .create({emailAddress:"test#test.com"}) call which is causing an error in the database (because nulls aren't allowed, obviously).
I've tried adding omitNull at the top level and at the "call" level and neither of them work , unless I remove the primary key and then it doesn't send a NULL. So it seems that Sequelize thinks that if something is the primary key then it must send a value, not understanding that the SQL SERVER database is going to handle insertion of that value.
Anybody know a workaround?
// Model
module.exports = function (sequelize, DataTypes) {
const Player = sequelize.define('Player', {
playerId: {
primaryKey: true,
type: DataTypes.STRING
},
emailAddress: {
type: DataTypes.STRING
}
}, {
timestamps: false
});
return Player;
};
// Create a row
let newPlayer = {
emailAddress:'test#test.com'
}
Player.create(newPlayer, {omitNull:true}).then(function(player){
console.log("player", player)
}).catch(function(error){
console.log("error", error)
})

Adding defaultValue and allowNull should do the job
playerId: {
primaryKey: true,
defaultValue: '',
allowNull: false,
}

Related

How to save entity with a foreign key referencing itself (recursive foreign key) in TypeORM

I'm new to TypeORM and I'm stuck in a chicken/egg scenario. I have the following entity:
#Index("institutes_pkey", ["idInstitute"], { unique: true })
#Entity("institutes", { schema: "public" })
export class Institutes {
#PrimaryGeneratedColumn({ type: "bigint", name: "id_institute" })
id_institute: string;
#Column("text", { name: "name" })
name: string;
#Column("text", { name: "description" })
description: string;
#ManyToOne(() => Institutes, (institutes) => institutes.institutes)
#JoinColumn([{ name: "id_grupo", referencedColumnName: "idInstitute" }])
id_group: Institutes;
#OneToMany(() => Institutes, (institutes) => institutes.id_group)
institutes: Institutes[];
}
child_institutes can be grouped under the umbrella of a bigger father_institute.
In that case the the id groups would look like this:
father_institute.id_group = father_institute.id_institute
child_institutes.id_group = father_institute.id_institute.
If a institute won't be part of any group, it's id_group equals it's id_institute (same as father case). And here lies my issue, when I need to save a father_institute, it's FK references the very same object I'm trying to save. What can I do in that case?
I imagined a workaround, create a sequence in PSQL and set id_institute with that sequence then set the default value of id_group to the current value of this sequence. But is there any better solution for this?

Upsert ref documents when pushing to array in Express

I have a Candidate schema with an array of refs to an Endorser schema. Like so:
const CandidateSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
endorsements: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Endorser'
}]
});
const EndorserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
});
I receive endorsements in the form of an array of strings (i.e., the endorser's name). I want to iterate through that array and retrieve the _id of each from the Endorser model or upsert a new one if it doesn't exist. I then want to push those refs onto the existing candidate instance.
The following code works, but I really don't feel comfortable modifying the candidate instance in-memory. Having to do the promise resolution separately also seems weird.
const endorsementPromises = endorsements.map(async endorser => {
let endorserObj = await Endorser.findOneAndUpdate({name: endorser}, {name: endorser}, {upsert: true, new: true});
return endorserObj._id;
});
const endorsementArray = await Promise.all(endorsementPromises);
candidate.endorsements = candidate.endorsements.concat(endorsementArray);
await candidate.save();
I have tried using findOneAndUpdate with $push and $each. However, this only returns an error and doesn't update the document.
Candidate.update(
{id: candidate._id},
{$push: {
endorsements: {
$each: endorsementArray
}
}}
);
// the error
Error: {"n":0,"nModified":0,"ok":1}
I'm not sure why $push and $each aren't updating the document.
Any guidance would be really appreciated.
Try using $addToSet instead of $push. Also, it seems like you should be matching on _id instead of id in your update.

Work around Sequelize’s unique constraints in belongsToMany associations

I'm using Sequelize in my project. These are the two models:
const User = db.define('user', {
name: Sequelize.STRING,
password: Sequelize.STRING
})
const Product = db.define('product', {
name: Sequelize.STRING,
price: Sequelize.INTEGER
})
Now users can purchase products and I have associations setup like below:
Product.belongsToMany(User, {through: 'UserProducts'})
User.belongsToMany(Product, {through: 'UserProducts'})
I also have this UserProducts table with an additional column.
const UserProducts = db.define('UserProducts', {
status: Sequelize.STRING
})
Sequelize creates a composite key with combination of userId and productId and will only allow one record with a combination of userId and productId. So, for example, userId 2 and productId 14.
This is a problem for me because sometimes people want to purchase multiple times. I need one of the following scenarios to work:
Don't use the composite key and instead have a completely new auto-increment column used as key in UserProducts.
Instead of making key with userId and productId alone, allow me to add one more column into the key such as the status so that unique key is a combination of the three.
I do want to use the associations as they provide many powerful methods, but want to alter the unique key to work in such a way that I can add multiple rows with the same combination of user id and product id.
And since my models/database is already running, I will need to make use of migrations to make this change.
Any help around this is highly appreciated.
If anyone else is having problems in v5 of Sequelize, it is not enough to specify a primary key on the 'through' model.
You have to explicitly set the unique property on the through model.
User.belongsToMany(Product, { through: { model: UserProducts, unique: false } });
Product.belongsToMany(User, { through: { model: UserProducts, unique: false } });
Belongs-To-Many creates a unique key when the primary key is not present on through model.
Since you also have additional property in your UserProducts table, you need to define the model for UserProducts and then tell the sequelize to use that model instead of creating its own
class User extends Model {}
User.init({
name: Sequelize.STRING,
password: Sequelize.STRING
}, { sequelize })
class Product extends Model {}
ProjProductect.init({
name: Sequelize.STRING,
price: Sequelize.INTEGER
}, { sequelize })
class UserProducts extends Model {}
UserProducts.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
}, { sequelize })
User.belongsToMany(Project, { through: UserProducts })
Product.belongsToMany(User, { through: UserProducts })
refer: Sequelize v4 belongsToMany
UPDATE
since you are using v3 and already have a model created for your UserProducts table use following snippet
UserProducts = db.define('UserProducts', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
status: DataTypes.STRING
})
Since setting explicitly the unique property on the through model is not working in v6, the only solution i found is to define the 3 parts of the association this way :
User.hasMany(UserProducts);
UserProducts.belongsTo(User);
Product.hasMany(UserProducts);
UserProducts.belongsTo(Product);
You can then create your models and associations :
const user = await User.create(user_data);
const product = await Product.create(product_data);
const up = await UserProduct.create(up_data);
await up.setUser(user);
await up.setProduct(product);
If anyone has a better solution, I would be happy to know it

Ajv return valid schema if schema has required set and data is empty string

Hi I have just started to learn AJV and I am a bit confused on how it handles required fields. I am working on an already built project and maybe some of the configuration is not needed for the current use case, or is done wrong.
I am using AJV verion 5.5.1.
Here is my code:
const opt = {
$data: true, allErrors: true, verbose: true, unknownFormats: ['int32', 'int64', 'double']
};
opt['v5'] = true;
const validator = withKeywords(new Ajv(opt), ['switch', 'if']);
withError(this.validator);`enter code here`
const schema = {
'required': [
'name'
],
'type': 'object',
'properties': {
'name': {
'type': 'string'
}
}
}
const data = {
name: ''
}
const isValid = validator.validate(schema, data);
Now this will set the isValid variable to true and validator.errors has no errors which kind of confuses me a bit. Since what i hoped would happen was it to be false because at the schema level I am setting in the required property the 'name' key.
I also tryed setting data.name = null an I am getting isValid false with error that makes sense to me because I am setting the type to string at the schema level, but it is not a validation required error. I want be able to diferentiate beetween a required error and a type error.
How can I make the validator return false if a property does not have a value?

Sequelize issue interting rows on tables with triggers

I'm currently working with SQL Server and Sequelize on a project. I'm facing a problem when trying to insert rows in a table that has a trigger associated with it.
I was able to find this information about the problem https://github.com/sequelize/sequelize/issues/3284
I have downloaded the .js files and what it says is that it works with a temp table to overcome this issue:
SequelizeDatabaseError: The target table 'tableA' of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause.
This solution is not working for me, I have issues when trying to re use the object that were interseted and can't access to all of the variables.
Is there any other walk around for this issue?
This is my code:
return Person.create( { lastName: options.lastName,
name: options.name,
}, {transaction: t}).then(
function (person) {
return Student.create({ id :person.id,
numberRecord :'PF-'+person.id,
}, {transaction: t}).then(function (student) {
return Shoots.create({ id : person.id,
numberRecord :'PF-'+person.id
}, {transaction: t}).then(function (record) {
return StudentContract.create({
PersonID :person.id,
dateStart:'2016021 00:00:00.000',
endDate:'20161231 00:00:00.000',}
, {transaction: t}).then(function (contract) {
return student;
});
});
});
});
}).then(function (result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback is
console.log(result);
cb(null, result);
}).catch(function (err) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback is
console.error(+err);
cb(err, "Transaction Failed");
});
When using the solution provided in the link above, it says that it can't find the 'id' property. 'id' is defined in Person and Student as the primary key.
Any ideas or official solution from sequelize to work this issue?
Thanks!
Set hasTrigger to true when defining the table, and Sequelize will take care of this for you. For example:
sequelize.define(TABLE_NAME,
{
id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true
},
description: {
type: Sequelize.VARCHAR(8000)
}
},
{
// options
timestamps: false,
hasTrigger: true
});
I have read your question and I understood that you need to capture what you inserted. And yes it is possible to capture the inserted data with table variable.
Try below, hope it will help atleast to get some other idea.
DECLARE #IDENTITY_CAPTURE TABLE (INSERTED_IDENTITY BIGINT, INSERTED_BASE_NME VARCHAR(20))
INSERT INTO SALESORDER
OUTPUT INSERTED.SALESORDER_NBR, INSERTED.SALESORDER_NME INTO #IDENTITY_CAPTURE
SELECT 1, 'PICSART'
UNION ALL
SELECT 2, 'IMAX'
Then you can query on Table variable to get the data inserted.
Thank you

Resources