TypeORM: how to .find() exact match? - node.js

I'm writing a TypeORM resolver to delete an entity with the following code:
Book schema in the database: id | idInAPI | userId
user is the other end of a many to one relation
Delete book query arguments are idInAPI and the context (to get the user).
const { userId } = req.session; // current user taken from cookie
const book= await Book.findOneOrFail({ where: [{ user: userId }, { idInAPI: bookId }] });
const response = await Book.delete(book.id);
The code does delete a book in my database, but my problem is that .find() looks for approximate values and not the exact one, so if I try to delete the book with id 12, the book with id 1234 might get deleted.
How can I force find to look for the exact match for user and book?

the issue is that you are using findOneOrFail method. You should use find() method this will search for the exact match

Solved it by passing the arguments directly to .delete() with the following syntax:
const response = await Book.delete({ idInAPI: bookId, user: { id: userId } });

Related

Perform check on record before performing update with Prisma

I'm creating the backend for a simple app which allows users to create, update, and delete products. Using Express as my framework, with Postgres as my DB and Prisma, which I'm new to, as my ORM. Users and products have a one-to-many relationship. Prisma's documentation states that when updating a record, you should use the update method - so to update the name of a product with a given ID, your code would look something like this:
export const updateProduct = async (req, res) => {
const [productID, newProductName, userID] = [req.params.id, req.body.name, res.locals.user.id];
const product = await prisma.product.update({
where: {
id: productID,
},
data: {
name: newProductName
}
});
res.status(200);
res.json(product);
};
However, there's a problem here - I'm not checking to see that the product with the provided ID belongs to the user that has sent the request to update it. I have the ID of the user who has sent the request in the variable userID, and each product in the DB has a field belongsToID which is set to the ID of the user that the product belongs to. I should theoretically therefore be able to modify my query to get the product with the specified ID and a matching belongsToID like so:
export const updateProduct = async (req, res) => {
const [productID, newProductName, userID] = [req.params.id, req.body.name, res.locals.user.id];
const product = await prisma.product.update({
where: {
id: productID,
belongsToID: userID
},
data: {
name: newProductName
}
});
res.status(200);
res.json(product);
};
That, however, does not work - I get the following error: Type '{ id: any; belongsToID: any; }' is not assignable to type 'ProductWhereUniqueInput'. Object literal may only specify known properties, and 'belongsToId' does not exist in type 'ProductWhereUniqueInput'.ts(2322).
It appears that when trying to do a 'findUnique', Prisma doesn't allow non-unique fields to be used in the query (even if the combination of both fields is unique, as is the case here). I do get that logically, my query doesn't make much sense - the ID alone is already enough to find a unique entry without the second field, so in that sense, the second field is totally redundant. Yet how else am I meant to check that the belongsToID is what it should be before updating the record? Is there somewhere else within the object passed to .update where I can provide a check to be performed on the retrieved record before performing the update?
I believe that creating an index would allow me to query for both fields at once - but why should I have to create an index when the ID (which is already indexed) alone is all I need to retrieve the record I need? What I really need is a way to perform a check on a retrieved record before performing the update when using Prisma.table_name.update(), not a way to query for something with a unique combination of fields.

findByIdAndUpdate adding another document to the database rather than updating

I am using MEAN stack for patient CRUD operations. The update does not seem to be working properly. It adds another document to the database with the updated info but with a null id and leaves the old document that is supposed to be updated as is.
below is the code I wrote in the service for update patient
editPatient(id:string,patient: Patient){
const headers = { 'content-type': 'application/json'}
const body=patient;
console.log(body)
let url=environment.PATIENT_BASE_URL+environment.PATIENT.UPDATE_PATIENT + "?userId=" +id;
return this.httpClient.put(url, body);
}
Those are the contents of the environment file
export const environment = {
production: false,
BASE_URL:'http://localhost:3000',
PATIENT_BASE_URL:'http://localhost:3000/patients/',
PATIENT:{
GET_ALL_PATIENTS: 'list',
GET_PATIENT: 'view',
UPDATE_PATIENT: 'update',
DELETE_PATIENT: 'delete',
SEARCH_PATIENT: 'search',
ADD_PATIENT: 'add',
}
};
This is the code in patients.js
router.put('/update', function(req, res, next) {
const userId = req.body.userId;
let firstnameVal = req.body.firstName;
let lastnameVal = req.body.lastName;
let usernameVal = req.body.username;
let emailVal = req.body.email;
let birthDateVal = req.body.birthDate;
let genderVal = req.body.gender;
let patientObj = {
firstName: firstnameVal,
lastName: lastnameVal,
username: usernameVal,
email: emailVal,
birthDate : birthDateVal,
gender: genderVal
};
// patientsModel.update({'gender':'female'}, )
patientsModel.findByIdAndUpdate(userId, patientObj,{upsert: true, new: true} ,function(err, patientResponse){
if(err){
res.send({status:500, message: 'Unable to update the patient'});
}
else{
res.send({status:200, message: 'User updated successfully' ,results: patientResponse});
}
});
});
Because you used this option
upsert: true
If item with id not found it creates a new document
you can read the docs here
Using the upsert option, you can use findOneAndUpdate() as a
find-and-upsert operation. An upsert behaves like a normal
findOneAndUpdate() if it finds a document that matches filter. But, if
no document matches filter, MongoDB will insert one by combining
filter and update as shown below.
The second argument to findByIdAndUpdate is an update object. If it does not contain any update operators, it is treated as a replacement document.
If your intent is to replace the entire document so the only fields it contains are the ones provided in this function, add the _id to the object:
let patientObj = {
_id: new mongoose.types.ObjectId(userId),
firstName: firstnameVal,
...
If the intent is to modify the provided fields but leave any others fields alone, use the $set update operator like
patientsModel.findByIdAndUpdate(userId, {"$set": patientObj}, ...
There are a few problems with your code - as others have said:
the use of upsert: true is suspicious - I can't imagine when you'd want to upsert this, and
the lack of $set is also unusual unless the patientObj represents the entire document you wish to set
both these items are causing you issues, but I suspect your main problem is actually that your ID doesn't match anything.
You mention an auto-generated ID. Mongo uses an ObjectId (though mongoose perhaps does not) - depending on how you serialise this value, the string representation of it would probably look like this: 63b310df2b36d95e156a237d - however when you query for that value (as you do with userId) - it will return no matches, since you need to convert it to an object ID:
userId = new mongoose.types.ObjectId(req.body.userId)
You should also fix items 1 and 2 above.

How can I get soft deleted entity from typeorm postgreSQL?

I am trying to get soft deleted doc from postgreSQL database using typeorm find, findOne or query builder get/getMany methods, but it always return undefined. Is there a way to get deleted value?
By the docs it should only set a timestamp for deletedAt, and it definitely works, because I can update same record using update where from query builder.
I found out there is another solution. You can use the querybuilders .withDeleted() method to also return soft deleted entities.
Example
const query = await this.manager
.getRepository(FolderEntity)
.withDeleted()
.createQueryBuilder('folder')
.leftJoinAndSelect('folder.subStatus', 'status')
.getMany()
Actually referring to the findOptions doc you can pass the withDeleted boolean that will retrieve the soft deleted rows.
for example:
const result = await this.repo.find({ where: { id }, withDeleted: true })
After more searching I found that only solution is to use raw sql query for that as it is shown in typeorm docs at the query block.
const deletedEntity = await connection
.getRepository(Entity)
.query(`SELECT * FROM Entity where id = '${deletedEntityId}'`)
This query returns values with deletedAt timestamp.
const queryBuilder = await this.yourRepository.createQueryBuilder('entity');
queryBuilder.withDeleted();
queryBuilder.where(`entity.deleted_at IS NOT NULL`)};
await getManager().transaction(async (em) => {
await em.update(Faqs, { id }, { updatedAt: userId });
await em.softDelete(Faqs, id);
});

Not able to access the data inside of an object

I am fetching id column value from database for a particular email. In this case I am passing email and want to get primary key i.e id. This operation is successful as I get object which contains Object with the right and expected result. However I am not able to access the object.
I am receiving object like this:
[ UserInfo { id: 21 } ]
And I am not able to access id part of it.
I am using node.js, postgres for database and typeorm library to connect with database.
const id = await userRepo.find({
select:["id"],
where: {
email:email
}
});
console.log(id)
This prints the above object.
The id I am getting is right. But I am not able to retrieve the id part of the object. I tried various ways for e.g.
id['UserInfo'].id, id.UserInfo.
Please help me in accessing the object I am receiving
Typeorm .find() returns an array of objects containing entries corresponding to your filters, in your case, all entries with an email field corresponding to the email you specified.
Because the result is an array, you can access it this way:
const records = await userRepo.find({
select: ['id'],
where: {
email,
},
})
console.log(records[0].id)
You could also use the .findOne() method, which returns a single element and might be a better solution in your case :)
When you are putting a field in the select part select:["id"], you are only retrieving this part of the database.
It is like your query was this: select id from userRepo where email = email
and you need to put * in the select part to retrieve all the information:
const id = await userRepo.find({
select:["*"],
where: {
email:email
}
});

Many to Many relationship Get by id is not working

I tried to create a user & pet model in Many to Many relationship using mongoDB and sailsJS, db creation and loading the data's are just fine i checked in mongoDB for dbs and collection it exists. when i try to get the user list and pet list its showing me the contents but when i try to GET a single pet by its iD under user by his iD i'm not getting result
It shows "Response does not contain any data."
I tried to get PET like this
"http://localhost:1337/user/54ffd28e9d9ee93c166c7500/pets/54ffd28e9d9ee93c166c7503"
I also tried to display the pet by its name instead of iD its also not working
my sails version is 0.11.0..
my OS is Win7 64 bit
here's a maybe useful thing to know, when requesting by id while using mongoDB.
to get some document inside of a collection, passing the id through an URL, it is useful to use the const { ObjectId } = require('mongodb').
you can for instance do as following:
// VS code might or might not write this for you
// but if you VIM, you have to write yourself
const { ObjectId } = require('mongodb')
fastify.get('/something/:id', async function (req, reply) {
const db = await this.mongo.db;
await db.collection('YourCollection', callbackFunc);
function callbackFunc(err, col) {
if (err) reply.send(err)
col.findOne({ "_id": new ObjectId(req.params.id) }, (err, result) =>
{
console.log(result)
})
}
})
Let me know if it helps, best wishes

Resources