Insert after another insert in transaction with pg-promise - node.js

Ok, I have looked but didn't find anything that worked for me. If you can point me to anything or help my find the solution it would be great.
Let's say I have a "users" table (columns: id, username and name) and a "users_items" table (columns: id_item, id_user, both Foreign Keys)
What I want to do: I want to insert a new user and assign him a default item that everyone should have. For that, I want to create a transaction where everything or nothing is saved. So if (for any reason) I can't give him that item, I want the user creation to fail.
How I do it:
const pgp = require('pg-promise')(
{
capSQL: true // generate capitalized SQL
});
const db = pgp(configuration);
saveUser = (username, name) =>
db.tx (t =>
t.one('INSERT INTO users (username, name) VALUES $1, $2 RETURNING *', [username, name]).then(user =>
t.none ('INSERT INTO users_items (id_user, id_item) VALUES $1, 1', [user.id]).then(()=>
console.log('Everything\'s alright :)');
)
);
What I expect: everything runs perfectly and we are all happy :).
What actually happens: The first instruction is OK, and returns correctly the user with the ID. However, the second one tells me that the constraint fk_id_user is being violated, and perform a ROLLBACK.
The part of it not commiting the first insert is working properly. However, shouldn't the second insert work too? Am I understanding something extremely wrong or is pg-promise not working as expected? Or maybe I need to do something different.. Any help would be appreciated.

Related

Most performant way to Insert or Read(if record already exists) in Google Cloud Spanner

Assuming I have a cars table where vin is the primary key.
I want to insert a record(in a transaction) or read the record(if one already exists with the same PK).
What's the most performant way to insert the record or read it if one already exists with the same PK?
This is my current approach:
Case A: Record does not exist
Insert record
Return record
Case B: Record already exists
Insert record
Check if error is due to the record already existing
Read the record
Return record
const car = { vin: '123', make: 'honda', model: 'accord' };
spannerDatabase.runTransactionAsync(async (databaseTransaction) => {
try {
// Try to insert car
await databaseTransaction.insert('cars', car);
await databaseTransaction.commit();
return car;
} catch (error) {
await databaseTransaction.end();
// Spanner "row already exists" error. Insert failed because there is already a record with the same vin(PK)
if (error.code === 6) {
// Since the record already exists, I want to read it and return it. Whats the most performant way to do this?
const existingRecord = await carsTable.read({
columns: ['vin', 'make', 'model'],
keys: [car.vin],
json: true,
});
return existingRecord;
}
}
})
As #skuruppu mentioned in the comment above, your current example is mostly fine for what you are describing. It does however implicitly assume a couple of things, as you are not executing the read and the insert in the same transaction. That means that the two operations together are not atomic, and other transactions might update or delete the record between your two operations.
Also, your approach assumes that scenario A (record does not exist) is the most probable. If that is not the case, and it is just as probable that the record does exist, then you should execute the read in the transaction before the write.
You should also do that if there are other processes that might delete the record. Otherwise, another process might delete the record after you tried to insert the record, but before you try to read it (outside the transaction).
The above is only really a problem if there are other processes that might delete or alter the record. If that is not the case, and also won't be in the future, this is only a theoretical problem.
So to summarize:
Your example is fine if scenario A is the most probable and no other process will ever delete any records in the cars table.
You should execute the read before the write using the same read/write transaction for both operations if any of the conditions in 1 are not true.
The read operation that you are using in your example is the most efficient way to read a single row from a table.

Typeorm querybuilder update get updated result

I'm running a query-builder that updates multiple users based on last logged in date, meaning I don't know which users are getting updated. Here is my code, which does not provide information about which users it updated.
await getConnection()
.createQueryBuilder()
.update(User)
.set({
isDeactivated: true
})
.where('lastConnected < :someTimeAgo', { someTimeAgo })
.andWhere('isDeactivated = :isDeactivated', { isDeactivated: false })
.execute()
.then(result => {
// Result: UpdateResult { generatedMaps: [], raw: undefined }
})
How can I access the updated data? Database is SQL Server.
Normally you cannot find which rows were updated by an UPDATE statement in SQL, hence tyeorm cannot tell you.
Here are several solutions if you REALLY need to know which rows were updated.
Before go ahead, ask WHY do you need to know? Do you REALLY need to know?
If, after careful consideration, you find you need to know which rows were updated, there are several solutions:
In your code, find the users to be deleted, then delete them one at a time, logging info on each one as you go.
Create a table in the database containing the user id's to be deactivated. Populate this table first: INSERT INTO deactivatedusers (userid) SELECT userid FROM users WHERE ... then run UPDATE users SET isDeactivated = 1 WHERE userid IN SELECT userid FROM deactivatedusers then to find which users were deactivated: SELECT userid FROM deactivatedusers and finally clear deactivatedusers ready for next time, either with DELETE FROM deactivatedusers or TRUNCATE TABLE deactivatedusers
Since you are using MS SQL Server, this provides OUTPUT INTO specifically to do what you are asking (non standard SQL, so only works with this DBMS). If you decide to use this approach, you should write a stored procedure to do the update and return the updated data back to caller, then call this stored proc from typeorm.

Azure CosmosDB/Nodejs - Entity with the specified id does not exist in the system

I am trying to delete and update records in cosmosDB using my graphql/nodejs code and getting error - "Entity with the specified id does not exist in the system". Here is my code
deleteRecord: async (root, id) => {
const { resource: result } = await container.item(id.id, key).delete();
console.log(`Deleted item with id: ${id}`);
},
Somehow below code is not able to find record, even "container.item(id.id, key).read()" doesn't work.
await container.item(id.id, key)
But if I try to find record using query spec it works
await container.items.query('SELECT * from c where c.id = "'+id+'"' ).fetchNext()
FYI- I am able to fetch all records and create new item, so Connection to DB and reading/writing is not an issue.
What else can it be? Any pointer related to this will be helpful.
Thanks in advance.
It seems you pass the wrong key to item(id,key). According to the Note of this documentation:
In both the "update" and "delete" methods, the item has to be selected
from the database by calling container.item(). The two parameters
passed in are the id of the item and the item's partition key. In this
case, the parition key is the value of the "category" field.
So you need to pass the value of your partition key, not your partition key path.
For example, if you have document like below, and your partition key is '/category', you need to use this code await container.item("xxxxxx", "movie").
{
"id":"xxxxxx",
"category":"movie"
}

Dynamodb putItem written twice

I am new to AWS and I feel like I am missing something important.
I am using this code from a lambda function in nodeJS to create an entry in a DynamoDB table :
function recordUser(item) {
return ddb.putItem({
TableName: 'Users',
Item: item,
Expected: {
username: { Exists: false }
}
}).promise();
}
username is the primary key of my table.
I though the condition would restrain duplicates to appear but I still see some duplicated entries with same username, what am I missing ?
You are giving "Expected" a wrong interpretation... You seemed to hope that it checks whether there is any existing item in the database with the given value for the "username" attribute. But this is not what Expected does... It does something very different: It reads one specific item - the item with the same key as the one you specified in "Item", and then check whether for this specific item, a value (any value!) exists for its "username" attribute.
To suggest how to fix your use case, we would need to know more about your data. The easiest solution is, of course, to have a table whose sole key is "username", which will allow just one item per username. But I don't know if this is good enough for your usecase.

How to change and save the association of a model instance?

I'm using sequelize for node.js. I define this relationship:
// 1:M - Platform table with Governance status table
dbSchema.Platform_governance.belongsTo(dbSchema.Governance_status, {foreignKey: 'platform_status'});
dbSchema.Governance_status.hasMany(dbSchema.Platform_governance, {foreignKey: 'platform_status'});
So that means I have a table called platform_governance which has a foreign key that points to a governance_status row. A user wants to change this foreign key to point to a different governance_status row.
So I want to first search that this governance_status row the user selected actually exists and is unique, and then make the foreign key point to it. This is what I currently have:
// first I select the platform_governance of which I want to change it's foreign key
dbSchema.Platform_governance.findById(5, {include: [dbSchema.Governance_status]}).then(result => {
// Now I search for the user-requested governance_status
dbSchema.Governance_status.findAll({where:{user_input}}).then(answer => {
// I check that one and only one row was found:
if (answer.length != 1 ) {
console.log('error')
} else {
// Here I want to update the foreign key
// I want and need to do it through the associated model, not the foreign key name
result.set('governance_status', answer[0])
result.save().then(result => console.log(result.get({plain:true}))).catch(err => console.log(err))
}
The result.save() promise returns successfully and the object printed in console is correct, with the new governance_status correctly set. But if I go to the database NOTHING has changed. Nothing was really saved.
Oops just found the problem. When setting associations like this you shouldn't use the set() method. Instead, sequelize creates setters for each association. In my case I had to use setGovernance_status():
// Update corresponding foreign keys
result.setGovernance_status(answer[0])
If anyone finds anywhere in the documentation where this is documented I would appreciate :)

Resources