Input field function when aggregating data in node and mongoDB - node.js

I am trying to develop a data aggregation feature for an application. The user should be able to enter a name and it'll search the database collection for that name and return the collection.
I am trying to implement it using node.js but i am confused as to how to pass a parameter for the name.
Controller:
exports.DAFacility_author_search = (req, res) => {
DAFacility.aggregate([
[
{
'$match': {
'author': author
}
}
]
]).then((DAFacility) => {
res.send(DAFacility);
})
.catch((e) => {
res.send(e);
});
};
Route:
router.get('/DAFacilityAuthor', DAFacilityController.DAFacility_author_search)
For the 'author': author i am trying to use author as the variable name but i dont exactly know how to structure the controller and router to take in a parameter so that i can retrieve a value on postman.
Any help would be great
Thank you

I suppose that your request in postman will be something like:
{
"author": "foo",
...
}
When your request is received by the API, the body is available in your req object in the controller, so, if you want to use author field in your aggregate, you simply access: req.body.author.
Your controller could have the next structure:
export const DAFacilityController = {
DAFacility_author_search: (req, res) => {
DAFacility.aggregate([
{'$match': {'author': req.body.author}}
])
.then((DAFacility) => {
res.send(DAFacility);
}).catch((e) => {
res.send(e);
});
},
DAFacility_another_method: (req, res) => {...},
}
The router is ok:
router.get('/DAFacilityAuthor', DAFacilityController.DAFacility_author_search);
router.get('/DAFacilityAuthor/<something>', DAFacilityController.DAFacility_another_method);
I hope I helped

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,
},
],

Update data in MongoDB using Mongoose and Node.js

I am trying to update certain info in a user collection, when the user is visiting a page.
But my method doesn't work. Can anyone help to get it fixed.
app.get('/add-your-accommodation/apartment-type', (req, res, next) => {
if (req.isAuthenticated()) {
res.render('apartment-type.ejs')
} else {
res.render('login.ejs')
}
var id = req.params.id
if(mongoose.Types.ObjectId.isValid(id)) {
User.findByIdAndUpdate(id, {$set: {accomtype: 'house'}},{new: true})
}
});
Your req.params.id is undefined since there is no mention of it in the route path. You can do this,
app.get('/add-your-accommodation/apartment-type', (req, res) => {
if (!req.isAuthenticated()) {
return res.render('login.ejs')
}
res.render('apartment-type.ejs')
var id = req.user._id //since you're using passport (LocalStrategy)
if(mongoose.Types.ObjectId.isValid(id)) {
User.findByIdAndUpdate(id, {$set: {accomtype: 'house'}})
}
})
Now when you call your API, do it like this,
GET /add-your-accommodation/apartment-type
I agree with #kedar-sedai, when you update/change something in your DB, you should not use a GET request. A good practise would be to use the PUT method, even if you have nothing to pass in the body. It makes it easier for you and other developers to understand what your code does at a glance.
Here are 4 HTTP requests that will work in most of the use cases :
GET
You want to retrieve information from your DB (ex: get users, get all the apartment types...)
POST
You want to add information (ex: register user, add an apartment, ...), or send information using the body of the POST request (ex: login, ...)
PUT
You want to update a value (ex: change username, change an apartment type, ...)
DELETE
You simply want to delete something in your DB (ex: delete a user...)
Try findOneAndUpdate. Also, use callback in your query function for getting the error or result.
app.get('/add-your-accommodation/apartment-type/:id', (req, res, next) => {
if (req.isAuthenticated()) {
res.render('apartment-type.ejs')
} else {
res.render('login.ejs')
}
var id = req.params.id
if(mongoose.Types.ObjectId.isValid(id)) {
User.findOneAndUpdate({_id: mongoose.Types.ObjectId(id)}, { $set: { accomtype:'house' } },(err, result)=>{
if (err) throw new Error(err);
console.log(result)
return
})
}
});

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.

expressjs: Best way to handle multiple optional route params

I'm using nodejs Express. I would like to have a router that receive id or username as request param as below:
router.get('/:id?/:username?', authMiddleware, function (req, res, next) {
models
.User
.findAll({
where: {
$or: {
id: req.params['id'],
username: req.params['username']
}
}
})
.then(function (rows) {
res.send(rows);
})
}
However, express seems to not understand my request on:
http://localhost:3000/api/1.0/users//david
Which I would like to query user by username instead of id
The only way works right now is defining a route as: /:id_or_username as below:
router.get('/:id_or_username', function (req, res, next) {
models
.User
.findAll({
where: {
$or: {
id: req.params['id_or_username'],
username: req.params['id_or_username']
}
}
})
.then(function (rows) {
res.send(rows);
})
}
But it is kind of dirty and I'm not happy with that code.
Is that possible to define router in this case using REST style instead of :idOrName param?
Assuming your id is an int, you can do something like this:
router.get('/:id_or_username', function (req, res, next) {
var where = {}
var param = req.params['id_or_username']
if (isNaN(param)) {
where.username = param
} else {
where.id = param
}
models.User.findAll({where}).then(function (rows) {
res.send(rows)
})
}
Now you can send the id or the username using the same paramether
I am not sure how your system is setup but this sounds like a better use for a req.query instead of req.param.
You should use req.query when you are not sure what the input might be, and use req.param when passing information you are sure will be there. There is probably a way to do this with req.param but I am not sure I can think of a reason why you'd want to do it that way.
This way you could do an action like:
action = "/users/"+ id ? id : username
and then req.query for it.

Resources