mongoose-schema - are both children array + parent doc's ID necessary? - node.js

I'm using a couple of one-to-many models and was wondering what the advantage of having both an array of "children" ObjectID()s and a "parent" model's ObjectID() in the child is. For example:
// a client will have a new card every ten visits
var ClientSchema = new Schema({
first_name: String,
last_name: String,
email: String,
cards: [] // ObjectID()s, <--- is this necessary?
});
var CardSchema = new Schema({
client: ObjectID(), // <--- or is this enough?
visits: []
});
I think the client: ObjectID() should do the trick in most cases, specially with the Population options Mongoose offers.

It suffices to store the reference ObjectId in one of the documents.
As you can read in the documentation or in this answer, the reference field needs a type and a ref. The name of field is arbitrary. Once you have done this and registered your models, you can use the models to populate your queries.
var clientSchema = new Schema({
first_name: String,
last_name: String,
email: String
});
var cardSchema = new Schema({
client: {
type: Schema.Types.ObjectId,
ref: 'client'
}
});
// models
var Card = mongoose.model('Card', cardSchema);
var Client = mongoose.model('Client', clientSchema);
Yet, it could be helpful to store an array of ObjectId's. However, this also creates a huge window for mistakes. A mismatch of foreign key and primary key, in SQL dialect. You can always do a count query if you need to count. I would say, do either the above or do this:
var clientSchema = new Schema({
first_name: String,
last_name: String,
email: String,
cards: [
{
type: Schema.Types.ObjectId,
ref: 'card'
}
]
});
var cardSchema = new Schema({
card_name: String
});
// models
var Card = mongoose.model('Card', cardSchema);
var Client = mongoose.model('Client', clientSchema);

Related

Whats the proper way to use mongoose population?

I have two schemas setup:
var ClientSchema = new Schema({
name: String
});
module.exports = mongoose.model('Client', ClientSchema);
And
var PlaceSchema = new Schema({
client: {type: mongoose.Schema.Types.ObjectId, ref: 'Client'},
address: String
});
module.exports = mongoose.model('Place', PlaceSchema);
If i get a list of places, i can populate the client easily like this:
Place.find({}).populate('client')
However, if i want to get a list of all the clients and all of their places, how would i make a query for that? Should i just simply loop through all of the clients and finding the places for it with Place.find({client:client._id) before returning the response?
Well if you have a number of places they will have their own unique ids, and for every place a client is connected to, you can have an array of placeId, you can push a placeId to this array everytime a client is connected to this place.`
var ClientSchema = new Schema({
name: String,
_address: [{type: mongoose.Schema.Types.ObjectId, ref: 'Place'}]
});`
This data structure would only work in one way, PlaceSchema do refer to ClientSchema but ClientSchema do not have any reference to PlaceSchema, the solution would be to define your ClientSchema this way :
var ClientSchema = new Schema({
name: String,
_address: [{type: mongoose.Schema.Types.ObjectId, ref: 'Place'}]
});
It seems kinda dumb but it would be much easier for your case

Accessing mongoose schema array with body-parser

Here is an example schema:
var schema = mongoose.Schema;
var userSchema = new schema ({
username: {type: String},
hobby: [{
indoor: {
type: {type: String},
description: {type: String}
},
outdoor: {
type: {type: String},
description: {type: String}
}
}]
});
module.exports = mongoose.model('User', userSchema);
So, a very simple schema. It asks for a user name, and then it asks for a user to list their hobbies, and is split between indoor and outdoor hobbies, and inside of an array.
Using body-parser, I could get to a user name in a form input like this:
var user = new User();
user.username = req.body.username;
That's simple enough. But how do I get to the hobbies inside of the array? I've tried doing:
user.hobby.indoor.type = req.body.type
But this doesn't work; it just gives me an error. Any help would be appreciated.
The following code will help you access your key properly. As hobby is an array, you need to provide an index for fetching its objects. Dot notation works with objects
user.hobby[0].indoor.type
Js Fiddle: https://jsfiddle.net/x18nyd2e/
So I did end up finding the answer to my problem. Here is an example of what to do:
var user = new User();
user.username = req.body.username;
user.hobby = {{
indoor: {
type: req.body.type,
description: req.body.description
}
}];
And that's how you can get to a property inside of an array.

Adding a Static, Complex Object to Mongoose/Passport Schema

I use Mongoose and Passport in my web application for the addition of new users into my MongoDB database. I use Google oauth for registering/signing in. In my user schema, I have the following defined for the google method:
//user.js
var userSchema = mongoose.Schema({
google : {
id : String,
token : String,
access_token : String,
email : String,
name : String,
picture : String,
nameInfo : Object,
}
});
I use the following method for user creation:
//passport.js
var newUser = new User();
//newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.nameInfo = profile._json.name
newUser.google.email = profile.emails[0].value;
newUser.google.id = profile.id;
newUser.google.picture = profile._json.image.url + '0';
newUser.google.access_token = token;
You can see that all this data goes under the "google" array in the top level of my user document. How would I add a new, complex, static array? For example, I want a new array at the top level of document in the following format:
newUser.dogs = ["cats":[]}]
I need this particular format based on a dependency in how users should look in my web code. I will eventually be adding data to the "cats" array, but it needs to start out empty. When I try varations of this, I'm only able to get the top level array - ex: my document looks like:
objectid: 1000,
google: [...],
dogs: []
when I need it to look like:
objectid: 1000,
google: [...],
dogs: [{"cats":[]}]
What do I need to change to my schema, either on the schema design, or the data going into the schema?
You want to set inner embedded docs.
Here it is sample, You can create it like this.
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var Trigger = new Schema({
type : {type: String},
value: {type: Number}
});
var Field = new Schema({
label : {type: String},
type: {type: String},
triggers: [Trigger]
});
var Form = new Schema({
fields : [Field],
user_id : {type: String}
});

how to implement the function like left join of mysql in mongoose

I am going to implement the function like left join of mysql in mongoose.
the date is
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var personSchema = Schema({
_id : Number,
name : String
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String
});
var personProfile = Schema({
userid : {type: Number, ref: 'Person'},
birthday: Date,
profilelink: String,
email: String
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
var personProfile = mongoose.model('Personprofile', personProfile );
I am going to display the Story model with the user profile.
We have to get the profile info with the _creator of story and the userid of personProfile
How can I get the info using mongoose query?
Thanks Nelis
What your are trying to do is not possible because there is no join statement on mongodb.
You can achieve this in two ways:
1 - By DBRefs: Changing your Schema to one that include all the user info and do not split them in two different schemas as you are doing, see denormalized. Then you can use the Population function to get all the persons data.
2 - By Manual references: The second solution is to make a second call to the database getting the personProfile data using the userid as a filter.
Example 1:
This way you can get all persons data without a second call to the database.
var personSchema = Schema({
_id : Number,
name : String,
birthday: Date,
profilelink: String,
email: String
});
var storySchema = Schema({
_creator : { type : Schema.Types.ObjectId, ref: 'Person' },
title : String
});
Story
.find()
.populate(['_creator'])
.exec(function(err, stories) {
//do your stuff here
}
Notice that I'm using the type Schema.Types.ObjectId and not the Number. This way, you can assign a new value to _creator passing either the _id or the person object and the mongoose will convert the object to its _id. For example, you can post something like
{
_creator : {
_id : 123123123123,
name : 'Foo',
birthday: '0000-00-00',
profilelink: 'http://foo.bar',
email: 'foo#bar.com'
},
title : 'Mr'
}
... and the mongoose will convert to
{
_creator : 123123123123,
title : 'Mr'
}
Example 2:
This way your data still normalized and you can get all the persons data with a second call.
Story
.find()
.exec(function(err, stories) {
var arrayLength = stories.length;
for (var i = 0; i < arrayLength; i++) {
var story = stories[i];
personProfile.findById(story._creator, function (err, person) {
story._creator = person;
}
};
// do your stuff here
}

Nested schema in Mongoose

Thanks for your help and valuable time. As you suggested I have created the schema's as below. Now I want to get records based on customer name to calculate the hours spent by them on each category. Please help me out in this. Thanks in Advance.
/*var query = {'timesheets[0].categories[0].catname':"Admin"} // I want to get all the records or documents with category admin */
Timesheet = new Schema({
created: {type: Date, default: Date.now},
categories: [{
catname: String,
custname: String,
hours: Number
}]
});
User = new Schema({
name: { type: String, required: true },
email:String,
password:String,
type:String,
timesheets: [Timesheet]
});
//timesheets: [{type: mongoose.Schema.Types.ObjectId, ref: 'Timesheet'}
var User = mongoose.model("User",User);
var Timesheet = mongoose.model("Timesheet",Timesheet);
module.exports = function(app) {
var timedata = {
created: new Date('2014-06-05'),
categories:[{catname:"Admin",cusname:"Sony",hours:8}]
}
var user = new User({
name:"Nelson",
email:"nelson#gmail.com",
password:"welcome123",
type:"Solutions"
});
var timesheet = new Timesheet(timedata);
user.timesheets.push(timesheet);
user.save(function(err,user){
console.log(user.timesheets.timesheet);
})
//console.log('category name');
//console.log(user.timesheets[0].categories[0].catname)
var query = {'timesheets[0].categories[0].catname':"Admin"}
// I want to get
all the records or documents with category admin
User.find(query,function(err,catname){
console.log('catname')
console.log(catname)
})
To create a child schema you should first define it and then insert into your primary schema. Alternatively, if you anticipate a lot of timesheets it's probably preferable to reference an independent schema. In both cases it would make sense to attach these to a User schema:
var Timesheet = new Schema({
created: {type: Date, default: Date.now},
categories: [{
name: String,
custname: String,
hours: Number
}]
});
Using embedded documents:
var User = new Schema({
timesheets: [Timesheet]
});
Insert can then be done directly using:
// create new timesheet doc using your user doc
var timesheet = user.timesheets.create(myData);
user.timesheets.push(timesheet);
or simply:
user.timesheets.push(data);
Using referenced documents:
var User = new Schema({
timesheets: [{type: Schema.Types.ObjectID, ref: 'Timesheet'}]
});
insert with:
// push timesheet reference to your user doc
var timesheet = new Timesheet(data);
user.timesheets.push(timesheet._id);

Resources