mongoose - possible circular dependency? - node.js

I have the following mongoose models in my express app:
//dog.js
var mongoose = require("mongoose");
var dogSchema = (exports.dogSchema = mongoose.Schema({
name: { type: String, required: true },
}));
Then I import dog.js to my user.js
//user.js
var mongoose = require("mongoose");
var dog = require("./dog");
var userSchema = mongoose.Schema({
user: { type: String, required: true },
pass: { type: String, required: true },
dogs: [dog.dogSchema],
});
Now, from my routes I am creating a new user like this:
var user = require("../models/user");
var dog = require("../models/dog");
dog = new dog.Dog(dogData);
user = new user.User(data); //this will of course contain also dogData
user.save(next);
Is this the correct way to do this kind of operation? I have the feeling that I might be generating a circular dependency somehow, and anyway it does not look right to me. Any ideas on how to create sub-documents where the schema is from another model file?

You can create simultaneous references in two directions without creating circular problems. Create a reference from one document to the other using ref. From the docs:
http://mongoosejs.com/docs/populate.html
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
Then you can then choose to load the sub document using populate
Story.find({ --your criteria-- })
.populate('_creator')
.exec(function (err, story) {../});
You can then store the 2 schemas in separate .js files and require them both

Related

Can I set a element of my mongoose model to be strictly of some particular other models?

My subsection model should strictly be a type of lecture or quiz model only.
I want something like this. Any idea how to implement it properly?
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const subSectionSchema = new mongoose.Schema({
subSection:{
type:ObjectId,
enum:["Lecture","Quiz"]
}
});
module.exports = mongoose.model("SubSection", subSectionSchema);
Although I can always do this way...check which type it is and set that value.
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const subSectionSchema = new mongoose.Schema({
lecture: {
type: ObjectId,
ref: "Lecture",
},
quiz: {
type: ObjectId,
ref: "Quiz",
},
});
module.exports = mongoose.model("SubSection", subSectionSchema);
But I am looking for something simple.
I'm not sure if I understood your question, but I think what you're looking for are dynamic references. This feature allow us to have dynamic properties on our MongoDB collection.
You'll basically need two properties for it, one for the ObjectID, other for the reference. Whenever you need to populate the reference, it will automatically detects which entity/collection to use.
Here's a quick example from Mongoose docs:
const commentSchema = new Schema({
body: { type: String, required: true },
on: {
type: Schema.Types.ObjectId,
required: true,
// Instead of a hardcoded model name in `ref`, `refPath` means Mongoose
// will look at the `onModel` property to find the right model.
refPath: 'onModel'
},
onModel: {
type: String,
required: true,
enum: ['BlogPost', 'Product']
}
});
const Product = mongoose.model('Product', new Schema({ name: String }));
const BlogPost = mongoose.model('BlogPost', new Schema({ title: String }));
const Comment = mongoose.model('Comment', commentSchema);

How to connect schemas with each other in mongooseJS?

Basically what I am trying to achieve is the following:
In my iOS application a QR-Code can be scanned to get the ID of a specific lounge and with that its JSON data (only ID and name for now) saved in MongoDB.
Every lounge is able (but doesn't have to) to add products to their lounge, so that in the end, after scanning the QR-Code, a customer is able to see which products are offered.
A product contains only a name and imageURL for now, but not a price, because the price can variate and be set by owners of a lounge. So a price of one and the same product can be different.
I would like to know, what the correct and best way is for implementing the schemas for that approach, so that I can easily fetch all products of a specific lounge, maybe based on its ID.
What I got so far:
lounge.js:
const mongoose = require('mongoose');
const loungeSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true }
});
module.exports = mongoose.model('Lounge', loungeSchema);
product.js
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
imageURL: { type: String, required: true, unique: true }
});
module.exports = mongoose.model('Product', productSchema);
Basically you can use mongoose populate. You can achieve this by storing the products in separate collection and basically populate an array on the lounge schema using the _id for the lounge let me show you an example:
Your lounge schema would be something like this
const mongoose = require('mongoose');
const loungeSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true }
products: [type: Schema.Types.ObjectId, ref: 'Products' ]
});
module.exports = mongoose.model('Lounge', loungeSchema);
And your product schema
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true },
imageURL: { type: String, required: true, unique: true }
});
module.exports = mongoose.model('Product', productSchema);
When saving creating a lounge just add the _id of the product to products array that apart of the lounge schema.
So basically you find the product and retrieve its _id.
To run a find query it would be something like this:
lounge.find({}).populate('products').exec();
products array will then have the related products for each lounge

Mongoose not populating array in Mongodb

Here is my Game Schema :
var mongoose = require('mongoose');
const GameSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
publishers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Publisher'
}
]
});
var GameModel = mongoose.model('Game', GameSchema);
module.exports = GameModel;
Here is my Publisher Schema :
var mongoose = require('mongoose');
const PublisherSchema = new mongoose.Schema({
companyName: {
type: String,
required: true
},
firstParty: {
type: Boolean,
required: true
},
website: {
website: String,
}
});
var PublisherModel = mongoose.model('Publisher', PublisherSchema);
module.exports = PublisherModel;
I have a picture of what you can find in my collection "games" in mongoDB :
When I use this route :
router.get('/games', async function(req, res) {
const games = await Game
.find()
.populate('publishers')
.select('title publishers')
res.json(games);
})
I have empty arrays as result for the publishers. If I don't use an array in Schema, this is correcly populated and I got data of publisher into each game. So why mongoose doesn't populate when it is an array?
Check below by modifying the schema definition as below
I think the below could fix your issue, please give a try by redefining the publishers as below
Publishers: [
publisher: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Publisher'
}
]
I think the definition of schema is more about to define the structure of the object that we would like to process, rather about the number of objects that we want to process.
The Schema definition with what I know is defining the semantics of the model and to take advantage of the middle ware functionalities.
You can save multiple objects of which meet the same semantic definition but the definition itself cannot be an array
Thanks
Pavan

Cannot overwrite model once compiled

I want to insert records in mongodb using mongoose but i am getting error "cannot overwrite "story" model once compiled"
app.post('/getdata', (req, res, next) => {
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/mydb');
var Schema = mongoose.Schema;
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
res.send("Om Success");
})
You are initializing mongoose and all the schema everytime someone hits /getdata post endpoint and as you have express app main process will not be terminated until you so that manually or any unhandled error occurs.
So currently in your program, this is the scenario:
First Request to /getdata
Your mongoose will be initialized and Story and Person models are registered with mongoose object and this is global so you can use it from anywhere and this is unique too(your error is here).
From second Request to /getdata
You already registered Story and Person models with mongodb://localhost:27017/mydb DB so as it needs unique model it will throw an error.
Solution
put your initialization code somewhere else where it will be called only once. You can consider structure something like this: https://github.com/ridhamtarpara/express-es8-rest-boilerplate/blob/master/src/api/services/user/user.model.js
or if you want to do this in the same file(not recommended for obvious reasons) do something like this
var express = require('express');
var mongoose = require('mongoose');
var app = express();
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost:27017/mydb');
var personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
app.post('/getdata', (req, res, next) =>
{
res.send("Om Success");
})

How to show relationship in mongoose?

I have two mongoose schemas 'user' and 'project' and i want to show relationship between these schemas like in mysql. How to do this?
And how can i pass user while creating a project?
User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const Schema = mongoose.Schema();
const UserSchema = mongoose.Schema({
fullname: {type: String},
username : {type:String},
password: {type:String}
});
UserSchema.methods.encryptPassword = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10), null);
};
UserSchema.methods.comparePassword = function(userPassword, cb) {
bcrypt.compare(userPassword, this.password, (err, isMatch) => {
if(err) throw err;
cb(null, isMatch);
});
}
module.exports = mongoose.model('User', UserSchema);
project.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema();
const User = require('./user');
const ProjectSchema = mongoose.Schema({
title: {type: String, required: true},
description: {type:String},
created_at: { type: Date, default: Date.now },
publish : { type: Boolean, default: false}
});
module.exports = mongoose.model('Project', ProjectSchema);
Creating schema in Mongoose isn't like creating schema in Relational DBMS, such as MySQL, PostGreSQL.
You can use objectId, bro.
If one project just can be handled by one user, you can use something like this.
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
But if one project is handled by multi users
users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}]
Hope it will help
Schema Change
To create a relationship in Mongoose (MongoDB), create a property on your schema with the following properties
1. type: mongoose.Schema.Types.ObjectId
2. ref: "string name of the Collection this reference lives in"
For example, if you wanted a project to contain a reference to the users inside of it, you could do the fallowing
const mongoose = require('mongoose');
const Schema = mongoose.Schema();
const User = require('./user');
const ProjectSchema = mongoose.Schema({
title: {type: String, required: true},
description: {type:String},
created_at: { type: Date, default: Date.now },
publish : { type: Boolean, default: false},
users: [{type: Schema.Types.ObjectId, ref: 'User'}]
});
module.exports = mongoose.model('Project', ProjectSchema);
Example Opertaion
Given the above Schema, if you wanted to create a new Project and add users to it during creation, you would need to have those users' _id properties on hand (i.e. cached). That could mean making a prior query to the db to get all the users who will be a part of this project or having some client send you the user ids.
A much better option would be to create a project, and update its' users property as users are added or removed.
Here is a quick example
const Project = require('./models/Project.js');
let userIds = // get the user _ids some how
Project.create({
title: 'A sample',
description: 'The usual',
publish: true,
users: userIds
})
.then(callback);

Resources