Deep populate self referencing schema in mongoose - node.js

I have an self referencing employee schema in mongoose.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Employee = new Schema({
name: String,
description: {
type: String,
default: 'No description'
},
manager: {
type: Schema.Types.ObjectId,
ref: 'Employee',
default: null
},
reportee: [{
type: Schema.Types.ObjectId,
ref: 'Employee'
}]
});
An employee can a manager & can have several reportee. If manager is null then the employee is treated as top level employee.
I need to created hierarchy based on this model. I am struggling to generate desired output.
So far I have tried to use popluate() & mongoose-deep-populate module but I am unable to get the desired output. I wonder its becuase I have a self referencing model. Or may be I am not using these two options properly.
This is what I have tried with deep-populate module. It seems to be populating reportee model but not repotree of reportee model. In short its only populating 1 level of records.
Employee.deepPopulate(employees, 'reportee.reportee.reportee.reportee.reportee', function (err, _employee) {
employees.forEach(function (employee) {
});
});
Please suggest how can I retrive all the employee hierarchy ?

To answer my own question, I am using mongoose-deep-populate library.
To use it we need to install the module:
npm install mongoose-deep-populate
//Register the plugin
var deepPopulate = require('mongoose-deep-populate');
Employee.plugin(deepPopulate);
And then use this code:
Employee.deepPopulate(employees, 'reportee.reportee.reportee.reportee.reportee', function (err, _employee) {
employees.forEach(function (employee) {
});
});
This will load 5 levels of reportees as we have mentioned reportee.reportee.reportee.reportee.reportee

Related

mongoose autoincrement counter per unique value

I have the two models, entity and post, and I'm trying to create an auto incremented counter on post for each unique entity:
//entity
var entitySchema = mongoose.Schema({
name: String,
counter: Number,
});
//post
var postSchema = mongoose.Schema({
test: String,
entity: {
type: Schema.Types.ObjectId,
ref: 'entity'
},
ticketNumber: Number //this needs to auto increment starting at zero PER user, so entity A has a 1+, entity 2 has a 1+, etc
});
I can have a sequence on entity, check it every time I create a post and use it, but that could possibly have duplicates.
I found a post suggesting an even on pre post'save', but that wouldn't be unique to each entity, just unique overall.
Any way to get this working on the model itself / a better way of doing this?
You can use the npm package called mongoose-auto-increment.
Your connection would look like this:
var autoIncrement = require('mongoose-auto-increment');
var connection = mongoose.connect('YOUR CONNECTION');
autoIncrement.initialize(connection);
your Schema would look like this:
var postSchema = mongoose.Schema({
test: String,
entity: {
type: Schema.Types.ObjectId,
ref: 'entity'
},
ticketNumber: Number
});
postSchema.plugin(autoIncrement.plugin, { model: 'NAME YOUR MODEL', field: 'ticketNumber', startAt: 0, incrementBy: 1 });

mongoose schema, is it better to have one or have several for different tasks?

so I'm been making a site that has comments section, messaging, profile and shopping for the user. I been wondering about when making a schema for those functions, is it better to have all in one schema like
userSchema {
name: String,
....
....
}
or have them seperate like
userSchema {
}
commentSchema {
}
gallerySchema {
}
No one can give you clear answer for this, everyone has different views.
Basically, It depends on your project's scalability
As I see your requirement for this project
You can create a single schema and use it as embedded form, but it's not a very good idea if you are scaling the app.
My recommendation is to create the separate schema for all the tasks which will be easy to debug,scaling the app and in readable form.
Edit
If you are creating separate schema and want to connect them then you can use populate on the basis of ObjectId
See the docs to populate collections
Example
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);
Population
Story
.findOne({ title: 'Once upon a timex.' })
.populate('_creator')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
});

Why Mongoose Populate Required?

Using mongoose populate:
http://mongoosejs.com/docs/populate.html
It seams that mongoose is forcing me to declare a ref value for populate when I first create the document but in my case i don't have the ref info yet. When I try to create a new document while providing an empty string I get to my developer field I get:
{"message":"Cast to ObjectId failed for value \"\" at path \"developer\"","name":"CastError","type":"ObjectId","value":"","path":"developer"}
Object that I'm saving through mongoose:
var Project = {
name: 'Coolproject',
status: 'pending',
developer: '',
type: 'basic',
};
Project.create(req.body, function(err, project) {
if(err) { return handleError(res, err); }
return
});
My Model:
var ProjectSchema = new Schema({
name: String,
status: {type:String, default:'pending'},
developer:{type: Schema.Types.ObjectId, ref: 'User'},
type:String
});
Basically I need to set it later, but it doesn't seam like this is possible. Currently my work around is populate it with a dummy user until later but this is less than desirable.
Thoughts?
Update
Realized that if i provide a object id like value (55132a418b3cde5546b01b37) it lets me save the document. Very odd. Guess it just figured it can find the document moves on. Wondering why this doesn't happen for a blank value.
The problem is explained in the error message. You cannot save an Empty String in the place of an ObjectId. The field is not listed as 'required', so there is no problem leaving it out and saving the document.
Code correction:
// you can save this
var Project = {
name: 'Coolproject',
status: 'pending',
type: 'basic',
};
You need to use the sparse index in model.
So, the valid model can have developer equal to nil
var ProjectSchema = new Schema({
name: String,
status: {type:String, default:'pending'},
developer:{type: Schema.Types.ObjectId, ref: 'User', sparse:true},
type:String
});
See
http://mongoosejs.com/docs/api.html#schematype_SchemaType-sparse and
http://docs.mongodb.org/manual/core/index-sparse/
for additional info

Storing a copy of a document embedded in another document in MongoDB via Mongoose

We have a requirement to store a copy of a Mongo document, as an embedded subdocument in another document. It should have a reference to the original document. The copied document needs to be a deep copy, like a snapshot of the original.
The original document's schema (defined with Mongoose) is not fixed -
it currently uses a type of inheritance to allow different additions to the Schema depending on "type".
Is there a way to such a flexible embedded schema within a Mongoose model?
Is it something that needs to be injected at runtime, when we can know
the schema?
The models / schemas we have currently look like this:
///UserList Schema: - this should contain a deep copy of a List
user: {
type: ObjectId,
ref: 'User'
},
list: {
/* Not sure if this is a how we should store the reference
type: ObjectId,
ref: 'List'
*/
listId: ObjectId,
name: {
type: String,
required: true
},
items: [{
type: ObjectId,
ref: 'Item'
}]
}
///List Schema:
name: {
type: String,
required: true
},
items: [{
type: ObjectId,
ref: 'Item'
}],
createdBy: {
type: ObjectId,
ref: 'User'
}
The code we currently have uses inheritance to allow different item types. I realise this technique may not be the best way to achieve the flexibility we require and is not the focus of my question.
///Item Model + Schema
var mongoose = require('mongoose'),
nodeutils = require('util'),
Schema = mongoose.Schema,
ObjectId = Schema.Types.ObjectId;
function ItemSchema() {
var self = this;
Schema.apply(this, arguments);
self.add({
question: {
type: String,
required: true
}
});
self.methods.toDiscriminator = function(type) {
var Item = mongoose.model('Item');
this.__proto__ = new Item.discriminators[type](this);
return this;
};
}
nodeutils.inherits(ItemSchema, Schema);
module.exports = ItemSchema;
I think you just need to create an empty {} object for the document in your parent mongoose schema. This way you´ll be able to store any object with a hardcopy of all it´s data.
parentobj : {
name: Sring,
nestedObj: {}
}
I think at this point, what you´ll need is to mark your nested objet as modified before you save it. Here is an example of my mongoose code.
exports.update = function(req, res) {
User.findById(req.params.id, function (err, eluser) {
if (err) { return handleError(res, err); }
if(!eluser) { return res.send(404); }
var updated = _.merge(eluser, req.body);
//This makes NESTEDDATA OBJECT to be saved
updated.markModified('nestedData');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, eluser);
});
});
};
In addition, if you need an array of different documents in nestedDocument, the right way is this one:
parentobj : {
name: Sring,
nestedObjs: [Schema.Types.Mixed]
}
Please check Mongoose Schema Types carefully
EDIT
As you said, I´ll add you final solution as including ItemSchema in the nestedObj array definition to clarifythe type of the object to a determined one..
var ItemSchema = new Schema({
item1: String,
item2: String
});
var parentobj = new Schema({
name: Sring,
nestedObj: [ItemSchema]
});
EDIT 2:
Remember adding new Items to the nestedArray, must be done with nestedArray.push(item)
regards!!

nodejs mongoose many-to-many relation on MongoDB

I'm trying to make a many-to-many relation between two documents in mongoose. Just can't make it work.
I've been trying to utilize the mongoose populate method, but with no success. Does anyone know any good tutorials or example on how to take on the matter?
Update:
Got schemas
var EventSchema = new Schema({
users: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
});
and
var UserSchema = new Schema({
events: [{
type: Schema.Types.ObjectId,
ref: 'Event'
}]
});
In my tests I pused to event model user like so
event.users.push(user);
event.save(function(err, doc) {
user.events[0].should.equal(event._id);
done();
});
firstly I need to push somehow saved event to added user to event. Then by using populate I should can 'dress up' every object in events array and users array. Preferably in post save callback, if I understood populate correctly.
This test pases
it('creates', function(done) {
event.users.append(user);
event.save(function(err, ev) {
user.save(function(err, doc) {
doc.events[0].should.equal(ev._id);
doc.populate('events', function(err, d) {
console.log(d);
done();
});
});
});
});
So I know the ids are stored correctly, but when I run doc.populate() the returned document has events array empty. Just don't get it.
Made it work... used mongo-relation package to add the needed ids for me, but docs for this plugin made me change the schema form
var UserSchema = new Schema({
events: [{
type: Schema.ObjectId,
ref: 'Event'
}]
});
to
var UserSchema = new Schema({
events: [Schema.ObjectId]
});
once corrected, it worked.

Resources