model in not defined error in node js - javascript

I am writing post api using restify and mongodb with mongoose.
'use strict'
const Trending = require('../models/trending');
const trendingController = {
postTrending: (req, res, next)=>{
let data = req.body || {};
console.log(Trending);
let Trending = new Trending(data);
Trending.save(function(err) {
if (err) {
console.log(err)
return next(new errors.InternalError(err.message))
next()
}
res.send(201)
next()
})
}
}
here error is that Trending is not defined, I don't know why.. other similar controllers are working fine.
Trending is mongoose Model
model code
'use strict'
const mongoose = require('mongoose');
const timestamps = require('mongoose-timestamp');
const Schema = mongoose.Schema;
const TrendingSchema = new mongoose.Schema({
_id: Schema.Types.ObjectId,
headline: {
type: String,
required: true
},
description: String,
data: [
{
heading: String,
list: [String]
}
],
tags: [{ type: Schema.Types.ObjectId, ref: 'Tags' }]
});
TrendingSchema.plugin(timestamps);
const Trending = mongoose.model('Trending', TrendingSchema)
module.exports = Trending;
folder structure
controllers
--- trending.js
models
---trending.js

You are having this problem because of this line;
let Trending = new Trending(data);
You should avoid using the same variable name for two different things to prevent this kind of problem. Especially in this case where you are using uppercase letter for an object when you should use it only for classes.
Replace that line with;
let trending = new Trending(data);
The problem happens because let (and const) are block scoped so a variable with the same name but from an outer scope will be overridden. You then get undefined for this variable because you are referencing it in the same line you are declaring it, so it is in fact still undefined.

Related

.findByIdAndUpdate is not a function error - coming from controller where the model has been required?

I'm working on a web application for my company to view a database of customers and their data using MongoDB, Mongoose, and Express. Our company resells used copiers/printers and also provides maintenance contracts for machines. I want to save each customer as a document, with machines as separate linked documents.
I have models, controllers, and routes set up for customers and machines. I am getting the following error when trying to delete a machine from it's customer:
Customer.findByIdAndUpdate is not a function
TypeError: Customer.findByIdAndUpdate is not a function at module.exports.deleteMachine (C:\controllers\machines.js:21:20) at C:\utils\catchAsync.js:3:9 at Layer.handle [as handle_request] (C:\node_modules\express\lib\router\layer.js:95:5) at next (C:\node_modules\express\lib\router\route.js:144:13) at module.exports.getCustomer (C:\middleware.js:15:5) at processTicksAndRejections (node:internal/process/task_queues:96:5)
My code is as follows:
Controller for Machines:
const Customer = require('../models/customer');
const Machine = require('../models/machine');
module.exports.deleteMachine = async (req, res) => {
const { id, machineId } = req.params;
await Customer.findByIdAndUpdate(id, { $pull: { machines: machineId } });
await Machine.findByIdAndDelete(machineId);
req.flash('success', 'Machine has been deleted');
res.redirect(`/customers/${id}`);
};
Route for Machines:
router.delete('/:machineId', getCustomer, catchAsync(machines.deleteMachine));
the "getCustomer" middleware is as follows - its only purpose is to ensure a valid customer is being requested and to set the "foundCustomer" to make my life easier elsewhere. I don't think it is the issue, but I'm including it just for clarity:
module.exports.getCustomer = async (req, res, next) => {
const { id } = req.params;
const customer = await Customer.findById(id).populate({ path: 'machines' });
if (!customer) {
req.flash('error', 'Sorry, that customer cannot be found!');
return res.redirect('/customers');
}
res.locals.foundCustomer = customer;
next();
};
The relevant routes have been set as follows in my app.js:
const customerRoutes = require('./routes/customers');
const machineRoutes = require('./routes/machines');
app.use('/customers', customerRoutes);
app.use('/customers/:id/machines', machineRoutes);
I haven't run into any issues with other machine routes, so I'm not sure why this one is throwing an error. This application is actually the second version that I've made, and the first version uses the exact same code, with no issue. So I'm super stumped.
Any help is greatly appreciated!
Customer Model -
const customerSchema = new Schema({
customer: String,
customerID: String,
category: {
type: String,
enum: ['contracted', 'billable']
},
contacts: [contactSchema],
address: String,
city: String,
state: String,
zip: String,
county: String,
machines: [
{
type: Schema.Types.ObjectId,
ref: 'Machine'
}
],
notes: [noteSchema]
});
I'm a dummy. I exported the Customer model as part of an array of exports like this:
const Customer = mongoose.model('Customer', customerSchema);
module.exports = {
Customer: Customer,
Note: Note,
Contact: Contact
};
When requiring the model in my Machine controller I had it formatted as:
const Customer = require('../models/customer');
To get it working correctly I needed to require it like this:
const { Customer } = require('../models/customer');
After making that change everything is working correctly, and I can move on with my life/application.

How to get the id of a document before even saving it in mongoose?

I have a simple controller that creates a post for a user. Another schema is linked to it. When I try to create a new post, I need to get the id of the post so that I can link other schema to it.
Here is the schema:
const mongoose = require("mongoose");
const User = require("./User");
const View = require("./View");
const ArticleSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
},
body: {
type: String,
required: true,
},
status: {
type: String,
default: "public",
enum: ["public", "private"],
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
views: {
type: mongoose.Schema.Types.ObjectId,
ref: "View",
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Article", ArticleSchema);
It is fine when I want to link the user field because I have that stored in memory.
But the view field requires postId of that particular document. And I can't get it without first creating the document.
My create post controller:
module.exports.createArticleController = async function (req, res) {
try {
req.body.user = req.User._id;
const article = await Article.create(req.body).exec()
res.redirect(`/${article.title}/${article._id}`);
} catch (e) {
console.error(e);
}
};
So my question is,
How can i get the id in the process of executing the model.create() so that i can link the view to that id. Maybe something using the this operator
I don't want to use update after create.
You can generate your own id and save it
ObjectId id = new ObjectId()
You can get object Id's right after creating an instance of Model or create your own object id's and save them.
Here's how i achieved it:
module.exports.createArticleController = async function (req, res) {
try {
const instance = new Article();
instance.title = req.body.title;
instance.body = req.body.body;
instance.status = req.body.status;
instance.user = req.User._id;
instance.views = instance._id;
const article = await instance.save();
if (article) {
res.redirect(`/${article.title}/${article._id}`);
}
} catch (e) {
console.error(e);
}
};
Or you can create them and save it to the db.
var mongoose = require('mongoose');
var myId = mongoose.Types.ObjectId();
const instance = new YourModel({_id: myId})
//use it
Continue reading
How do I get the object Id in mongoose after saving it.
Object Id's format and usage
You can just simply create a schema ovject like this:
const task: TaskDocument = new this.taskSchema({ ...createTaskDto })
This is from one of my projects, since the ObjectId from MongoDB is based on the operating machine and the time it is created, it doesn't need the database to generate the id.
You can now access task._id to get your id without saving it.

Mongoose populate returns empty array or list of ObjectIds

I am practicing my express.js skills by building a relational API and am struggling to populate keys in a schema.
I am building it so I have a list of properties, and those properties have units. The units have a propertyId key.
This is currently returning an empty array, whereas if i remove the populate({}) it returns an array of ObjectIds.
I've read a number of posts and some people solved this by using .populate({path: 'path', model: Model}); but this doesn't seem to be doing the trick. I think it might be the way I am adding a propertyId to the unit but I'm not sure. Can anyone see where I am going wrong? Any help will be massively appreciated.
Here are the schemas.
Property:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PropertySchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
units: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'unit'
}
]
});
module.exports = Property = mongoose.model('property', PropertySchema);
Unit:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UnitSchema = new Schema({
title: {
type: String,
required: true
},
propertyId: {
type: Schema.Types.ObjectId,
ref: 'property'
}
});
module.exports = Unit = mongoose.model('unit', UnitSchema);
I am then creating the unit like this:
-- api/properties/:id/units --
router.post('/:id/units', async (req, res) => {
// Get fields from req.body
const { title } = req.body;
// Get current property
const property = await Property.findById(req.params.id);
try {
// Throw error if no property
if (!property) {
return res.status(400).json({ msg: 'Property not found' });
}
// Create new unit
const newUnit = new Unit({
title,
propertyId: req.params.id
});
// Add new unit to property's units array
property.units.unshift(newUnit);
// Save property
await property.save();
// Return successful response
return res.status(200).json(property);
} catch (error) {
console.error(error.message);
return res.status(500).send('Server error');
}
});
And trying to populate in the GET request
-- /api/properties/:id/units --
const Unit = require('../../models/Unit');
router.get('/:id/units', async (req, res) => {
const property = await Property.findOne({ _id: req.params.id }).populate({path: 'units', model: Unit});
const propertyUnits = property.units;
return res.status(200).json(propertyUnits);
});
If i remove the .populate({path: 'units', model: Unit});, I get a list of unit id's like this:
[
"5ff7256cda2f5bfc1d2b9108",
"5ff72507acf9b6fb89f0fa4e",
"5ff724e41393c7fb5a667dc8",
"5ff721f35c73daf6d0cb5eff",
"5ff721eb5c73daf6d0cb5efe",
"5ff7215332d302f5ffa67413"
]
I don't know, why you don't try it like this:
await Property.findOne({ _id: req.params.id }).populate('units')
I've been try that code above and it's working.
Note: Make sure to check your req.params.id is not null or undefined and make sure the data you find is not empty in your mongodb.
Updated: I've been try your code and it's working fine.
The issue was caused by inconsistent naming and not saving the new created unit as well as the updated property.
I double checked all my schema exports and references and noticed I was using UpperCase in some instances and LowerCase in others, and saved the newUnit as well as the updated property in the POST request and it worked.

Mongoose: use hook/middleware to remove referenced objects (has many)

In a nodejs app using Mongoose, I have a relation has many: App has many AppClients.
Models:
const mongoose = require('mongoose')
const appSchema = new mongoose.Schema({
name: String,
appClients : [{ type: mongoose.Schema.Types.ObjectId, ref: 'AppClient' }]
})
const App = mongoose.model('App', appSchema)
module.exports = App
const appClientSchema = new mongoose.Schema({
access_key: String,
secret_key: String
})
const AppClient = mongoose.model('AppClient', appClientSchema)
The thing is that I want to remove all AppClients documents related to an App document when it is deleted.
My current code is:
exports.delete = async function(req, res, next) {
const app = await App.findOne({ _id: req.params['id']}).exec()
const listToDelete = [...app.appClients]
await App.deleteOne({ _id: req.params['id']}).exec()
await AppClient.remove({_id: {$in: listToDelete}}).exec()
res.redirect('/apps')
}
This works but I was wondering how to use a hook. I have taken a look at the middleware but I cannot make it work with the pre('remove'), it is never called. I was using something like this:
appSchema.pre('remove', (next) => {
console.log('pre remove') //never called
})
remove is a middleware that's specified on a schema level (like in your example) but it runs on a document level. So the only way to get this fired is to fetch the document and then execute remove() on it
const app = await App.findOne({ _id: req.params['id']}).exec();
await app.remove(); //prints 'pre remove'
There is a paragraph in Mongoose docs about that:
Note: There is no query hook for remove(), only for documents. If you set a 'remove' hook, it will be fired when you call myDoc.remove(), not when you call MyModel.remove(). Note: The create() function fires save() hooks.
According the documentation you should be able to use the hook without finding the document in a first hand.
appSchema.pre('remove', { query: true }, function() {
console.log('remove');
});

How to add save another Schema data to a model?

I am using mongoose with Mongodb v3.4.3
Below is my image model code
const mongoose = require("mongoose");
const CoordinateSchema = require("./coordinate");
const ImageSchema = new mongoose.Schema({
image_filename: {
type: String,
required: true
},
image_url: {
type: String,
required: true
},
coordinates: [CoordinateSchema],
});
Below is my CoordinateSchema code
const mongoose = require("mongoose");
const CoordinateSchema = new mongoose.Schema({
coordinates : {
type: Array,
default: [],
}
});
module.exports = CoordinateSchema;
Below is my api js code running on express,
router.post('/receiveCoordinates.json', (req, res, next) => {
Image.findOneAndUpdate({image_filename:req.body.file_name}).then((image) => {
})
});
How to finish this code so I can store coordinates data in Image model.
Thanks.
UPDATE
To update the coordinates inside of findOneAndUpdate, you simply check that the returned document isn't undefined (which would mean your image wasn't found). Modify your api.js code like so:
router.post('/receiveCoordinates.json', (req, res, next) => {
Image.findOneAndUpdate({image_filename:req.body.file_name}).then((image) => {
if (!image) return Promise.reject(); //Image not found, reject the promise
image.where({_id: parent.children.id(_id)}).update({coordinates: req.body.coordinates}) //Needs to be an array
.then((coords) => {
if (!coords) return Promise.reject();
//If you reach this point, everything went as expected
});
}).catch(() => {
console.log('Error occurred');
);
});
Here's my guess why it isn't working.
In ImageSchema, you are sub-nesting an array of CoordinateSchema. But CoordinateSchema is a document which already contains an array.
This is probably not what you're looking for. If you're using mongoose version 4.2.0 or higher, you can nest CoordinateSchema inside of ImageSchema as a single document. Re-write your ImageSchema like this:
// ...
const ImageSchema = new mongoose.Schema({
// ...
coordinates: CoordinateSchema,
});
If this didn't work or doesn't resolve your issue, please let me know so we can work together to find a solution.

Categories

Resources