Mongoose Error while performing delete - node.js

I am running into following error but I unable to completely grasp the understanding behind the error.
CastError: Cast to ObjectId failed for value "XYZ" at path "_id" for model "Partner"
I have my schema defined as following
var partnerList = new Schema (
{
partnerName: String,
supportedProducts: [String]
},
{
collection: 'partnerList'
}
);
module.exports = mongoose.model('Partner', partnerList);
The functionality of my delete function
delete: function (req, res) {
var removePartner = req.params.partnerName;
var promise = Partner.findByIdAndRemove(removePartner).exec();
promise.then(function removePartner(val) {
console.log('partner value removed');
res.send(val);
}).catch(function catchError(err){
console.error(err);
throw err;
});
}
I am trying to making a request to my node app service using
localhost:8443/data/XYZ, where i am passing the value 'XYZ' as the parameter. This value is used to delete the appropriate object from the database.

Basically the error means that whatever you pass as your "XYZ" url param is not a valid ObjectId.
Guessing from your code you use the "partner name" (probably some arbitrary string) instead of the database id of the partner. However findByIdAndRemove() requires you to specify an ObjectId as it uses this to identify which document to delete:
Model.findByIdAndRemove(id, [options], [callback])
Your delete API call could then look something like this: http://localhost:8443/data/59558ccd7acc4dd63ea88988. However for this the client needs to know the ObjectId of the partner.
So you have to either use the ObjectId of a partner in the URL, or use remove() to implement your custom delete query instead, for example like this (if name is the property you use to store your partner names):
Partner.remove({ name: partnerName }).exec();
Be careful however that this might remove multiple documents if your partner name is not unique, as remove will delete all documents matching the query.
In order to prevent this you can also use findOneAndRemove() using the same query. This would only remove one document at a time. If there are multiple partners with the same name it would remove the first one (depending on your sort order).

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.

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
}
});

In Cloud function how can i join from another collection to get data?

I am using Cloud Function to send a notification to mobile device. I have two collection in Firestore clientDetail and clientPersonalDetail. I have clientID same in both of the collection but the date is stored in clientDetail and name is stored in clientPersonal.
Take a look:
ClientDetail -- startDate
-- clientID
.......
ClientPersonalDetail -- name
-- clientID
.........
Here is My full Code:
exports.sendDailyNotifications = functions.https.onRequest( (request, response) => {
var getApplicants = getApplicantList();
console.log('getApplicants', getApplicants);
cors(request, response, () => {
admin
.firestore()
.collection("clientDetails")
//.where("clientID", "==", "wOqkjYYz3t7qQzHJ1kgu")
.get()
.then(querySnapshot => {
const promises = [];
querySnapshot.forEach(doc => {
let clientObject = {};
clientObject.clientID = doc.data().clientID;
clientObject.monthlyInstallment = doc.data().monthlyInstallment;
promises.push(clientObject);
});
return Promise.all(promises);
}) //below code for notification
.then(results => {
response.send(results);
results.forEach(user => {
//sendNotification(user);
});
return "";
})
.catch(error => {
console.log(error);
response.status(500).send(error);
});
});
}
);
Above function is showing an object like this
{clienId:xxxxxxxxx, startDate:23/1/2019}
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID.
What should do ?
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name .
Can Anybody please Help.?
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID. What should do ?
Unfortunately, there is no JOIN clause in Firestore. Queries in Firestore are shallow. This means that they only get items from the collection that the query is run against. There is no way to get documents from two top-level collection in a single query. Firestore doesn't support queries across different collections in one go. A single query may only use properties of documents in a single collection.
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name.
So the most simple solution I can think of is to first query the database to get the clientID. Once you have this id, make another database call (inside the callback), so you can get the corresponding name.
Another solution would be to add the name of the user as a new property under ClientDetail so you can query the database only once. This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoQSL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase realtime database but same rules apply to Cloud Firestore.
Also, when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
The "easier" solution would probably be the duplication of data. This is quite common in NoSQL world.
More precisely you would add in your documents in the ClientDetail collection the value of the client name.
You can use two extra functions in this occasion to have your code clear. One function that will read all the documents form the collection ClientDetail and instead of getting all the fields, will get only the ClientID. Then call the other function, that will be scanning all the documents in collection ClientPersonalDetail and retrieve only the part with the ClientID. Compare if those two match and then do any operations there if they do so.
You can refer to Get started with Cloud Firestore documentation on how to create, add and load documents from Firestore.
Your package,json should look something like this:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"firebase-admin": "^6.5.1"
}
}
I have did a little bit of coding myself and here is my example code in GitHub. By deploying this Function, will scan all the documents form one Collection and compare the ClientID from the documents in the other collection. When it will find a match it will log a message otherwise it will log a message of not matching IDs. You can use the idea of how this function operates and use it in your code.

Express, Mongoose, db.findOne always returns same document

I am attempting a CRUD app with MEAN stack. I am using mongoose in Express to call to the MongoDB. I am using the FindOne function with a specified parameter, and it's always returning the same (incorrect) document.
I know I am connected to the correct database, since I get a document back from that collection, but it's always the same document, no matter what I pass as the parameter.
module.exports = mongoose.model('Player', playersSchema, 'players'); //in player.js
const Player = require('./model/players');
app.get('/api/player/:id', (req, res) =>{
Player.findOne({id: req.params.playerId},
function(err, player) {
if(err) {
res.json(err);
}
else {
res.json(player);
}
});
});
I have 3 separate "players", with three distinct "playerID" fields (38187361, 35167321, 95821442). I can use Postman to GET the following URL, for example:
http://localhost:3000/api/player/anythingyouWantInHere
and it will return 38187361, the first document. I've been over this website, many tutorials, and the Mongoose documentation and I can't see what I'm doing wrong..
I'd like to eventually find by playerId OR username OR email, but one hurdle at a time...
From the mongoose documentation of findOne, if you pass Id a null or an empty value, it will query db.players.findOne({}) internally which will return first document of the collection everytime you fetch. So make sure you are passing non-empty id here.
Note: conditions is optional, and if conditions is null or undefined,
mongoose will send an empty findOne command to MongoDB, which will
return an arbitrary document. If you're querying by _id, use
findById() instead.
Your route is '/api/player/:id', so the key on the req.params object will be 'id' not 'playerId'.
I don't know what/where/if you're populating the playerId param, but if you update your query to call req.params.id it should actually change the document based on the path as you seem to be wanting to do.
I had the same problem, and it was that the name of column's table was different from the model I had created.
In my model the name of the wrong column was "role" and in my table it was "rol".

Update by Id not finding document

I am attempting to perform a relatively simple update using Mongoose:
var location = locationService.getLocation(typedLat, typedLong, travelRadius);
DocumentModel.update({_id : passedInId }, { location : location }, function(err, resp){
next(err, resp);
});
In this case passedInId is the string:
"561ee6bbe4b0f25b4aead5c8"
And in the object (test data I created) the id is:
"_id": ObjectId("561ee6bbe4b0f25b4aead5c8")
When I run the above however, matched documents is 0. I assume this is because passedInId is being treated like a string, however when I type it to an ObjectId:
var queryId = ObjectId(passedInId)
The result is the same, the document doesn't match. What am I missing? This seems like it should be obvious....
Mongoose will correctly interpret a string as an ObjectId. One of the following must be the case:
That record is not in the collection. Run a query in the mongo shell to check.
Mongoose I'd looking in collection other than the one containing your test data. Remember, by default, mongo will lowercase the name under which you register your model and will add an a "s" to it.
Lastly, and your answer speaks to this, maybe your model it's just not being updated with any new information.
This behavior was because I had not yet updated the model in mongoose to include the location element. There is no error, it just doesn't match or do anything.

Resources