How to get auto Id after upsert on a persisted model in loopback? - postgresql

I have a some models generated from postgresql db using looback-connector postgresql. Id column of these models is a auto incremented integer column of postgresql db.
1) I have a remote method added on one of persisted models, where i perform simple update or insert(upsert.
Car.CreateOrUpdateCar = function (carobj, req) {
Car.upsert(Carobj, function (err, Car) {
if (err)
console.log(err);
else {
req(err, Car);
}
});
};
2) have added a remote hook to execute after this remote method.
Car.afterRemote('CreateOrUpdateCar', function (context, remoteMethodOutput, next) {
//Remaining code goes here
next();
});
3) I want to use Id of newly inserted row in step (1), in the remote hook mentioned in step (2)

I don't have much idea about postgresql db. But Try it like this
var carObj;
Car.CreateOrUpdateCar = function (carobj, req) {
Car.upsert(Carobj, function (err, Car) {
if (err)
console.log(err);
else {
req(err, Car); // Your Car object contains final result after upserting along with Id
carObj = Car;
}
});
};
Now you can get id by using carObj.id and you can use it where ever you want. I hope this helps

You can access to generated id in remote hook like this :
Car.afterRemote('CreateOrUpdateCar', function (context, remoteMethodOutput, next) {
var genId = remoteMethodOutput.id || context.result.id;
next();
});

Related

Mongodb delete where parent does not exist

I have a collection called faculty and another collection called program, if I want to delete faculty I need to check if faculty is not exist in any program, I mean faculty id is not associated with any program
Why this code is not working!!!
//Delete faculty
router.delete('/:id',(req,res)=>{
let programCount = Program.find({faculty:req.params.id}).count();
if(programCount > 0)
{
console.log('don\'t delete this')
}
you are using synchronous function in your router
thus,
you conditional statement if(programCount > 0) is already executed while the Program.find isn't finish yet. Use async/await
//Delete faculty
router.delete("/:id", async (req, res) => {
let programCount = await Program.find({ faculty: req.params.id }).count();
if (programCount > 0) {
console.log("don't delete this");
}
});

Is there a way to check for errors first from inserting in two related rows before saving in database

I'm currently making job/task list as a test project to learn web development (REST API, Express.js, Postgresql 13 via pg-node).
The structure is that the user can add employees in various tasks and jobs.
1 job can have multiple tasks
1 task can have multiple employees
Employees cannot have more than 1 task in the same job, but can be assigned another task in a different job.
The flow in the UI is a modal that allows you to fill out the job details (name), add tasks, then assign the employees on the said task.
What I currently have was to create two endpoints being sent when clicking a "Create" button, one that creates the job, and the second that creates and assigns the employees to the tasks as I need to attach the job_id to the task, which I cannot do until it has already been generated/created. The problem with this is that in the case of an error in inserting a task, the job will already be created and saved in the database while failing to assign the task and employees, causing a conflict when the "Create" button in the UI.
What I want to do (if possible) is to create a single query that will do both the creation and insertion of the job and initial tasks and employee assignments. The query will then not save the job in the database if there are errors that occurred during the entire transaction (e.g. a task failed to be inserted due to failing a condition such as wrong data type, etc.).
DB Fiddle for the schema: https://www.db-fiddle.com/f/izPsVVxPZ8e9ZMPwbL9her/10
These are my 2 routes:
//Create Job
const {
name
} = req.body;
const job = await pool.query(`SELECT * FROM jobs WHERE
name = $1)`, [
job_name
]);
if (job.rows.length !== 0) {
return res.status(401).send("Job already exists.");
}
const newJob = await pool.query(
`INSERT INTO jobs (job_name) VALUES
($1) RETURNING *`,
[job_name]
);
res.json({ "message": "Job created successfully!" });
//Assign Task
const {
job_id
employee_id
} = req.body;
const checkTask = await pool.query(`SELECT * FROM treatments WHERE
job_id = $1 AND
employee_id $2`, [
req.params.id, employee_id
]);
if (checkTreatment.rows.length !== 0) {
return res.status(401).send("Technician already assigned in the same treatment and schedule.");
}
const newTaskAssignment = await pool.query(
`INSERT INTO treatments (job_id,
employee_id) VALUES
($1, $2) RETURNING *`,
[req.params.id, job_id]
);
res.json({ "message": "Task added to job successfully!" });
Also, if possible, how can I do bulk insert tasks/employee assignments through the API POST route, I read that it involves making an array, but I haven't delved into it yet, if you can also give me advice for it that would be great (or any resource to read, I'm currently reading documentation and stackoverflow topics).
Thank you in advance for helping it!
UPDATE: I managed to do it via following the tutorial from kb.objectrocket.com
It involves using Transactions (which I just learned last night, and are really awesome!). This is the code that solved my problem:
//2. Declare an asynchronous function for the PG transaction
async function execute() {
// Promise chain for pg Pool client
const client = await pool
.connect()
.catch(err => {
console.log("\nclient.connect():", err.name);
// iterate over the error object attributes
for (item in err) {
if (err[item] = undefined) {
process.stdout.write(item + " - " + err[item] + " ");
}
}
//end the Pool instance
console.log("\n");
process.exit();
});
try {
//Initiate the Postgres transaction
await client.query("BEGIN");
try {
const sqlString = `WITH INSERTED AS (
INSERT INTO jobs (job_name) VALUES
($1) RETURNING id)
INSERT INTO tasks(
employee_id, job_id) VALUES
($1,(
SELECT id FROM inserted
))`;
const sqlValues = [job_name, employee_id
];
// Pass SQL string to the query() method
await client.query(sqlString, sqlValues, function(err, result) {
console.log("client.query() SQL result:", result);
if (err) {
console.log("\nclient.query():", err);
// Rollback before executing another transaction
client.query("ROLLBACK");
console.log("Transaction ROLLBACK called");
} else {
client.query("COMMIT");
console.log("client.query() COMMIT row count:", result.rowCount);
}
});
} catch (er) {
// Rollback before executing another transaction
client.query("ROLLBACK");
console.log("client.query():", er);
console.log("Transaction ROLLBACK called");
}
} finally {
client.release();
console.log("Client is released");
}
}
execute();
res.json({ "message": "Service job created successfully!" });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
});
Thank you!

Feather.js + Sequelize + postgres 11 : How to patch a jsonb column?

I would like to update several row of my db with the same object.
let say I have a column customText type jsonb which contains an array of object
here my sequelize model :
customText: {
type: DataTypes.JSONB,
allowNull: true,
field: "custom_text"
}
Now from client I send an object:
const obj = {}
const data = {
textid: "d9fec1d4-0f7a-2c00-9d36-0c5055d64d04",
textLabel: null,
textValue: null
};
obj.customText = data
api.service("activity").patch(null, obj).catch(err => console.log(err));
Like the documentation from feathers.js said if I want to replace multiple record, I send an id equal to null.
So now here come the problem, if I do that my column customText will contain the new object only but I want an array of object, so I want to push the new data in the array. How can I patch the data?
My guess is to use a hook in feathers.js and a raw query with sequelize. But I'm not sure how to do that.
I'm not really sure of my answer but this hook work :
module.exports = function() {
return async context => {
debugger;
const sequelize = context.app.get("sequelizeClient");
const customText = JSON.stringify(context.data.customText[0]);
console.log(customField);
let query =
"UPDATE activity SET custom_text = custom_text || '" +
customText +
"' ::jsonb";
console.log(query);
await sequelize
.query(query)
.then(results => {
console.log(results);
context.results = results;
})
.catch(err => console.log(err));
return context;
I still have a problem because after this hook in feathers, the patch continue so it will update my db again.. so i put a disallow() hook.
Also, with this hook i lost the abilities to listening to event
Also i have a concern with the query, i'm not sure if it's better to use :jsonb_insert over ||

How can I drop all tables with Sequelize.js using postgresql?

I am trying:
if (process.NODE_ENV === 'test') {
foreignKeyChecks = 0;
forceSync = true;
} else {
foreignKeyChecks = 1;
forceSync = false;
}
global.db.sequelize.query("SET FOREIGN_KEY_CHECKS = " + foreignKeyChecks).then(function() {
return global.db.sequelize.sync({
force: forceSync
});
}).then(function() {
return global.db.sequelize.query('SET FOREIGN_KEY_CHECKS = 1');
}).then(function() {
var server;
console.log('Initialzed database on:');
console.log(config.db);
return server = app.listen(port, function() {
return console.log("Server listening at http://" + (server.address().address) + ":" + (server.address().port));
});
})["catch"](function(err) {
return console.log('err', err);
});
module.exports = app;
But I get: SequelizeDatabaseError: unrecognized configuration parameter "foreign_key_checks"
I assume I can't have that keyword in postgres? But is there an equivalent way to drop all tables and recreate?
This is an updated answer, targeted at the googlers who wound up here like me.
Sequelize offers a drop function:
drop(options) => promise
Drop all tables defined through this sequelize instance. This is done by calling Model.drop on each model. Sequelize docs
Example
var sequelize = new Sequelize(config.database, config.username, config.password, config);
var someModel = sequelize.define('somemodel', {
name: DataTypes.STRING
});
sequelize
.sync() // create the database table for our model(s)
.then(function(){
// do some work
})
.then(function(){
return sequelize.drop() // drop all tables in the db
});
For wiping out data and create all again from scratch (like in tests):
sequelize.sync({force: true});
I don't know anything about that JavaScript library, but Postgres provides a single command to drop everything that is owned by a user:
drop owned by <our_user_name cascade
This will only work if everything is owned by the same user and that user doesn't have some tables (or views, sequences, ...) that you do not want to drop.
More details in the manual:
http://www.postgresql.org/docs/current/static/sql-drop-owned.html
For anyone looking for a solution with sequelize-cli checkout this link Sequelize CLI:
You can just run:
sequelize_cli db:drop
sequelize_cli db:create
To create or drop your db using the cli tool. This way you will have a empty db.

Create an object from multiple database collections (SailsJS, MongoDB, WaterlineJS)

I'm very new to Sails and noSQL databases and I'm having trouble gathering information together from different collections. Basically I need to gather an object of items from one collection and then use a foreign key stored in that collection to add data from a separate collection so the whole thing can be sent as one object.
Currently I find all the items in a collection called Artwork, then I'm using a for loop to iterate through the artworks. I need to use an id stored in Artworks to query a collection called Contacts but having successfully found the contact I am unable to pass it back out of the function to add it to the Artwork object.
find: function ( req, res, next ) {
Artwork.find().done( function ( err, artwork ) {
// Error handling
if (err) {
return console.log(err);
} else {
for ( x in artwork ) {
var y = artwork[x]['artistID'];
// Get the artsists name
Contact.find(y).done( function( err, contact ) {
// Error handling
if ( err ) {
return console.log(err);
// The Artist was found successfully!
} else {
var artist = contact[0]['fullName'];
}
});
artwork[x]['artistsName'] = artist;
}
res.send(artwork);
}
});
}
The result of the above code is an error thrown that tells me 'artist' is undefined. The variable is not being passed outside the function?
Any advice greatly received.
Sails is about to release an update that will include associations. In the meantime, here's an answer for how you can accomplish it using async. https://stackoverflow.com/a/20050821/1262998