Not able to save my array in to mongodb database - node.js

I am new in backend development, creating a post API by using node js and MongoDB,
This is my schema model
// restaurantSchema.js
const mongoose = require('mongoose');
const foodSchema = new mongoose.Schema({
categoryName:{
type: String,
required: true
},
dish: [
{
dishName: {
type: String,
required: true
},
dishDescription: {
type: String
},
dishPrice: {
type: Number,
required: true
},
// dishImg: {
// type: String,
// },
dishRating: {
type: String
},
}
]
});
const restaurantSchema = new mongoose.Schema({
restaurantName: {
type: String,
},
restaurantRating:{
type: String
},
restaurantAddress: {
type: String
},
category: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'foodSchema'
}
]
})
const Restaurant = new mongoose.model('RESTAURANT', restaurantSchema);
module.exports = Restaurant;
here is the router code
// auth.js
const express = require('express');
const router = express.Router();
require('../db/conn');
const Restaurant = require('../model/restaurantSchema');
const app = express();
router.post('/post-food', async (req, res) => {
try {
console.log(req.body, "body")
const restaurant = new Restaurant({
restaurantName: req.body.restaurantName,
restaurantRating: req.body.restaurantRating,
restaurantAddress: req.body.restaurantAddress,
category: [
{
categoryName: req.body.categoryName
},
{
dishName: req.body.dishName,
dishDescription: req.body.dishDescription,
dishPrice: req.body.dishPrice,
dishRating: req.body.dishRating
}
]
});
await restaurant.save();
console.log(restaurant, "save")
res.status(201).json({ message: "food added successfully" })
} catch (err) {
console.log(err)
}
})
module.exports = router;
this code is going to the app.js (main file)
I am getting only this data while I comment out the category(array) in the auth.js file otherwise not able to save any data in the database file
[1]: https://i.stack.imgur.com/wKn9p.png

First of all this is a great question with very valid code, nicely done!
So, everything is correct in your code except you should declare your foodSchema as a mongoose model as well, just like you're doing so with this one
const Restaurant = new mongoose.model('RESTAURANT', restaurantSchema);
You don't need to use new, you can just omit it.
So basically next to this add the declaration for your foodSchema it should look something like this:
const Restaurant = mongoose.model('RESTAURANT', restaurantSchema);
const Food = mongoose.model('FOOD', foodSchema)
and for exporting them you could do something like this:
module.exports = {
Restaurant: Restaurant,
Food: Food
}
For importing any of these two in your router file you could use destructuring:
const { Restaurant } = require('../model/restaurantSchema')
I hope this helps, have a good day!

You have declared category as a list of mongoose typeId and inserting a list of object of type foodSchema. In your schema file after declaring foodSchema, create a model using that food schema.
const Food = mongoose.model('FOOD', foodSchema)
and now in restaurant schema the category will refer ref: 'Food'
mongoose expecting category as ["61407ce0b6c1fc83d896002e"] but you are providing
[{
categoryName:{
type: String,
required: true
},
dish:[...]
}]
export both Food and Restaurant from the schema file.
module.exports = Food;
for creating restaurant first create an Food
const food = new Food({
categoryName: req.body.categoryName,
dish: [ {
dishName: req.body.dishName,
dishDescription: req.body.dishDescription,
dishPrice: req.body.dishPrice,
dishRating: req.body.dishRating
} ]
})
let foodInserted = await food.save();
then the foodInserted._id to the category of restaurant.
const restaurant = new Restaurant({
restaurantName: req.body.restaurantName,
restaurantRating: req.body.restaurantRating,
restaurantAddress: req.body.restaurantAddress,
category: [foodInserted._id]
});
Follow this documentation

Related

Populate array inside object in mongoose

I have a company model which looks like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const CompanySchema = new Schema(
{
companyName: String,
settings: {
type: {
priceVisible: Boolean,
allowPickupAddressAddition: Boolean,
paymentMethodsAvailable: [
{ type: Schema.Types.ObjectId, ref: "PaymentMethod" },
],
},
},
}
);
const Company = mongoose.model("Company", CompanySchema);
module.exports = Company;
And I want to populate the values store in paymentMethodsAvailable array. Here is relevant controller code:
const company = await Company.findOne({ _id: id }).populate([
{
path: "settings",
populate: [{path: "paymentMethodsAvailable"}]
},
]);
But this doesn't work as expected. I can see that it might be trying to populate settings object, and fails there. Is there a way in which I can tell mongoose to populate settings.paymentMethodsAvailable ?
Try this
const company = await Company.findOne({ _id: id }).populate(
"settings.paymentMethodsAvailable"
);
You can find more examples in the documentation. I was using this section as a reference https://mongoosejs.com/docs/populate.html#populating-maps
Mongoose provides clear syntax
The following code will work fine
const company = await Company.findOne({ _id: id }).populate(
"settings.type.paymentMethodsAvailable"
);
if(!company) {
// if there is no company with that id
// this acully not error it's simply
// return `null` to tell you company not found.
res.status(404).send()
}
Also: you can go further and populate specific fields inside settings.paymentMethodsAvailable
const company = await Company.findOne({ _id: id }).populate(
"settings.type.paymentMethodsAvailable",
"field1 filed2 filed3"
);

create mongoose schema with array of objects

Even though the question have been asked numerous time none of the answers have any idea to help me .
This is my mongoose Schema
const mongoose = require('mongoose')
const { Schema } = mongoose;
const recipeSchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: true },
imagePath: { type: String, required: true },
ingredients:[
{
name:{type:String, required:true},
amount:{type:Number,required:true }
}
]
})
module.exports = mongoose.model("Recipe",recipeSchema);
what i need is to get the data from angular and store it to my database using node
const Recipe = require('../models/recipe.model');
const recipeCtrl={};
recipeCtrl.CreateRecipeServer =async(req, res, next)=>{
if(!req.file) {
return res.status(500).send({ message: 'Upload fail'});
}
else {
let ingredientArray=new Array()
ingredientArray.push(req.body.ingredients)
req.body.imageUrl = 'http://192.168.0.7:3000/images/' + req.file.filename;
const recipe=new Recipe({
name:req.body.name,
description:req.body.description,
imagePath:req.body.imageUrl,
ingredients:[
{
name:ingredientArray,
amount:ingredientArray }
]
});
await recipe.save();
}
Everything except the ingredients array works perfectly/as i require.
I am getting the ingredients as an array from formdata so it have to be JSON.stringfied inorder to append with the form. So what i am getting at backend is string . eg
**[{"name":"dasdasd","amount":2},{"name":"fsfsd","amount":2},{"name":"sdfsdgd","amount":3}]**
this is a string. Any ideas on how to convert it and store to database
use JSON.parse and choose first element of that
JSON.parse(data)[0]

How to create and update referenced documents with mongoose?

I have this setup
var NotesSchema = new mongoose.Schema({
title: String,
category: [{ type: mongoose.ObjectId, ref: "Categories", default: [] }],
},{ timestamps: { createdAt: 'created_at' } });
var CategoriesSchema = new Schema({
name: {type: String, required: true}
})
var Notes = mongoose.model('Notes', NotesSchema);
var Cat = mongoose.model('Categories', CategoriesSchema);
If I want to create a new note and categories I do this
.get('/new', async (req, res) => {
var post1= {
title : "Post: books, thriller, crime and classics",
favorite : true,
categories:[ 'thriller', 'books']
}
try{
var note = post1.categories.map( (cat)=>{
var ca = new Cat({name: cat})
ca.save()
return ca._id
})
post1.category = note
const newNote = new Notes(post1);
const n = await newNote.save()
res.send(n)
} catch(error) {
console.error(error);
};
})
If I were to create a new note that has some new categories I'm stuck.
var post1= {
...
categories:[ 'thriller', 'books', 'classics']
}
'thriller' and 'books' already exist, but 'classics' doesn't.
I tried Cat.find({"name": {$in: post1.categories}}).exec() but I can't seem to look through that data.
All the example tutorials seem to just add one new entry at a time.
Inside your post1.categories.map callback you are creating the Cat documents and call save() on them but you do not await the returned promise. So Note is created before those promises are fulfilled and thus post1.category will be an empty array.
You can fix this by awaiting the save-promises of the categories:
const savePromises = post1.categories.map((cat) => {
const ca = new Cat({name: cat})
return ca.save();
})
const categoryIds = (await Promise.all(savePromises)).map(category => category._id);
post1.category = categoryIds;
// rest of your code

Filter moongose results by reference field using express

I need filter the products of a collection by category id which is a reference field.
product.js
const restful = require('node-restful')
const mongoose = restful.mongoose
const productSchema = new mongoose.Schema({
name: { type: String, required: true },
category: {type: mongoose.Schema.Types.ObjectId, ref: 'CategoryProduct'}
})
productSchema.pre('find', function () {
this.find().populate('category')
})
module.exports = restful.model('product', productSchema)
routes.js
const express = require('express')
const auth = require('./auth')
module.exports = function (server) {
const protectedApi = express.Router()
server.use('/api', protectedApi)
const Product = require('../api/product/productService')
Product.register(protectedApi, '/products')
}
If I run this on Postman, http://localhost:3003/api/products/?name__regex=/test/i, I can get all products which contains 'test' on name.
So I try get all products by a specific category doing this, http://localhost:3003/api/products/?category=5af3ac4372edc6000468d766.
But as the category is an objectID, I receive this error:
{
"message": "Cast to ObjectId failed for value \"5\" at path \"category\" for model \"SimpleProduct\"",
"name": "CastError",
"stringValue": "\"5\"",
"kind": "ObjectId",
"value": 5,
"path": "category"
}
How do I filter the products by category? I do not know how to treat this parameter correctly and pass to mongoose
Here is my CategoryProduct.js file
const restful = require('node-restful')
const mongoose = restful.mongoose
const categorySchema = new mongoose.Schema({
name: {type: String, required: 'O campo Categoria é obrigatório'},
status: {type: Number, required: true},
updated_at: {type: Date}
})
module.exports = restful.model('CategoryProduct', categorySchema)
you would have to do the following in your route:
const mongoose = require('mongoose');
router.get('/', (req, res, next) => {
const category = req.query.category; // assign the query param for use
// mongoose query now
Product.find({category: mongoose.Types.ObjectId(category)}, (err, result) => {
if (err) {console.log(err)};
if (result) {
res.json(result);
}
});
});
This is just a basic example. Since you are using node-restful you might have to adjust the code but the main idea here is that you require the mongoose module using
const mongoose = require('mongoose')
then pass your category query param to it to convert the string to objectID using:
mongoose.Types.ObjectID(YOUR_CATEGORY_PARAM_HERE)
You can do this anywhere, in the your routes.js or in your express.js, just have to have the string you want to convert :)
hope that helps :).

File Structure of Mongoose & NodeJS Project

I currently have all my models (Schema definitions) in the /models/models.js file for my Mongoose/NodeJS application.
I'd like to break these apart into different files as such: user_account.js, profile.js, etc. However I cannot seem to do so as my controllers break and report back "cannot find module" once I pull these classes apart.
My project structure is as follows:
/MyProject
/controllers
user.js
foo.js
bar.js
// ... etc, etc
/models
models.js
server.js
The contents of my models.js file looks like this:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
mongoose.connect('mongodb://localhost/mydb');
var UserAccount = new Schema({
user_name : { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
password : { type: String, required: true },
date_created : { type: Date, required: true, default: Date.now }
});
var Product = new Schema({
upc : { type: String, required: true, index: { unique: true } },
description : { type: String, trim: true },
size_weight : { type: String, trim: true }
});
My user.js file (controller) looks like this:
var mongoose = require('mongoose'),
UserAccount = mongoose.model('user_account', UserAccount);
exports.create = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
// Do epic sh...what?! :)
}
How can I break the schema definition into multiple files and also reference it from my controller? When I do reference it (after the schema is in a new file) I get this error:
*Error: Schema hasn't been registered for model "user_account".*
Thoughts?
Here's a sample app/models/item.js
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
},
equipped: Boolean,
owner_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
room_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
}
});
var Item = mongoose.model('Item', ItemSchema);
module.exports = {
Item: Item
}
To load this from an item controller in app/controllers/items.js I would do
var Item = require("../models/item").Item;
//Now you can do Item.find, Item.update, etc
In other words, define both the schema and the model in your model module and then export just the model. Load your model modules into your controller modules using relative require paths.
To make the connection, handle that early in your server startup code (server.js or whatever). Usually you'll want to read the connection parameters either from a configuration file or from environment variables and default to development mode localhost if no configuration is provided.
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost');
A couple answers here really helped me develop an alternative approach. The original question is regarding breaking just the Schema definition out, but I prefer to bundle the Schema and Model definitions in the same file.
This is mostly Peter's idea, only exporting the model definition by overriding module.exports to make accessing the model from your controller a little less verbose:
Project layout:
MyProject
/controllers
user.js
foo.js
bar.js
// ... etc, etc
/models
Item.js
server.js
models/Item.js would look like:
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
}
});
module.exports = mongoose.model('Item', ItemSchema);
// Now `require('Item.js')` will return a mongoose Model,
// without needing to do require('Item.js').Item
And you access the model in a controller, say user.js, like:
var Item = require(__dirname+'/../models/Item')
...
var item = new Item({name:'Foobar'});
Don't forget to call mongoose.connect(..) in server.js, or wherever else you deem appropriate!
I recently answered a Quora question with regard to this same problem.
http://qr.ae/RoCld1
What I have found very nice and saves on the amount of require calls is to structure your models into a single directory. Make sure you only have a single model per file.
Create an index.js file in the same directory as your models. Add this code to it. Be sure to add the necessary fs require
var fs = require('fs');
/*
* initializes all models and sources them as .model-name
*/
fs.readdirSync(__dirname).forEach(function(file) {
if (file !== 'index.js') {
var moduleName = file.split('.')[0];
exports[moduleName] = require('./' + moduleName);
}
});
Now you can call all your models as follows:
var models = require('./path/to/models');
var User = models.user;
var OtherModel = models['other-model'];
Peter Lyons pretty much covered the basis.
Borrowing from the above example (removing the lines after the schema) I just wanted to add:
app/models/item.js
note: notice where `module.exports` is placed
var mongoose = require("mongoose");
var ItemSchema = module.exports = new mongoose.Schema({
name: {
type: String,
index: true
},
...
});
To load it from the app/controllers/items.js
var mongoose = require('mongoose');
var Item = mongoose.model('Item', require('../models/item'));
Another way without the module.exports or require:
app/models/item.js
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
},
...
});
mongoose.model('Item', ItemSchema); // register model
In the app/controllers/items.js
var mongoose = require('mongoose')
, Item = mongoose.model('Item'); // registered model
Inspired by sequelize-cli, I have a models directory where i define all schema.
Complete app on github: https://github.com/varunon9/node-starter-app-mongo
models/index.js-
'use strict';
const fs = require('fs');
const path = require('path');
const mongoose = require('mongoose');//.set('debug', true);
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
const Schema = mongoose.Schema;
fs
.readdirSync(__dirname)
.filter(fileName => {
return (
fileName.indexOf('.') !== 0)
&& (fileName !== basename)
&& (fileName.slice(-3) === '.js'
);
})
.forEach(fileName => {
const model = require(path.join(__dirname, fileName));
const modelSchema = new Schema(model.schema);
modelSchema.methods = model.methods;
modelSchema.statics = model.statics;
// user.js will be user now
fileName = fileName.split('.')[0];
db[fileName] = mongoose.model(fileName, modelSchema);
});
module.exports = db;
models/user.js-
'use strict';
module.exports = {
schema: {
email: {
type: String,
required: true,
unique: true,
},
mobile: {
type: String,
required: false
},
name: {
type: String,
required: false
},
gender: {
type: String,
required: false,
default: 'male'
},
password: {
type: String,
required: true
},
dob: {
type: Date,
required: false
},
deactivated: {
type: Boolean,
required: false,
default: false
},
type: {
type: String,
required: false
}
},
// instance methods goes here
methods: {
},
// statics methods goes here
statics: {
}
};
I like using classes to organize everything, maybe try this:
const mongoose = require('mongoose')
class UserAccount {
constructor() {
this.schema = new mongoose.Schema({
user_name: { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now }
});
this.model = new mongoose.model('UserAccount', this.schema)
}
create = (obj) => {
return new Promise((resolve, reject) => {
this.model.create({ ...item })
.then((result) => {
resolve(result)
}).catch((err) => {
reject(err)
});
});
}
}
module.exports = UserAccount;
This approach allows you to add custom methods. Plus it combines the controller/model into a single vector, allowing you to detach the model at any time. This may not scale in enterprise, but it may suite smaller apps.

Resources