Remove embedded document in mongoose - javascript

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)

Related

Populating an Object with Information from a Child Object with Different Model (JS/Mongoose)

Here is what I have. I created a project model that references a user model for an array of members.
var ProjectSchema = new mongoose.Schema(
{title: {
type: String,
required: true
},
members: [
{user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
}
}],
});
User Schema (I have code that creates a model from both of these schemas)
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
}
});
In a separate file, I want to export the JSON of a found project and include the information of the users in the members array, but I am not sure how to do that. This is the code I have right now.
const project = await Project.findById(req.params.proj_id).populate(
'members'
);
res.json(project);
It has no trouble finding the project but the only information I can get on the members is their id. I tried using for loops to gather the information from members separately using the id that I can get from the project, but the code gets messy and I am hoping to find a simpler way to do it.
You can use mongoose populate query to get all members associated with a project. It should populate array of objects users associated to a project. You should be doing something like this:
const project = await Project.findById(req.params.proj_id)
await project.populate('members').execPopulate()
console.log(project.members)
Mongoose docs for the reference: Populate
You can give the model to your mongoose populate.
const project = await Project.findById(req.params.proj_id)
.populate({
'members',
model: UserModel
})
.exec()
res.json(project);
keep in mind you've to create UserModel just like
const UserModel = mongoose.model('User', userSchema);

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();

Trying to understand how mongoose populate or join works

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.

MissingSchema Error: Schema hasn't been registered for model "User"

I keep getting this error whenever I launch my application:
--MissingSchema Error: Schema hasn't been registered for model "User" --
I'm working a tutorial from the "Mongoose for Application Development" book by Simon Holmes.
I'm at Chapter 5 "Interacting with Data - Creation"
Here's my code:
app.js:
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, project = require('./routes/project')
, http = require('http')
, path = require('path');
db.js:
//Creating the Application Schemas:
//====================================
//User Schema:
//===============
var userSchema = new mongoose.Schema({
name: String,
email: {type: String, unique:true},
createdOn: { type: date, default: date.now },
modifiedOn: Date,
LastLogin: Date
});
//Build the User Model:
//===========================
mongoose.model( 'User', userSchema );
User.js:
var mongoose = require("mongoose");
var User = mongoose.model( 'User' );
ERROR THAT RESULTS:
throw new mongoose.Error.MissingSchemaError(name);
^
MissingSchemaError: Schema hasn't been registered for model "User".
Use mongoose.model(name, schema) at Mongoose.Model (C:\Users\New\Desktop\mongoose_pm_app\
mongoosepm\node_modules\mongoose\lib\index.js.311.13)
at Object. <anonymous> (C:\Users\New\Desktop\mongoose_pm_app\mongoosepm\routes\user.js:2:21)
atModule._compile (module.js:456:26)
atObject.Module._extensions..js (module.js:474:10)
atModule.load (module.js:356:32)
at Function.Module._load (module.js:364:17)
at require (module.js:380:17)
at Object <anonymous> (C:\Users\New\Desktop\mongoose_pm_app\mongoosepm\app.js:8:12)
at Module._compile (module.js:456:26)
25 June 19:52:55 - [nodemon] app crashed waiting for file changes before starting...
I'm young to mongoose and mongodb.
I've been through the books errata pages to
check if I mistyped anything but its all the same as here.
I also tried downloading the sample code from PACKT, the sample code
looks the same.
Any and all assistance would be Greatly appreciated.
Thanks.
You need to require your db.js file someplace, as otherwise the code in it will never run, and the schema will never be registered.
Also, I would recommend that you define your user schema inside of User.js and add the model to exports, like so:
User.js
//User Schema:
//===============
var userSchema = new mongoose.Schema({
name: String,
email: {type: String, unique:true},
createdOn: { type: date, default: date.now },
modifiedOn: Date,
LastLogin: Date
});
//Build the User Model:
//===========================
var User = mongoose.model( 'User', userSchema );
//Exports
//=======
exports = User;
This allows you to do just require the file elsewhere to get a reference to the User model (instead of having to retrieve it through mongoose.model). For example:
var User = require('User');
// ...
var fred = new User({ ... }); // create a new user
fred.save(function (err, user) { ... });
I've not read the book that you referred to, so I'm not sure if there might be a good reason to define all your schemas in one file (which is the direction it looked like you were going in with db.js). From what I've seen, it's more common to have one schema/model per file.
That error comes from one line:
var User = mongoose.model( 'User' );
You need to provide a Schema object to mongoose.model() method. The first param, 'User', just tells mongoose in what will the collection be named. The second one defines the user schema.
So either do something like Sergey has answered, or add these few changes.
To your db.js, add export line at the bottom:
module.exports = userSchema;
Then require it in User.js:
var mongoose = require("mongoose");
var userSchema = require("./db.js");
var User = mongoose.model( 'User', userSchema );
module.exports = User;
Alternatively you can just alter the last line in your Schema definition, by exporting the user model when you build it:
//Build the User Model:
//===========================
module.exports = mongoose.model( 'User', userSchema );
Then you don't need User.js at all.
Although, then your file naming doesn't follow convention, if you care about it.
Advantages of separating Schema and Model are when you require multiple complex schemas, when you have a lot of schema plugins so you want a simpler model file where you require all that. If you only have a simple schema like above, I recommend that you use a single-file version.

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