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) {
//...
});
Related
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.
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
I'm trying to model a Saas application in which I'll have different companies using the services. I'm a bit confused on best way of modelling my mongoose database for this type of service.
So the model is something like
Company
Name
Users
->Roles (Admin,editor etc)
Projects
What's the best way of modeling such an application?
Should i create different models for user, projects etc and attach the company unique ID to all those users or projects that are within that company. Also Referencing those users or projects in the company model as well. Something like
//User is used as a separate model and is only referenced in company schema
var userSchema = new Schema({
Name : String,
Role: {
type: String,
enum: ['admin','reader', 'editor'],
default: 'reader'
},
Company : { type: Schema.ObjectId, ref: 'Company' }
});
mongoose.model('User', userSchema);
var companySchema = new Schema({
Name : String,
Users : [
{ type: Schema.ObjectId, ref: 'User' }
],
Projects : [
{ type: Schema.ObjectId, ref: 'Project' }
]
});
mongoose.model('Company', companySchema);
Or
Just referencing the user and project schemas in the company schema without having separate models
//userSchema is used as a child in company schema
var userSchema = new Schema({
Name : String,
Role: {
type: String,
enum: ['admin','reader', 'editor'],
default: 'reader'
},
});
var companySchema = new Schema({
Name : String,
Users : [
userSchema
],
Projects : [
projectSchema
]
});
mongoose.model('Company', companySchema);
Which is the best approach on the long run in terms of easily making queries for the sub-documents, memory allocation, public api exposure in future if required?
And if its the second way then do i need to have id's or those subdocuments? Can i just use the unique company id for making queries and then finding the stuff needed once the relative company is found within the document?
Nevermind, found a very detailed answer from the series of posts https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-1
I'm trying to locate a reference to another schema and update a sibling field. Specifically, I'm trying to manipulate the hasResponded field below based on a particular 'survey' ObjectId.
My schema looks like this:
var userSchema = new mongoose.Schema({
// some other stuff
surveys: [{
survey: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Survey'
},
hasResponded: {
type: Boolean,
default: false
}
}]
});
If you have a survey id, simply search for all the users that have this particular id in the array.
Something like that:
Users.find({surveys: { "$elemMatch": { type: <ID> } } });
Then, iterate through the users and their corresponding survey array to find the ones that match the id you gave.
Got to say I would structure this db a little different if this query takes place often.
Make a new Schema - UserSurveys that holds the id of the user and the survey + hasResponded. Like this:
var UserSurveySchema = new mongoose.Schema({
user_id: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
survey_id: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'}
hasResponded: {type:Boolean, 'default':false}
...
});
You might also want to keep an index on the user and survey ids.
Then, it will be much easier to update the field, requests will take much shorter times.
Hope this helps.
I have a requirement for setting a TTL on a collection of Docs in my app but also need those docs to be referenced by another collection and am unsure how this would work?
Does setting a TTL on a document remove that document from all other reference docs when it is expired or would I have to write a backend process myself and not use mongoDb's TTL indexes?
An example schema of the functionality I would want :
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' },
createdAt: { type: Date, expires: 60*60*24 },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
So after a Day the Story doc would expire and i would need the ref to that story to expire from the person
You cannot handle this with TTL indexes.
You could use a cron job/scheduled task to remove old documents.
I'd suggest you read this, a nice lightweight way of implementing background processes on MongoDb.
The built-in TTL doc expiration has no support for removing references to expired docs.
You'll need to provide that functionality in your own code if you need it.