Trying to understand how mongoose populate or join works - javascript

I just want to see how to join different collections. Suppose I have a patient collection and doctor collection. I want to do this because a doctor could have a lot of patients and I'm not sure if I should put all the patients into an array of objects in the model field called patients : [] or do what I was now trying to practice with populate method mongoose.
Now I have another question. If I go with the populate(join) method all the doctors patients would be all together. I think it is weird because it sound like personal information will be mixed up with different people. I know that populate associates a patient with a doctor by associating an Id with a ref. Is that a good way to do it?
Anyways I tried playing around with populate and I failed miserably. I'll show you the code below. If you can help me make the joining of the 2 collections like the way I described that would be awesome. It would also be great if you addresses some other concerns.
What I tried to do was associate doctor2 with patient 1.
I get an error :
throw new MongooseError.MissingSchemaError(name);
MissingSchemaError: Schema hasn't been registered for model "Doctor".
Use mongoose.model(name, schema)
Codes
var express = require("express");
var app = express();
var mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/population");
var db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", function(){
console.log("connected")
var doctorSchema = mongoose.Schema({
name : String,
address : String,
username: String,
password : String,
patients : [{type : mongoose.Schema.Types.ObjectId, ref: "Patient"}]
})
var patientSchema = mongoose.Schema({
_doctor : {type: mongoose.Schema.Types.ObjectId, ref : "Doctor"},
name: String,
illness : String
})
//compiling our schem into a Model. A class where we construct documents
var Doctor = mongoose.model("doctor", doctorSchema );
var Patient = mongoose.model("patient", patientSchema);
var doctor1 = new Doctor({name : "doc1", address :"add1", username :"user1", password : "pass1"})
console.log(doctor1.username);
//creating a patient for doctor2
var doctor2 = new Doctor({name: "doc2", address : "add2", username : "user2", password : "pass2"});
doctor2.save(function(err){
var patient1 = new Patient({
name : "pat1",
illness: "high",
_doctor: doctor2._id
})
patient1.save(function(err){
console.log("saved")
})
})
Patient.findOne({name : "pat1"})
.populate("_doctor")
.exec(function(err, patient){
console.log("the creator is %s", patient._doctor.name)
})
})
app.listen(3000, function(){
console.log("listening on port: " , 3000)
})

Issues of your codes
First of all, please make sure the right model name when you reference it.
var Doctor = mongoose.model("Doctor", doctorSchema ); // rather than doctor
var Patient = mongoose.model("Patient", patientSchema); // rather than patient
Secondly, after the patient1 is saved successfully, then find it in the db. Because the save() is Async operation.
patient1.save(function(err){
if (err)
console.log(err);
else {
console.log("saved");
Patient.findOne({name : "pat1"})
.populate("_doctor")
.exec(function(err, patient){
if (err)
console.log(err);
else {
console.log(patient);
console.log("the creator is %s", patient._doctor.name);
}
});
}
});
The Data Schema
As we know, either put the patient as reference to doctor
var doctorSchema = mongoose.Schema({
patients : [{type : mongoose.Schema.Types.ObjectId, ref: "Patient"}]
});
Or put the doctor as reference to patient
var patientSchema = mongoose.Schema({
_doctor : {type: mongoose.Schema.Types.ObjectId, ref : "Doctor"},
});
Both of them could be OK. However, the data schema should meet your requirement for querying more efficiently.
I prefer to put the doctor as reference to patient, because one doctor could have more patients as you said. The max size of mongodb document is 16 megabytes. If we put the patient as reference to doctor, the doctor document could be more larger under more patients case.
Anyway, which schema you chosen to meet your requirement is the first concern.

Related

Is it possible to populate without `_id`?

I Set manually an uid to each item in my collections...I Want To Know It's Possible that I use uid for populate?
I Dont Want Use from '_id' because Have Many collections In My DB And should change many things...somthing like this :
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var PersonSchema = new Schema({
name : String
, age : Number
, stories : [{ type: String, ref: 'Story' }]
});
var StorySchema = new Schema({
_creator : { type: String, ref: 'Person' }
, title : String
, fans : [{ type: String, ref: 'Person' }]
});
var Story = mongoose.model('Story', StorySchema);
var Person = mongoose.model('Person', PersonSchema);
No, it is not possible to have a mongodb document without '_id' , if you want to avoid duplication of ids, you can put uid in '_id' before saving the document.
If you keep uid seperately, '_id' will get autopopulated as ObjectId.
Here is a clear example of how to do it (https://mongoosejs.com/docs/populate.html):
Story.
find(...).
populate({
path: 'fans',
select: 'name -_id' // <---------------'-_id' exclude _id field.
}).
exec();

Adding a list of objectIds to an array in a collection (ref)

I have a user and doctor collection/model. here's the file it is in
module.exports = function(mongoose){
var User = mongoose.Schema({
username: {type : String, unique : true},
password : String,
nameOfOrg : String,
workers : [],
doctors : [{type : mongoose.Schema.Types.ObjectId, ref : Doctor}],
patients : [{type : mongoose.Schema.Types.ObjectId, ref : Patient}]
})
var Doctor = mongoose.Schema({
name : String,
address : String,
parentOrg :[{type : mongoose.Schema.Types.ObjectId, ref : User}],
patients :[{type : mongoose.Schema.Types.ObjectId, ref : Patient}]
})
var Patient = mongoose.Schema({
name : String,
DOS : String,
parentOrg :[{type : mongoose.Schema.Types.ObjectId, ref : User}],
parentDoctor : [{type : mongoose.Schema.Types.ObjectId, ref : User}]
})
var models = {
User : mongoose.model("user", User),
Doctor : mongoose.model("doctor", Doctor),
Patient : mongoose.model("patient", Patient)
};
return models;
}
I have a route handler like doctor/:name every time I go there I put the param in the db so doctor/doc1 adds doc1 to the name field in the doctors collection. Well every time that happens Im trying to add the objectId of this newly created document to the doctors array in the users collection. I think that is what you are supposed to do to use populate in the future to keep data separate in different collections then bring the appropriate parts together. Any ways I'm having problems adding the doctors ID to the doctors array in the users collection when I go to the route. here is the routing app.js code
mongoose.connect("mongodb://localhost/pop3");
var models = require("./db")(mongoose);
app.get("/:name", function(req, res){
var user1 = new models.User({"username": req.params.name})
user1.save(function(err, doc){
if(err){
console.log(err)
}
console.log(doc)
res.send(doc)
app.locals.user = doc
})
})
app.get("/doctor/:name", function(req, res){
var doctor = new models.Doctor({"name" : req.params.name, "parentOrg" : app.locals.user._id})
// app.locals.user.doctors.push(app.locals.user._id)
models.User.findOne({"username": app.locals.user.username}).push(doctor._id)
doctor.save(function(err, doc){
if(err){
console.log(err)
}
console.log(doc)
res.send(doc)
})
});
I tried to folow the mongoose doc on populate they show to do a similar thing I think by demoing this:
aaron.stories.push(story1);
aaron.save(callback);
I guess arron will be my user that was just saved.
stories will be the doctors field and
story1 will be the doctor.
I just couldn't do it
EDIT:: I get this error in the browser window:
TypeError: models.User.findOne(...).push is not a function

Mongoose Followers/Friends Relationship Query

I am trying to make a query that returns posts from both Friends and Non-Friends, but ensures Friends Posts are at top of list. What I have now only gets posts from Friends:
Schemas
var Post = mongoose.Schema({
name: String,
user: { type:ObjectId, ref:'User' },
createdAt: { type:Date, default:Date.now }
});
var User = mongoose.Schema({
username: String
});
var Relationship = mongoose.Schema({
from: { type:ObjectId, ref:'User' },
to: { type:ObjectId, ref:'User' }
});
Query looks like
Relationship.find({ from : thisUser },function(err,docs){
if (err) {console.log(err);}
var query = Post.find();
var plucked = _.pluck(docs,'to');
query.where('user').in( plucked );
query.sort('-createdAt');
query.limit(20);
query.skip( 20 * page);
query.exec(function(err,posts){
if (err) {console.log(err);}
res.send(posts);
});
});
The client will grab say 20 posts on each page. So any suggestions on how I can return all posts, but ensure posts by Friends appear first? For instance, if there are 100 posts that meet the query criteria and 30 of those are from friends, the first page and half of the second will all be friends posts (sorted by createdAt).
If I need to redo the schemas and relationships thats fine as well.
What I would have done is to skip the Relationship model all together (if it is not absolutely essential) and have a friends field in User like this instead:
var User = mongoose.Schema({
username: String,
friends: [User],
});
Then you're able to query the friends posts like this:
Post.find({user: {$in: thisUser.friends}}, function(err, posts) {
...
})
And append it with non-friends using $nin instead of $in.
Honestly, the best thing to do is to store the user names with the friendship object. This information is extremely unlikely to change. How many times have you changed your name on Facebook?

Remove embedded document in mongoose

I'm new to node.js and MongoDB. I'm using the Mongoose Library for accessing MongoDB with node.js.
I have two Schemas, Book and Author. Author belongs_to a Book and Book has_many Author.
I have this in my schemas:
var mongoose = require( 'mongoose' );
var Schema = mongoose.Schema;
var Book = new Schema({
title : String,
isbn : String,
authorId : [{ type: Schema.Types.ObjectId, ref: 'Author' }],
updated_at : Date
});
var Author = new Schema({
name : String,
updated_at : Date
});
mongoose.model( 'Book', Book );
mongoose.model( 'Author', Author );
mongoose.connect( 'mongodb://localhost/library' );
The problem is that when I delete a document from Author which is embedded with Book it is deleted without checking the referential integrity. My scenario is that if the Author document is embedded with the Book it can't be deleted. Is Mongoose automatically check the author document embedded in the book ? Is it possible? then how?
You can try the following code for the schema you mentioned.
Author.pre('remove', function(next) {
Author.remove({name: this.name, updated_at: this.updated_at }).exec();
Book.remove({authorId : this._id}).exec();
next();
});
More info on SchemaObj.pre(method,callback)

MongooseJS schema relation

I have MongooseJS schema as follow:
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String,
tasks : [{ type : Schema.ObjectId,
ref : 'Task'}]
})
var ActionSchema = new Schema({
name : String,
description : String,
module : String
})
var enumTaskState = ['New', 'Indexing', 'Idle', 'In Queue', 'Working'];
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
actions : [{type : Schema.ObjectId,
ref : 'Task'}],
user : { type : Schema.ObjectId,
ref : 'User'},
status : { type : String,
enum : enumTaskState,
default : 'New'}
})
Problem is, when I set a task's user, do I manually have to go to the user and add a task there too? This seems like extra work (redundancy), is there an option in mongoosejs that will allow me to specify the relations it will handle everything by itself?
Thanks.
MongoDB is not a relational database, it is a document based based database.
You can get a user's list of tasks by querying the TaskSchema and looking for the user you want. Just make sure to add an index so it will be a fast query:
user: {type: Schema.ObjectId, ref: 'User', index: true}
To elaborate on emostar's answer:
You're trying to use MongoDB like a relational database. You're giving Users a list of Tasks, but each Task is referencing a User. In a typical MongoDB schema, you'd figure out how you want to use the models in your app and just embed the documents where it makes sense (e.g. if Users contains an array of Tasks, there's no need for a task to have a reference to it's owner--just look at the User that owns the collection).

Categories