Find specific data in MongoDB via NodeJS - node.js

I need to find specific data in my MongoDB for the rest API but it's returning null.
app.get('/api/v1/champions/:name', (req, res) => {
db.find({"champions": req.params.name}, (err, champion) => {
res.json(err)
})
})
Here is my MongoDB Schema:
champions: {
champ_name_1: {
dmg: Number,
cost: Number
},
champ_name_2: {
....
}
}

Since you are checking to see if a key exists in the champions object you'll need to write the query differently.
If your data was formatted like this then your query would work. (champions is a String)
{
"champions": "champ_name_1",
"dmg": 123,
"cost": 123
}
To check if a key exists in an object in mongo, use a query like this.
const champKey = 'champions.' + req.params.name;
db.find({ [champKey]: { $exists: true } });
https://docs.mongodb.com/manual/reference/operator/query/exists/

you can use mongoose package
here
and simply use the mongoose's find() methond to find particular data as providing to it.
For Example: find({_id: MY_ID_HERE});

Related

MERN - update specific string in an array's object

I am using mongoose to connect my backend (Express) server to database. I want to do normal CRUD operations - but I am able to do it only for direct data in object, but I need to be able to access also array data.
Example of my model:
const LeewaySchema = new mongoose.Schema({
name: {
type: String,
},
shirt: [
{
name: String,
image: String,
},
],
With the following code I am able to update only name of the object, but I need to be able to update also name in shirt array
Here is working approach when changing name of object:
app.put('/update', async (req, res) => {
const updateName = req.body.updateName;
const id = req.body.id;
console.log(updateName, id);
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.name = updateName;
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
And I tried the same with shirt array, just specifying the correct path
app.put('/update-shirt', async (req, res) => {
const updateShirtName = req.body.updateShirtName;
const id = req.body.id;
try {
await ClosetModel.findById(id, (error, closetToUpdate) => {
closetToUpdate.shirt.name = updateShirtName; // different path here
closetToUpdate.save();
});
} catch (err) {
console.log(err);
}
res.send('success');
});
The server crashes and /update-shirt conflicts with /update path
I am using the same route and frontend for READ
useEffect(() => {
axios
.get('http://localhost:8000/read')
.then((response) => {
setListOfClosets(response.data);
})
.catch(() => {
console.log('error');
});
}, []);
And update name function calling with button onClick:
const updateCloset = (id) => {
const updateName = prompt('Enter new data');
axios
.put('http://localhost:8000/update', {
updateName: updateName,
id: id,
})
.then(() => {
setListOfClosets(
listOfClosets.map((val) => {
return val._id === id
? {
_id: id,
name: updateName,
email: val.email,
}
: val;
})
);
});
};
I don't really know how to do update for shirt's name, I tried to copy paste and just change path and url of course, but it did not work.
The question doesn't actually describe what specific transformation (update) you are attempting to apply to the document. Without knowing what you are attempting to do, there is no way for us to help advise on how to do it.
Say, for example, that the document of interest looks like this:
{
_id: 1,
shirt: [
{ name: "first shirt", image: "path to first shirt" },
{ name: "second shirt", image: "path to second shirt" },
{ name: "third shirt", image: "path to third shirt" }
]
}
Also let's say that the application hits the /update-shirt endpoint with an id of 1 and a updateShirtName of "updated shirt name". Which entry in the array is that string supposed to be applied to? Similarly, how would that information be passed to the server for it to construct the appropriate update.
It is absolutely possible to update documents in an array, here is some documentation about that specifically. But the actual structure of the command depends on the logic that you are attempting to provide from the application itself.
The only other thing that comes to mind here is that the motivation for the schema described in the question seems a little unclear. Why is the shirt field defined as an array here? Perhaps it should instead just be an embedded document. If so then the mechanics of updating the field in the subdocument are more straightforward and none of the aforementioned concerns about updating arrays remain relevant.
just make an update api where you just have to pass the id and and pass the shirt in the findByIdAndUpdate query and hit the postman by passing the below code.
shirt: [
{
name: "jhrh",
image: String,
},
],

How can i customize a field when i am making a pull request in Mongodb

Below is a code that deletes a sub document inside roomTypes field and it works fine. The problem is that i want to reuse the code instead of writting one. This because apart from having a field roomTypes with sub documents i also have other fields with sub documents. so i would like to write one code that i can use to update them.
So under the field that is being pulled is there way i can use something like a template string the way i have done for { _id: ${req.params.position} } so that i can update a field based on what is coming in the request body. In short i do not want to hard code the roomTypes field. I want it to be dynamic based on what is coming from the request body. Please help on this
exports.deleteRoomType = catchAsync(async (req, res, next) => {
const reqHostel = await Hostel.findByIdAndUpdate(req.params.id, {
$pull: {
roomTypes: { _id: `${req.params.position}` }
}
});
// process.exit();
res.status(204).json({
status: 'success',
data: null
});
});
You can pass another paramer fieldName and build your update expression based on that field's value:
const reqHostel = await Hostel.findByIdAndUpdate(req.params.id, {
$pull: {
[req.params.fieldName]: { _id: `${req.params.position}` }
}
})

Pull objects from array in embedded MongoDB document

I've been unable to figure out a way to to remove objects from arrays in embedded mongoDB documents.
My schema holds a user and a list of flower objects. It looks like this:
const UserSchema = new Schema({
name : {
type : String,
required : true
},
flowers : [
{
name : {
type : String,
required : true
},
water_freq : {
type : Number,
required : true
}
}
]
});
I've managed to add flower objects like so:
router.post('/:id/flowers/add', (req, res) => {
const { name, water_freq } = req.body;
User.findByIdAndUpdate(
req.params.id,
{
$push : {
flowers : { name, water_freq }
}
},
{ new: true }
).then((user) => res.json(user));
});
I want to delete flowers from users by their id but I'm unable to get it to work.
The code below is my non-working attempt at it.
router.delete('/:id/flowers/delete/:flowerid', (req, res) => {
User.findByIdAndUpdate(req.params.id, {
$pull : {
flowers : { _id: req.params.flowerid }
}
}).then((user) => res.json(user));
});
I would greatly appreciate if someone can help me get this right.
One possible cause is, in your query {$pul: xxxx}, MongoDB is expecting a BSON type object id that was auto generated for each flower entry, while you are giving a string. So you may want to convert it to the proper type before doing the query:
try:
router.delete('/:id/flowers/delete/:flowerid', (req, res) => {
User.findByIdAndUpdate(req.params.id, {
$pull : {
flowers : { _id: ObjectId(req.params.flowerid) }
}
}).then((user) => res.json(user)); });
To see more about the objectId
Thanks for the replies!
I did some more testing with postman and found out that in fact the code I posted in my question did work after all. What set me off was that the response with the user document still displayed the flower I had just deleted, which made me think it didn't work.
I still have no idea why that is, or if there's a way to get a response with the updated user. But the deletion seems to work as intended.

Mongoose updating document with findOne() matches only wrong results

I have a collection of fixtures that 'belong' to a competitor and look something like this:
{
"_id": {
"$oid": "59dbdf6dbe628df3a80419bc"
},
"timeOfEntrance": "1507581805813",
"timeOfFinish": null,
"competitor": {
"$oid": "59db5a3f3d6119e69911a61a"
},
"__v": 0
}
My goal is to update only the document's timeOfFinish by sending a PUT request with competitor's ID as a param in the url and timestamp in the body. However I'm struggling to compose the update query.
The following is what I have currently, it never finds the right match and to my surprise it's always updating the wrong document.
fixtureController.put = (req, res) => {
const competitorId = req.params.id;
const timeOfFinish = req.body.timeOfFinish;
Fixture.findOne({'competitor.$oid': competitorId}, (err, fixture) => {
fixture.set({ timeOfFinish });
fixture.save()
.then(updatedFixture => {
return res.status(200).json({
success: true,
fixture: updatedFixture
})
})
.catch(err => {
return res.status(500).json({
message: err
});
});
});
};
Bit of a beginner in the MongoDB field, will appreciate your comments and solutions.
Turns out there was no need to specify the exact field in the match parameter. Mongoose matches by the id field automatically.
Fixture.findOne({'competitor': competitorId}, (err, fixture) => {
...
});

How expose data from server to client

I'm new with node.js. I'm writing a single rest API and I want to know what is the right way to expose data without expose unnecessary information.
In my example, I have a "Tag" schema. And I don't want to expose the mongoose fields to my client. My code:
apiRoutes.get('/tag', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Tag.find({}, (err, tags) => {
return res.json(tags);
});
});
But in client, I don't want to expose "_id" and "__v":
{
"_id": "57083a5e725f3cf0242a2916",
"tagName": "Test",
"en_us": "Testing",
"__v": 0,
"lastUpdated": "2016-04-08T23:10:22.759Z"
}
What is the right way to map only relevant fields?
Your can always use mongoose virtuals. Here is an example:
In your model, you can use something like this:
Tag
.virtual('public')
.get(function() {
return {
tagName: this.tagName,
en_us: this.en_us,
lastUpdated: this.lastUpdated
};
});
Then, when making a query, just use the virtual you've created:
Tag.find({}, (err, tags) => {
res.json(tags.map(tag => tag.public));
});
You can use Schema transform toJSON. Here is a blog post explains it in details ignore certain fields from mongoose schema when return object to client
TagSchema.set('toJSON', {
transform: function(doc, ret, options) {
delete ret._id;
delete ret.__v;
return ret;
}
});
You can declare required fields separated by space in find method:
Tag.find({}, 'tagName en-us', (err, tags) => {
return res.json(tags);
});
Check mongoose documenation.

Resources