Many to many relationship in mongoose.js - node.js

please check the schema of Student and Subject
var Schema = mongoose.Schema
var studentSchema = Schema({
name: String,
roll: Number,
subject: [{
type: Schema.Types.ObjectId,
ref: 'subject'
}]
})
var subjectSchema = Schema({
name: String,
marks: Number,
student: [{
type: Schema.Types.ObjectId,
ref: 'student'
}]
})
var studentModel = mongoose.model('student', studentSchema);
var subjectModel = mongoose.model('subject', subjectSchema);
I want that whenever I save a new Subject its student field should get the id of the student and corresponding student's subject field should get the id of subject.
I've written following code
var subject = new subjectModel({
name: "FCPC",
marks: 79,
})
studentModel.findOne({name:"Yname"}).exec(function(err,stu){
subject.student=stu._id;
subject.save();
studentModel.updateOne({_id:stu._id},{$push:{subject:subject._id}}).exec();
})
Its working fine , but please tell me is it the correct approach and is there any better way to accomplish it

You just need to set Subject in Student Object.
You can get Student's subject by findOne({name:"name"})
So for example with student.id="id" and student.name="name" so for student.subject[i].id ( desired subject id ) we can get students that have this subjects with :
studentModel.find({
subjects:{
$elemMatch:student.subject[i].id
}
});
So you don't need to add Student to Subject Schema
PS : you also don't need to update your datas twice

Related

How can I populate an array field without ref in MongoDB (mongoose)?

I have a Schema that contains an array field. In this array field I'll insert objects that contains the author's _id and an author's comment.
I want populate this field, but my Schema don't have ref.
that's my Schemas
const Book = new Schema({
name: {
type:String,
required: true
}
authors: [{
type: Array
}]
})
const Author = new Schema({
name: {
type:String,
required: true
}
(... author's data)
})
I will insert objects in authors field of Book collection:
insertAuthor = {
_id:id,
comment: 'comment'
}
It's working fine, but I can't populate the authors field.
Can anybody help me with this?
Thank you!!
You can specify the model in the populate if the ref is not defined in the Schema. So from what I understand you need to query the Book by populating Author.
const books = await Book.find().populate({path: 'authors._id', model: 'Author'}).exec();
Also in your Book schema if you are inserting a JSON object as mentioned in your question then don't need to define type: Array inside the JSON. You can update it as below.
const Book = new Schema({
name: {
type:String,
required: true
}
authors: [{}] //or [{_id: {type: Schema.Types.ObjectId}, comment: {type: String}}]
})

What data structure should i use in mongoDB

I am making a small project in MEAN stack but I can not figure out what data structure to use.
So, in this case, I don't have any reference in the store I just fetch store when a person asks for his or her stores.
var personSchema = Schema({
_id : Number,
name : String,
stores: [{ type: Schema.Types.ObjectId, ref: 'Store' }]
});
var storeSchema = Schema({
_id : Number,
title : String
});
AND:
In this case, I give the store a reference of the person so when a person asks for his or her stores I fetch all the store which has a reference to the person.
var personSchema = Schema({
_id : Number,
name : String
});
var storeSchema = Schema({
_id : Number,
owner : { type: Schema.Types.ObjectId, ref: 'Person' },
title : String
});
Which one is better approach?
First one is better to use as it helps in clean code and queries.

Mongoose: Referencing a subdocument from another subdocument AND population

I have a document model called school which in turn has two subdocuments:
students
class
Each student belongs to one class.
var classSchema = new Schema({
name: {type : String}
});
var studentSchema = new Schema({
name : {type : String, required:true},
class: { type: ObjectId, ref: 'Institute.classes' },
});
var schoolSchema = new Schema({
name : {type : String, required:true},
students : [studentSchema],
classes : [classSchema]
});
var School = mongoose.model('School', schoolSchema);
Now when I am fetching a student, I want to populate the class as well. I am doing this as:
var school_id = req.params.school_id;
var student_id = req.params.student_id;
School
.findOne({'_id': school_id, 'students._id': student_id})
.populate({path: 'students.class'})
.exec(function (err, data) {
done(err, data);
});
When I do this, I get following error:
"message": "Schema hasn't been registered for model \"School.classes\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
Is is possible to populate a subdocument from another subdocument?
Any help is appreciated. Thanks :)

Is there way to populate fields from SubDocuments?

I have the following model where I'm using SubDocuments:
const SubCategory = new Schema({
label: { type: String}
});
const Category = new Schema({
label: { type: String},
subcategories: [SubCategory]
});
Also I have a model where I'd like to save subcategory
const Article = new Schema({
title: { type: String }
subcategory: { type: Schema.Types.ObjectId, ref: 'SubCategory'}
},
Could you help me how I should populate subcategory for Article model?
Short answer: To populate a document the referenced document needs to be in its own collection.
Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s). -- http://mongoosejs.com/docs/populate.html
So in the example above, SubCategory would need to be a model/collection on its own that is referenced from both the Category collection and the Article collection.

Nesting and querying documents with MongoDB

I have a data structure that looks more or less like this:
var city = {
name: String,
mayor: Person,
citizens: [Person]
};
I think my use case is pretty good for using MongoDB, but I have a few questions. The above model has been implemented with mongoose, and I use sub documents to nest Persons inside City.Obviously the citizens array could get quite long, and that's why MongoDB seems like a good choice.
Is this an efficient way to structure my data? I'm wondering if Mongo will have to do some sort join each time I want to select a city, with all of it's citizens (or a large part of them). That would obviously defeat the purpose of using a document database.
Also, when in the mongo terminal i try something like db.cities.find({name:'Berlin'}).mayor I don't get any results. When I try db.cities.find({name:'Berlin'}) it shows the city, and it also shows an object id for the mayor but not all the properties of the mayer/Person.
So how do I query with sub documents and is this a good way of working?
I would recommend your schema as following with Populate. There are no joins in MongoDB but sometimes we still want references to documents in other collections. This is where population comes in.
var personSchema = Schema({
_id : Number,
name : String,
});
var Person = mongoose.model('Person', personSchema);
var citySchema = Schema{
name: String,
mayor: { type: Schema.Types.ObjectId, ref: 'Person' },
citizens: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
};
var City = mongoose.model('City', citySchema);
To query the city like Berlin through
City.find({name: 'Berlin'})
.populate('mayor')
.populate('citizens')
.exec(...)
The mayor and citizens related document will be retrieved from DB.
If you concern there are too many citizens in the city to make the city too large with populate the person in the city, another option is
var personSchema = Schema({
_id : Number,
name : String,
cityId : { type: Schema.Types.ObjectId, ref: 'City' }
});
var Person = mongoose.model('Person', personSchema);
var citySchema = Schema{
name: String,
mayor: { type: Schema.Types.ObjectId, ref: 'Person' }
};
var City = mongoose.model('City', citySchema);
To query the city Berlin with aggregation, here are the sample codes.
City.find({name: 'Berlin'})
.populate('mayor')
.exec(function(err, city) {
Person.aggregate([
{$group: {cityId: city._id, citizens: {$push: '$name'}}}
// ...
], function(err, result) {
//...
});

Resources