Mongoose include model with dependencies - node.js

I'm creating a node application based off of This example.
server.js has the following:
fs.readdirSync(__dirname + "/app/model").forEach(function (file) {
if (~file.indexOf(".js")) {
require (__dirname + "/app/model" + "/" + file);
}
});
This includes all of the files from app/model. This works, but the problem is that my models have reference dependencies that don't come up in the example. Specifically I have a model like this:
ResourceSchema = new Schema({
"comment": [Comment]
});
However when I run node I get an error that Comment is not defined, which is not really unexpected.
This does not come up in the example even though the schema has a reference because it uses:
user: {type : Schema.ObjectId, ref : 'User'},
My question is, should I use "comment": {type: [Schema.ObjectId], ref: "Comment"} instead (or something else?) Is there a proper way to include the schema reference for Comment in the Resource Schema declaration?

If you want to define an array of references, you should use the following definition:
ResourceSchema = new Schema({
"comment": [{type : Schema.ObjectId, ref : 'Comment'}]
});
The way you defined comments is used to define an array of subdocuments (see mongoose API docs).
So, you should use it only if you want to store all your comments directly inside of the parent document. In this case Comments schema should be already defined, or required from another module.

Related

what is the difference between these 2 mongoose schemas while exporting them?

Below is the schema for one user profile in my project.
var agencyProfile = mongoose.Schema({
name: {
type: String
},
user: {
type: mongoose.Schema.ObjectId,
ref: "users"
}
})
I would like to know what is the difference between these 2 exported schemas?
module.exports = mongoose.model('agencyProfile', agencyProfile);
vs
module.exports = mongoose.model('agencyProfile', agencyProfile, "agencyProfile");
The third argument basically allows you to change the collection name (which is inferred from the model-name by default) to something else. From the documentation:
When no collection argument is passed, Mongoose uses the model name.
If you don't like this behavior, either pass a collection name, use
mongoose.pluralize(), or set your schemas collection name option.
In your case, it does not make any difference as the collection name matches the model name agencyProfile.

Mongodb Many to Many relationship

I am working on a webapp built on mean.js and part of the app is projects that have tags, similar to a blog site that has tags for each blog entry.
I have searched everywhere for a good example/tutorial explaining the best way to create a many to many relationship using mongodb/mongoose, but I can't seem to find anything.
Each project can have multiple tags and I want the users to be able to find all projects with a specific tag.
I would really appreciate any suggestions/examples on the correct way to achieve this. TIA
Keep an array of id's in both collections. For example:
coll1:
{
_id: ObjectId("56784ac717e12e59d600688a"),
coll2_ids: [ObjectId("..."), ObjectId("..."), ObjectId("..."), ...]
}
coll2:
{
_id: ObjectId("56784ac717e12e59d600677b"),
coll1_ids: [ObjectId("..."), ObjectId("..."), ObjectId("..."), ...]
}
The advantage of this approach is that when you have a document from either collection, you can immediately pull all associated documents from the opposite collection simply by doing
obj = db.coll1.findOne({})
db.coll2.find({_id: {$in: obj['coll2_ids']}}) # returns all associated documents
and vice-versa.
For many-to-many relationship, I have found mongoose-relationship really useful.
Here's a sample how you would solve it using mongoose-relationship module:
// In TagsModel.js file ----------------------------------------------
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var relationship = require("mongoose-relationship");
var tagSchema = new schema({
projects: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Project',
childPath: 'tags'
}],
....
});
tagSchema.plugin(relationship, {
relationshipPathName: 'projects'
});
var tagModel = mongoose.model('Tag', tagSchema);
--------------------------------------------------------------------
// In ProjectModel.js file -----------------------------------------
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var Projects = new Schema({
tags : [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tag'
}],
...
});
var tagModel = mongoose.model('Project', tagSchema);
With this model structure, you will be able to find projects by specific tag and tags by project.
It seems you just want to have an Array of tags within your Project schema.
var Tags = new Schema({
name : String
});
var Projects = new Schema({
tags : [String] // When you save a Tag just save the Name of it here
});
// This way you could easily look it up
// Assuming "name" was passed in as a param to express
Projects.find({ 'tags' : req.params.name }) // voila!
There are other ways as well (such as saving ObjectIds).
This method would be easier if you think Tag names could change often but once again if a Tag is "deleted" you'd have to go look through all Projects to remove that ObjectId
tags : [{ type: Schema.ObjectId, ref : 'Tags' }]
Basically the gist of this is that you are saving the a String or ObjectId reference (of the name) in an Array of "tags" within your Project model.
Just remember when you're going to Edit Tag names / Delete a tag / etc, you'll want to go through and update (if they are saved as Strings) / remove those tags from any Projects that have it in their array.

Easy way to reference Documents in Mongoose

In my application I have a User Collection. Many of my other collections have an Author (an author contains ONLY the user._id and the user.name), for example my Post Collection. Since I normally only need the _id and the name to display e.g. my posts on the UI.
This works fine, and seems like a good approach, since now everytime I deal with posts I don`t have to load the whole user Object from the database - I can only load my post.author.userId/post.author.name.
Now my problem: A user changes his or her name. Obviously all my Author Objects scattered around in my database still have the old author.
Questions:
is my approuch solid, or should I only reference the userId everywhere I need it?
If I'd go for this solution I'd remove my Author Model and would need to make a User database call everytime I want to display the current Users`s name.
If I leave my Author as is, what would be a good way to implement a solution for situations like the user.name change?
I could write a service which checks every model which has Authors of the current user._id and updates them of course, but this sounds very tedious. Although I'm not sure there's a better solution.
Any pro tipps on how I should deal with problems like this in the future?
Yes, sometime database are good to recorded at modular style. But You shouldn't do separating collection for user/author such as
At that time if you use mongoose as driver you can use populate to get user schema data.
Example, I modeling user, author, post that.
var UserSchema = new mongoose.Schema({
type: { type: String, default: "user", enum: ["user", "author"], required: true },
name: { type: String },
// Author specific values
joinedAt: { type: Date }
});
var User = mongoose.model("User", UserSchema);
var PostSchema = new mongoose.Schema({
author: { type: mongoose.Scheam.Types.ObjectId, ref: "User" },
content: { type: String }
});
var Post = mongoose.model("Post", PostSchema);
In this style, Post are separated model and have to save like that. Something like if you want to query a post including author's name, you can use populate at mongoose.
Post.findOne().populate("author").exce(function(err, post) {
if(err)
// do error handling
if(post){
console.log(post.author.type) // author
}
});
One solution is save only id in Author collection, using Ref on the User collection, and populate each time to get user's name from the User collection.
var User = {
name: String,
//other fields
}
var Author = {
userId: {
type: String,
ref: "User"
}
}
Another solution is when updating name in User collection, update all names in Author collection.
I think first solution will be better.

Weird mongoose behavior when viewing ObjectIds?

When viewing a sub-document with Robomongo I see something like this:
"views" : [
ObjectId("53a478431275cf0f3d91e27d"),
ObjectId("53a478431275cf0f3d91e27d")
]
But when I pull down the object through Mongoose into node.js, I see something like this:
views:
[ { _bsontype: 'ObjectID',
id: 'T\u001aôj#Ü«m¢©Ö',
viewDate: '2015-07-07T23:21:32.259Z' } ]
Yes, the schema is a little different, and I'm trying to write a script to remediate the data into the new format.
The schema is currently
views: [{view:{type: Schema.Types.ObjectId, ref: 'users'},viewDate:{type: Date, default: Date.now}}],
But
A) Why does the view object look all messed up in the latter, and
B) How can I get what I see in Robomongo? (Answered. See edit)
EDIT: Question B is answered. If I do .lean() to my query, then I'll be able to get it back as a non-mongoose object and it'll look how I expect it to look. So that just leaves question A
I managed to reproduce this.
First, you declared a schema similar to this:
views : { type : Schema.Types.ObjectId, ref : 'users' }
You created and wrote documents to the database using that schema.
Then you changed the schema to your current:
views: [{
view : { type: Schema.Types.ObjectId, ref: 'users' },
viewDate : { type: Date, default: Date.now }
}]
Using that schema, you are reading the documents that you wrote to the database using the first schema.
Those schema are fundamentally different: the first is stored as a single ObjectId in the database (the term "subdocument" is a bit confusing, because in Mongoose, subdocuments are documents that are stored with their parent document; the method you're using is called "population" in Mongoose-speak), but the second schema makes views an array of documents that have two properties (view, which is stored as an ObjectId and viewData which is a date).
This confuses Mongoose because it tries to apply the second schema to documents that were written using the first schema, and because of that, it's showing the internal representation of an ObjectId object instead of a stringified version of it.
This also explains why .lean() shows the correct results, because that tells Mongoose to return raw documents (as they are stored in the database) instead of trying to convert them according to the schema.

Mongoose: Find reference documents by id before inserting a doc

Is there a way for checking if a reference document id exists in the array field of its "parent" model?
Imagine you know the objectId you want to check if exists because you don't want duplicates and also want to avoid that an error was thrown when trying to insert it.
I would like to know if there is an elegant and simple way as the method mongoose provides when working with subdocuments: var doc = car._components.id(theComponentIdIWantToCheck)
In this case it is a reference document:
Example:
// Car.js
var CarSchema = new Schema({
name: String,
description: String,
_components: [ { type: Schema.Types.ObjectId, ref: 'Component'},]
});

Resources