Mongoose is not creating collection name from variable - node.js

schema.js
****************************************************************
var nameOfCategory = "hello";
ecomm.createProductCollection = async (categoryName) =>{
nameOfCategory = categoryName;
}
const productSchema = new mongoose.Schema({
productName:{
type: String,
require: true
}
},
// { collection: nameOfCategory }
)
ecomm.productModel = new mongoose.model(nameOfCategory, productSchema, nameOfCategory)
*******************************************************************************************
controller.js
await ecomm.createProductCollection("someDynamicName")
await ecomm.productModel.create(product);
-----------------------------------------------------------------------
Expected Result: Collection created with name "someDynamicName".
Actual Result: Collection created with name "hello".
But while printing in console, nameOfCategory displays "someDynamicName"

This works out when the productSchema is created inside function.
But still could not find the reason why the code in question is not working.
schema.js
****************************************************************
ecomm.createProductCollection = async (categoryName) =>{
nameOfCategory = categoryName;
const productSchema = new mongoose.Schema({
productName:{
type: String,
require: true
}
},
// { collection: nameOfCategory }
)
ecomm.productModel = new mongoose.model(nameOfCategory, productSchema, nameOfCategory)
}

Related

Not able to save my array in to mongodb database

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

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

Returning a field which is not in the model

I have projects which store batches of records. When retrieving a project from my mongoDB using mongoose, I want to calculate the number of batches which belong to that specific project.
My Project model-schema currently looks like so:
const schema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
}
}, {
timestamps: true
})
My batch model-schema looks:
const schema = new Schema({
project:{
type: ObjectId,
ref: 'Project',
required: true
},
file: {
type: String,
required: true
},
orginal_name: {
type: String,
required: true
}
}, {
timestamps: true
})
I have a function which counts the number of batches that belong to a project based off of the batch.project field and then adds to it to the JSON object of the project (e.g project.batchCount). However I have run into a problem where the new field project.batchCount is being removed by the by the models toJSON or toObject function because the field is not present in the model-schema.
My current solution is to add it to the model-schema as a 'dummy field' which is never saved to the mongoDB:
const schema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
},
batchCount: {
type: Number
}
}, {
timestamps: true
})
However I do not like this way as it makes my model larger than it needs to be and slightly less 'readable'.
Is there a better way to do this? And if so how?
I think what you're looking for is a virtual on the schema. A virtual on a document in mongoose is available locally, but doesn't get saved to the DB.
I liked the idea of using a static on the schema to get the project counts. I also liked the idea of just calling the method on the document rather than the Model, so I implemented it in an instance method. A static would have worked just as well. It depends on your preference.
Here is what I came up with:
batch.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const batchSchema = new Schema({
project: {
type: Schema.Types.ObjectId,
ref: 'Project',
required: true
},
file: {
type: String,
required: true
},
original_name: {
type: String,
required: true
}
}, {
timestamps: true
});
batchSchema.virtual('projectCount')
.get(function () {
return this._projectCount;
})
.set(function (v) {
this._projectCount = v;
});
batchSchema.methods.count = async function () {
let found = await this.model('Batch').count({ project: this.project });
this._projectCount = found;
};
const Batch = mongoose.model('Batch', batchSchema);
module.exports = Batch;
The virtual called projectCount has a simple setter and getter to overwrite the value if you need to or retrieve it once it's been set.
The instance method on each document is called count() and calls the Model.count() method with a query for the current document's project _id.
project.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const projectSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
}
}, {
timestamps: true
});
const Project = mongoose.model('Project', projectSchema);
module.exports = Project;
populate.js
#!/usr/bin/env node
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Batch = require('./batch');
const Project = require('./project');
const projects = [];
const batches = [];
for (let i = 0; i < 10; i++) {
const project = new Project({
name: `project${i}`,
slug: `slug${i}`
});
for (let j = 0; j < 10; j++) {
const batch = new Batch({
project: project._id,
file: `file${j}`,
original_name: `name${j}`
});
batches.push(batch);
}
projects.push(project);
}
async function add () {
await conn.dropDatabase();
const savedProjects = await Project.create(projects);
const savedBatches = await Batch.create(batches);
console.log(`Added ${savedProjects.length} projects.`);
console.log(`Added ${savedBatches.length} batches.`);
return conn.close();
}
add();
populate.js is just how I created the collections and the docs for this example, nothing fancy here.
get.js
#!/usr/bin/env node
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Batch = require('./batch');
async function get () {
let f = await Batch.findOne({});
await f.count();
console.log(`the project for this batch has ${f.projectCount} matches`);
return conn.close();
}
get().catch(console.error);
Once the instance method count() is called, we have access to the value stored in the virtual projectCount.
bash output
49729301: ./populate.js
Added 10 projects.
Added 100 batches.
49729301: ./get.js
the project for this batch has 10 matches
49729301:
mongo shell output
49729301: mongo --quiet test
> db.batches.findOne()
{
"_id" : ObjectId("5acbbebb4cd320cb4e403e8f"),
"project" : ObjectId("5acbbebb4cd320cb4e403e8e"),
"file" : "file0",
"original_name" : "name0",
"createdAt" : ISODate("2018-04-09T19:27:55.395Z"),
"updatedAt" : ISODate("2018-04-09T19:27:55.395Z"),
"__v" : 0
}
>
as you can see, virtual properties are only available locally, and do not get stored in the db.

How to set ObjectId as a data type in mongoose

Using node.js, mongodb on mongoHQ and mongoose. I'm setting a schema for Categories. I would like to use the document ObjectId as my categoryId.
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
categoryId : ObjectId,
title : String,
sortIndex : String
});
I then run
var Category = mongoose.model('Schema_Category');
var category = new Category();
category.title = "Bicycles";
category.sortIndex = "3";
category.save(function(err) {
if (err) { throw err; }
console.log('saved');
mongoose.disconnect();
});
Notice that I don't provide a value for categoryId. I assumed mongoose will use the schema to generate it but the document has the usual "_id" and not "categoryId". What am I doing wrong?
Unlike traditional RBDMs, mongoDB doesn't allow you to define any random field as the primary key, the _id field MUST exist for all standard documents.
For this reason, it doesn't make sense to create a separate uuid field.
In mongoose, the ObjectId type is used not to create a new uuid, rather it is mostly used to reference other documents.
Here is an example:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Product = new Schema({
categoryId : ObjectId, // a product references a category _id with type ObjectId
title : String,
price : Number
});
As you can see, it wouldn't make much sense to populate categoryId with a ObjectId.
However, if you do want a nicely named uuid field, mongoose provides virtual properties that allow you to proxy (reference) a field.
Check it out:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Schema_Category = new Schema({
title : String,
sortIndex : String
});
Schema_Category.virtual('categoryId').get(function() {
return this._id;
});
So now, whenever you call category.categoryId, mongoose just returns the _id instead.
You can also create a "set" method so that you can set virtual properties, check out this link
for more info
I was looking for a different answer for the question title, so maybe other people will be too.
To set type as an ObjectId (so you may reference author as the author of book, for example), you may do like:
const Book = mongoose.model('Book', {
author: {
type: mongoose.Schema.Types.ObjectId, // here you set the author ID
// from the Author colection,
// so you can reference it
required: true
},
title: {
type: String,
required: true
}
});
My solution on using ObjectId
// usermodel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const ObjectId = Schema.Types.ObjectId
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
UserSchema.set('autoIndex', true)
module.exports = mongoose.model('User', UserSchema)
Using mongoose's populate method
// controller.js
const mongoose = require('mongoose')
const User = require('./usermodel.js')
let query = User.findOne({ name: "Person" })
query.exec((err, user) => {
if (err) {
console.log(err)
}
user.events = events
// user.events is now an array of events
})
The solution provided by #dex worked for me. But I want to add something else that also worked for me: Use
let UserSchema = new Schema({
username: {
type: String
},
events: [{
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}]
})
if what you want to create is an Array reference. But if what you want is an Object reference, which is what I think you might be looking for anyway, remove the brackets from the value prop, like this:
let UserSchema = new Schema({
username: {
type: String
},
events: {
type: ObjectId,
ref: 'Event' // Reference to some EventSchema
}
})
Look at the 2 snippets well. In the second case, the value prop of key events does not have brackets over the object def.
You can directly define the ObjectId
var Schema = new mongoose.Schema({
categoryId : mongoose.Schema.Types.ObjectId,
title : String,
sortIndex : String
})
Note: You need to import the mongoose module
Another possible way is to transform your _id to something you like.
Here's an example with a Page-Document that I implemented for a project:
interface PageAttrs {
label: string
// ...
}
const pageSchema = new mongoose.Schema<PageDoc>(
{
label: {
type: String,
required: true
}
// ...
},
{
toJSON: {
transform(doc, ret) {
// modify ret directly
ret.id = ret._id
delete ret._id
}
}
}
)
pageSchema.statics.build = (attrs: PageAttrs) => {
return new Page({
label: attrs.label,
// ...
})
}
const Page = mongoose.model<PageDoc, PageModel>('Page', pageSchema)
Now you can directly access the property 'id', e.g. in a unit test like so:
it('implements optimistic concurrency', async () => {
const page = Page.build({
label: 'Root Page'
// ...
})
await page.save()
const firstInstance = await Page.findById(page.id)
const secondInstance = await Page.findById(page.id)
firstInstance!.set({ label: 'Main Page' })
secondInstance!.set({ label: 'Home Page' })
await firstInstance!.save()
try {
await secondInstance!.save()
} catch (err) {
console.error('Error:', err)
return
}
throw new Error('Should not reach this point')
})

Resources