Post TypeScript Object without '_id' field? - mongodb

I use Express, Mongoose and Angular 2 (TypeScript) making an web app. Now I want to post a MyClass Instance without any _id field.
In mongoose we could use _id to do a lot of operations on mongoDB, so here is what I have done on the server side using mongoose
router.post('/', function(req, res, next) {
Package.create(req.body, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
/* GET /package/id */
router.get('/:id', function(req, res, next) {
Package.findById(req.params.id, function (err, post) {
if (err) return next(err);
res.json(post);
});
});
/* PUT /package/:id */
router.put('/:id', function(req, res, next) {
Package.findByIdAndUpdate(req.params.id, req.body, function (err, post, after) {
if (err) return next(err);
res.json(post);
});
});
To contain the field _id I created a ts Class like this:
export class Package{
constructor(
public guid: string,
...
[other fields]
...
public _id: string
){}
}
Please note the _id at the end.
In my angular 2 service I am doing this to post the json object to server
//create new pakcage
private post(pck: Package): Promise<Package> {
let headers = new Headers({
'Content-Type': 'application/json'
});
return this.http
.post(this.packageUrl, JSON.stringify(pck), { headers: headers })
.toPromise()
.then(res => res.json())
.catch(this.handleError);
}
Then I received an error as shown in the screenshot below:
In which it indicates that the object I post back got a empty _id field.
How do I post a ts class without the _id field or should I do it totally differently?

Since no one has given an answer I went to the internet and found a good example of how to implement a Angular2 -- Mongoose -- Express System.
https://github.com/moizKachwala/Angular2-express-mongoose-gulp-node-typescript
A very good example with the original Hero App from official tutorial. Although it is based on RC1 but it provides a good start point on how to do the RESTFUL Request properly.
Hope this would help someone who is looking for a similar answer.

Related

Why is JQuery casting a string to _id for Mongodb in this? (Please read EDIT)

I have a route that adds an image (a meme) like this:
// add new image by URL
app.post('/api/addMeme', function (req, res) {
var meme = new Meme({
title: req.body.title.trim().toLowerCase(),
image: req.body.image,
meta: {
votes: 0,
favs: 0
},
related: []
});
// Save meme to database
meme.save(function (err) {
if (err) throw err;
Meme.find({}, function (err, meme) {
if (err) throw err;
io.emit('new meme', meme);
});
res.send('Succesfully inserted meme.');
});
});
It takes the only two attribute title and image given by client side ajax and add it to my Mongodb database named Meme. Emit the updated database using socket.io. Both title and image are String type. image is suppose to be an URL to an image.
Now, I'm not ashamed to admit it, but my friend trolled my site and sent image = "www.pornhub.com" to this route and it crashed my database/site. Whenever I go and try to retrieve the image by its _id, I get the error:
CastError: Cast to ObjectId failed for value "www.pornhub.com" at path "_id" for model "meme"
EDIT: it looks like the error is actually coming from the route
app.post('/api/vote', function(req, res){
Meme.findOneAndUpdate({_id: req.body.id}, {$inc : {'meta.votes' : 1}}, {new: true}, function (err, meme) {
if (err) throw err;
if (!meme) return res.send('No meme found with that ID.');
io.emit('new vote', meme);
res.send('Succesfully voted meme.');
});
});
where a POST request is updating the database, and there's a cast error where the _id is given as a string?
The client side script that's doing this is
$("#vote").click(function(){
$.ajax({
type: "POST",
url: '/api/vote',
data: {
id: App.meme._id
},
success: function (data, status) {
console.log(data);
}
});
return false;
});
where App is a Express-state exposed data for which meme, the database, lives under.
But this error ONLY occurs on the object with image = "www.pornhub.com". My guess is that somewhere in the HTML, a cross-site href is visiting www.pornhub.com and somehow App is getting distorted? It doesn't fully make sense why id: App.meme._id would give www.pornhub.com as its value.

Express JS routing based on the user names

I am working on a express js project.I have got all my basic routing set up working perfectly. Usually when I want to search a record based on id I do this:
router.route('/sensors_home/:sensor_id')
.get(function (req, res) {
Sensor.findById(req.params.sensor_id,function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});
This allows me to retrieve the data when I do http://localhost:4000/sesnors_home/45000cbsfdhjbnabfbajhdb
(45000cbsfdhjbnabfbajhdb = Object id from the MongoDB )
Now my goal is to have several users to my application. I have my mongoose schema set up and the mongoDB looks like this :
Here is the issue: I wanna retrieve data corresponding to John Peterson based on his _id that is "John".Instead of doing this http://localhost:4000/sesnors_home/45000cbsfdhjbnabfbajhdb I wanna do something like this http://localhost:4000/sesnors_home/John and retrieve all the data specific to John. I tried various methods but still stuck with this issue. I tried using req.params._id and also some Mongodb queries on the User Collection but still no luck. Please suggest some ideas.
Thanks!
UPDATE:
I tried using the following code :
router.route('/sensors_home/:id')
.get(function (req, res) {
res.send(_id.toString());
User.findOne({_id: req.params._id} ,function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});
This gives me the following error :
ReferenceError: _id is not defined
Have you tried the following?
router.route('/sensors_home/:_id')
.get(function (req, res) {
Sensor.findOne({_id: req.params._id},function(err, sensorInfo) {
if (err)
res.send(err);
res.send(sensorInfo);
});
});

Update MongoDB object field with _assign/_merge/_extend

I am running into a question when to use which one, the following is update function for mongoose, it works fine.
// Updates an existing form in the DB.
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Form.findById(req.params.id, function (err, form) {
if (err) { return handleError(res, err); }
if(!form) { return res.send(404); }
var updated = _.assign(form, req.body);
updated.formContent = req.body.formContent;
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, form);
});
});
};
Tried the following to replace the form data.
_.assign(form, req.body); // Works (update database)
_.merge(form, req.body); // Not Work (database not updating, remain the same)
_.extend(form, req.body); // Works (update database)
The above result show merge doesn't work when there is object within the post data.
Could some please explain why one is not working the others is ok. I have read the following question
Lodash - difference between .extend() / .assign() and .merge()
but i am curious to understanding which one won't update the database, but when applied with assign and extend it's working.

Mongoose findByIdAndUpdate not working

I have a fairly straight forward method below to update a document based on its ObjectId. It does not return an error but it fails to make the required updates to the document. I think it is failing because, according to my research, findByIdAndUpdate() takes only plain Javascript whereas job._id is an ObjectId from the document that I want to update. Can someone tell me how to make this work correctly?
function handleEncoderResponse(xmlResponse, job) {
var r = et.parse(xmlResponse);
var mediaID = r.findtext('./MediaID');
var message = r.findtext('./message');
EncodingJob = mongoose.model('EncodingJob');
EncodingJob.findByIdAndUpdate( job._id, {
"MediaID": mediaID,
"Status": message
}, function(err, result) {
if (err) console.log(err);
console.log(result);
});
}
Edit: Per this question Mongoose update document Fail with findByIdAndUpdate
I also tried the following code to no avail.
job.MediaID = mediaID;
job.Status = message;
job.save(function(err, res) {
if(err) console.log(err);
});
This approach yields the issue. It does not update the document and it does not return an error.
As it turns out, my mistake was forgetting to define MediaID and Status in the Schema as follows:
var encodingJobSchema = new mongoose.Schema({
...
MediaID: String,
Status: String
});

Cascade style delete in Mongoose

Is there a way to delete all children of an parent in Mongoose, similar to using MySQLs foreign keys?
For example, in MySQL I'd assign a foreign key and set it to cascade on delete. Thus, if I were to delete a client, all applications and associated users would be removed as well.
From a top level:
Delete Client
Delete Sweepstakes
Delete Submissions
Sweepstakes and submissions both have a field for client_id. Submissions has a field for both sweepstakes_id, and client_id.
Right now, I'm using the following code and I feel that there has to be a better way.
Client.findById(req.params.client_id, function(err, client) {
if (err)
return next(new restify.InternalError(err));
else if (!client)
return next(new restify.ResourceNotFoundError('The resource you requested could not be found.'));
// find and remove all associated sweepstakes
Sweepstakes.find({client_id: client._id}).remove();
// find and remove all submissions
Submission.find({client_id: client._id}).remove();
client.remove();
res.send({id: req.params.client_id});
});
This is one of the primary use cases of Mongoose's 'remove' middleware.
clientSchema.pre('remove', function(next) {
// 'this' is the client being removed. Provide callbacks here if you want
// to be notified of the calls' result.
Sweepstakes.remove({client_id: this._id}).exec();
Submission.remove({client_id: this._id}).exec();
next();
});
This way, when you call client.remove() this middleware is automatically invoked to clean up dependencies.
In case your references are stored other way around, say, client has an array of submission_ids, then in a similar way as accepted answer you can define the following on submissionSchema:
submissionSchema.pre('remove', function(next) {
Client.update(
{ submission_ids : this._id},
{ $pull: { submission_ids: this._id } },
{ multi: true }) //if reference exists in multiple documents
.exec();
next();
});
which will remove the submission's id from the clients' reference arrays on submission.remove().
Here's an other way I found
submissionSchema.pre('remove', function(next) {
this.model('Client').remove({ submission_ids: this._id }, next);
next();
});
I noticed that all of answers here have a pre assigned to the schema and not post.
my solution would be this: (using mongoose 6+)
ClientSchema.post("remove", async function(res, next) {
await Sweepstakes.deleteMany({ client_id: this._id });
await Submission.deleteMany({ client_id: this._id });
next();
});
By definition post gets executed after the process ends pre => process => post.
Now, you're probably wondering how is this different than the other solutions provided here.
What if a server error or the id of that client was not found?
On pre, it would delete all sweeptakes and submissions before the deleting process start for client. Thus, in case of an error, it would be better to cascade delete the other documents once client or the main document gets deleted.
async and await are optional here. However, it matters on large data. so that the user wouldn't get those "going to be deleted" cascade documents data if the delete progress is still on.
At the end, I could be wrong, hopefully this helps someone in their code.
Model
const orderSchema = new mongoose.Schema({
// Множество экземпляров --> []
orderItems: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'OrderItem',
required: true
}],
...
...
});
asyncHandler (optional)
const asyncHandler = fn => (req, res, next) =>
Promise
.resolve(fn(req, res, next))
.catch(next)
module.exports = asyncHandler;
controller
const asyncHandler = require("../middleware/asyncErrHandler.middleware");
// **Models**
const Order = require('../models/order.mongo');
const OrderItem = require('../models/order-item.mongo');
// #desc Delete order
// #route DELETE /api/v1/orders/:id
// #access Private
exports.deleteOrder = asyncHandler(async (req, res, next) => {
let order = await Order.findById(req.params.id)
if (!order) return next(
res.status(404).json({ success: false, data: null })
)
await order.remove().then( items => {
// Cascade delete -OrderItem-
items.orderItems.forEach( el => OrderItem.findById(el).remove().exec())
}).catch(e => { res.status(400).json({ success: false, data: e }) });
res.status(201).json({ success: true, data: null });
});
https://mongoosejs.com/docs/api/model.html#model_Model-remove