Confused about Mongoose/Mongo Terminology. Are Sub-Docs/Embedded-Docs also Collections? - node.js

If I have the following mongoose models:
// child.model.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
childSchema = new Schema({
name: String,
age: Number
}, {collection: 'children'});
module.exports = mongoose.model('Child', childSchema);
//parent.model.js
var mongoose = require('mongoose'),
Child = require('./child.model.js'),
Schema = mongoose.Schema,
parentSchema = new Schema({
name: String,
children: [Child.schema]
});
module.exports = mongoose.model('Parent', parentSchema);
Would this mean I would then have two collections, one called 'parents' and one called 'children'?
As I currently understand it the above code creates a nested document structure whereby the Child objects exist only within the collection of Parent documents. However, I'm getting confused by the {collection: 'name'} option that you can pass to the Schema constructor. Is this option just ignored when creating sub-documents like this?

There are two kinds of subdocs - Embedded and Referenced. This is a Mongoose-level classification. At MongoDB level it's just Collections and Documents.
The difference between Embedded and Referenced docs in Mongoose is that the former is akin to having the child schema "embedded" in the parent. I.e. as far as MongoDB is concerned it (Parent) is just a one big document.
Whereas in referenced docs the Parent document only stores the child document's ObjectID, i.e. the child document is "referenced", and it's left up to you to "populate" the entire document.
What you're using children: [Child.schema] is the syntax of an Embedded document.
Would this mean I would then have two collections, one called 'parents' and one called 'children'?
So you'll have only 1 collection in MongoDB.
However, I'm getting confused by the {collection: 'name'} option that you can pass to the Schema constructor. Is this option just ignored when creating sub-documents like this?
That option is just so that if you were to actually create a model from that schema, it uses the name you provided instead of automatically inferring.

Related

Mongoose Typescript 2 Schema that have a field that use each other schemas

I am trying to declare 2 schemas, using mongoose and typescript, that both have a field that their type is the other schema, like this:
const Schema1: Schema = new Schema({
fieldA: Number,
fieldB: Schema2
};
const Schema2: Schema = new Schema({
fieldA : Number,
fieldB : Schema1
};
The problem is that i get an error, because the Schema2, on the fieldB of Schema1 is being used before being assigned.
I already tried to declared it before like:
var Schema2: Schema = new Schema();
But then, when adding values to the database, Schema2 is just an empty schema with no fields.
I don't think you can create subdocuments like this. According to docs, you can create an array of subdocuments and single subdocuments. And they always maintain the parent-child relationship.
In your case, if you want to use subdocuments, you must declare it first. So in schema1, you've declared schema2 as a subdocument (child), but it got referenced below. The structure is
|--mongoose-schema
|--- children subdocument
|---- parent schema
I found out that you can add a field to the schema after you define it, like this:
Schema1.add({ fieldB: Schema2 });

How to use find() called from mongoose model to get just that specific model from a collection that contains multiple models?

I have these specific schemas:
const PersonSchema = new Schema({
Name: String,
})
const StudentSchema = new Schema({
Class: String,
Professor: String
})
const ProfessorSchema = new Schema({
Class: String,
Students: Number
})
I produce the following models, note that the Student and Professor are extended from the Person model:
mongoose.model("Person", PersonSchema, "objects" )
Person.discriminator("Professor", ProfessorSchema, "objects")
Person.discriminator("Student", StudentSchema, "objects")
I store all of them in the same collection "objects" and when I call the find() method from any of the models I get all the objects in the collection and not just from the specific model. How can I just retrieve from the collection one specific model?
The way to solve this problem of getting all the documents from all models when asking just a specific one is to provide some specific arguments arguments when constructing the model.
Instead of just providing the collection name in the model:
Person.discriminator("Professor", ProfessorSchema, "objects")
You should provide a conjunct of arguments that specify the collection and the type attribute:
const baseOptions = {
collection: "objects",
discriminatorKey: '__type'
}
const Professor = Person.Model.discriminator("Professor", ProfessorSchema, baseOptions)
The default discriminatoryKey now has the value equal to the model name instead of its default value that is the collection name. When you use the find() method it only retrieves the type connected with the model.

Mongoose - Defining a model for a preexisting collection

I have imported some CSV data to my database through mongoimport, which created my collection during the import.
When defining my model in Node, what do I pass for the schema parameter? Viewing my db in compass shows a schema already created based off the imported data.
I'm currently passing an empty schema which seems totally wrong.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Units = new Schema({
});
module.exports = mongoose.model('Units', Units, 'units');
The schema should contain something like this that defines the kind of data you're working with.
var Units = new Schema({
f_name: String,
l_name: String,
manager: Boolean
});
See 'Defining your schema'.
Also, I don't believe mongoose.model takes a third parameter.
module.exports = mongoose.model('Units',Units);
Edit: yes it does.

Data modeling in mongoDB - Social-media app

I'm trying to build some sort of a social media app using node.js and mongoDB.
I have a mongoose schema for 'User', and when i render some user page on the app, it needs to also show all of his posts/images/list of friends and etc...
right now i have a mongoose schema for 'UserPost' and also for 'Image', and when i save an image for example, it has a field which keeps the username of the user who uploaded it, so when i render the user page it finds all of his images.
It is the first time i'm dealing with db's so i heard that i might have to use a reference data instead of embedded data.
can someone explain to how should i organize the data model for my app?
It's very handful to use mongoose population for handling db references
Define your schemas like these:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var userSchema = Schema({
name : String,
posts : [{ type: Schema.Types.ObjectId, ref: 'Post' }]
});
var postSchema = Schema({
title : String,
images : [{ url: String, filename: String }]
});
var User = mongoose.model('User', userSchema);
var Post = mongoose.model('Post', postSchema);
According to mongoose population docs, you can get all your data:
User.findOne().populate('posts').exec(function(error, user) {
console.log(user.posts) // there are populated posts objects inside array
})
Not sure, is it a good idea to use separated collection for image uploads, it's simpier to embed it inside Post (or User), but you may always add { type: Schema.Types.ObjectId, ref: 'Image' } for images
MongoDB is a NoSql DBMS. It means, you schould not create references between data fields, because the performance coming from NoSql will be killed: https://en.wikipedia.org/wiki/NoSQL
But, if you are really thinking you need references, checkout this: http://docs.mongodb.org/master/reference/database-references/
You can reference to a data document in the mongoDB by the _id ("Page"=>"createdBy" = "User"=>"_id" for example).
It depends of what kind of data you want to embed. MongoDB has object size limits according to the storage engine you use. Thus you should predict or estimate the the size of the object you want to embed.
See more about limits here: http://docs.mongodb.org/master/reference/limits/
See more about references here: http://docs.mongodb.org/master/reference/database-references/

Underscore.js _.extend function does not copy mongoose model properties when they are not defined in the schema?

Why can I not use underscore(_) extend to update mongoose models where the properties are not defined in the schema definition. Is there a way to get around this?
Node model:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var NodeSchema = new Schema({
label: {type : String, default : 'none'}
}, { strict: false })
mongoose.model('Node', NodeSchema)
Node Controller:
var node = new Node();
node = _.extend(node, {"EXTENDNOTinSchema":"TRUE"});
console.log("extend: " + node);
node.set("SETNOTinSchema","TRUE");
console.log("set: " + node);
Console Output:
extend: { __v: 0,
_id: 50bb05656880a68976000001,
label: 'none' }
set: { __v: 0,
_id: 50bb05656880a68976000001,
label: 'none'
SETNOTinSchema: TRUE}
This is happening because if something is not in the schema then Mongoose cannot use 'defineProperty', and this treats the assignment like any other.
So first off, just to be clear.
node = _.extend(node, {"EXTENDNOTinSchema":"TRUE"});
is identical to this:
node['EXTENDNOTinSchema'] = 'TRUE';
Which is entirely different from this, in the general case.
node.set("SETNOTinSchema","TRUE");
The trick is that Mongoose is smart, and using the defineProperty function I mentioned above, it can bind a function to get called for things like this:
node['INSCHEMA'] = 'something';
But for things that are not in the schema, it cannot do this, so the assignment works like a normal assignment.
The part that tripping you up I think is that console.log is doing some hidden magic. If you look at the docs, console.log will call the inspect method of an object that is passed to it. In the case of Mongoose, it's models do not store attributes directly on the model object, they are stored on an internal property. When you assign to a property being watched with defineProperty or call set, it stores the value on the internal object. When you log the model, inspect prints out the internal model contents, making it seem like the model values are stored right on the object.
So when you do
console.log(node);
what you are really seeing is
console.log(node.somehiddenproperty);
So the answer to your question is really, if you have a bunch of values that are not in the schema, you cannot use _.extend. Instead, just use set because it takes an object anyway.
node.set({"EXTENDNOTinSchema":"TRUE"});

Resources