Linking 2 mongoose schemas - javascript

I have two Schemas, a Team and a Match. I want to use the Team Schema to identify the Teams in the Match Schema. So far, here is my Team and Match JS files. I want to link the Team Schema to my Match Schema so that I can identify the home or away team simply, and so that I am storing in the Match Schema an actual Team object.
This way I can refer to the home team for example as Match.Teams.home.name = England (this is just an example of course)
Team.js
'use strict';
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var validatePresenceOf = function(value){
return value && value.length;
};
var getId = function(){
return new Date().getTime();
};
/**
* The Team schema. we will use timestamp as the unique key for each team
*/
var Team = new Schema({
'key' : {
unique : true,
type : Number,
default: getId
},
'name' : { type : String,
validate : [validatePresenceOf, 'Team name is required'],
index : { unique : true }
}
});
module.exports = mongoose.model('Team', Team);
And here is what I am trying to do with Match.js
'use strict';
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TeamSchema = require('mongoose').model('Team');
var validatePresenceOf = function(value){
return value && value.length;
};
var toLower = function(string){
return string.toLowerCase();
};
var getId = function(){
return new Date().getTime();
};
/**
* The Match schema. Use timestamp as the unique key for each Match
*/
var Match = new Schema({
'key' : {
unique : true,
type : Number,
default: getId
},
'hometeam' : TeamSchema,
'awayteam' : TeamSchema
});
module.exports = mongoose.model('Match', Match);

Your solution: Use the actual schema, rather than a model that uses the schema:
module.exports = mongoose.model('Team', Team);
To
module.exports = {
model: mongoose.model('Team', Team),
schema: Team
};
and then var definition = require('path/to/js'); that, and use definition.schema instead of a model directly

You don't want to nest schemas.
Try Population in Mongoose: http://mongoosejs.com/docs/populate.html
That will solve your problem.

Try using Schema.Types.ObjectId in Match.js :
hometeam: { type: Schema.Types.ObjectId, ref: 'Team' }
awayteam: { type: Schema.Types.ObjectId, ref: 'Team' }

Related

Using Mongoose Virtual to calculate the sum of values from subdocuments

I am designing a character sheet app for a tabletop RPG.
I have a character schema and an item schema created using Mongoose.
The character schema has a property called equippedItems which is an array of item subdocuments that represent what the character currently has equipped.
I want to add a virtual to the character schema called 'currentLoad' that gets the 'load' value from each item in equippedItems and adds them together to get the total weight of the character's currently equipped items.
How would I go about using mongoose's .virtual() to accomplish this?
Character Schema:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const characterSchema = new Schema({
... //lots of other fields
equippedItems: [
{
type: Schema.Types.ObjectId,
ref: 'Item'
}
],
});
characterSchema.virtual('currentLoad')
//code to get load values from equippedItems, add together, then set the total to currentLoad
.get()
.set()
const Character = mongoose.model('Character', characterSchema);
module.exports = Character;
Item Schema:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const itemSchema = new Schema({
name: {
type: String,
required: true,
trim: true
},
load: {
type: Number,
required: true,
min: 0,
max: 3
},
});
const Item = mongoose.model('Item', itemSchema);
module.exports = Item;

Mongoose - Child Schema References Parent Subdocument

Is it possible to have a Mongoose Schema that resembles the following:
var categorySchema = new Schema({
name : String
});
var childSchema = new Schema({
name : String,
category : {
type : Schema.Types.ObjectId,
ref : 'parent.categories'
}
});
var parentSchema = new Schema({
categories : [categorySchema],
children : [childSchema]
});
Basically a child can only have a category that is contained by its parent. Is what I am trying to do possible? If not what is the cleanest way to do this?
If there is only one field name in categorySchema, maybe you could just put it into parentSchema without population as below,
var childSchema = new Schema({
name : String,
category : {
name: String
}
});
var parentSchema = new Schema({
categories : [{name: String}],
children : [childSchema]
});
When try to insert new child into parent, you could query the parent firstly, then iterate categories to get existing one and add it to children, save the parent as last, sample codes as below
Parent.find({_id: parent._id})
.exec(function(err, p) {
if (err) throw err;
var p = new Child({name: 'tt'});
p.categories.forEach(function(c) {
if (c /*find the match one*/) {
p.category = c; // assign the existing category to children
}
});
// save this parent
p.save(function(err) {...});
});
If there are many fields in categorySchema, maybe define it as individual schema could be one option, in case of there are many categories in Parent to make parent collection too large.
var categorySchema = new Schema({
name : String,
// other fields....
});
var Category = mongoose.model('Category', categorySchema);
var childSchema = new Schema({
name : String,
category : {type : Schema.Types.ObjectId, ref : 'Category'}
});
var parentSchema = new Schema({
categories : [{type : Schema.Types.ObjectId, ref : 'Category'}],
children : [childSchema]
});
The same logic when try to add new children to parent document as above.

Javascript TypeError: undefined is not a function when initializing an instance

I am getting a JS TypeError: undefined is not a function when trying to create an instance of a schema I define and import.
article.js
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Article Schema
var ArticleSchema = new Schema({
title : { type: String, required: true, index: { unique: true }},
author : { type: String, required: true },
day : { type: String, required: true },
month : { type: String, required: true },
year : { type: String, required: true },
link : { type: String, required: true },
password : { type: String, required: false, select: false }
});
module.exports = mongoose.model("Article", ArticleSchema);
api.js
var bodyParser = require("body-parser"); // get body-parser
var article = require("../models/article");
var config = require("../../config");
module.exports = function(app, express) {
var apiRouter = express.Router();
// Some stuff
var article = new article(); // create a new instance of the article model
// ...
The error is being thrown when the api tries to create the new Article, here is a screenshot of the full error:
The line 34:34 is when I try to initiate the new article.
I am aware this question gets asked all the time, and I'm sorry if the error is incredibly stupid but I went through 20 different "TypeError: undefined" questions, trying different things inside and I could not for the life of me fix it.
You're declaring a variable called "article". That's the same name you used for the module you imported, so your local variable will hide the more global one. Variables start off with no value, so they're undefined.
If you change the local variable name, then assuming your export is set up correctly you will be able to access the constructor.
Use a different name: If you do var article you overwrite the initial var article and thus it doesn't work.
Good practice is to use uppercase for the ModelNames:
var bodyParser = require("body-parser"); // get body-parser
var Article = require("../models/article");
var config = require("../../config");
module.exports = function(app, express) {
var apiRouter = express.Router();
// Some stuff
var article = new Article(); // create a new instance of the article model
// ...
Try it like this, should work now :)

How To Require A Model Within Another Model's Schema When It Hasn't Been Registered Yet

Inside the Schema of a Mongoose Model, I need to run a query for another model, but that second Model hasn't been registered yet, resulting in the following error MissingSchemaError: Schema hasn't been registered for model "Vote". How can I get this to work?
Here is how I bootstrap my models in server.js:
//Bootstrap models
var models_path = __dirname + '/app/models';
var walk = function(path) {
fs.readdirSync(path).forEach(function(file) {
var newPath = path + '/' + file;
var stat = fs.statSync(newPath);
if (stat.isFile()) {
if (/(.*)\.(js|coffee)/.test(file)) {
require(newPath);
}
} else if (stat.isDirectory()) {
walk(newPath);
}
});
};
walk(models_path);
The model starts is called Object, which I believe is being registered before Vote, which might be the problem:
// Object Schema
// Module dependencies.
var mongoose = require('mongoose'),
Vote = mongoose.model('Vote'),
Schema = mongoose.Schema;
var ObjectSchema = new Schema({
short_id: {
type: String,
required: true,
unique: true
},
created: {
type: Date,
default: Date.now
}
});
ObjectSchema.virtual('voted').get(function () {
// Here is where I'd like to run a query with the Vote model and then set the virtual attribute either true or false
});

Is it possible to reference two schemas to eachother in Mongoose?

I have two Schemas, and I want to be able to access both of them from the other one.. I am trying to do something like this:
//email.js
var mongoose = require('mongoose')
,Schema = mongoose.Schema
, FoodItemSchema = require('../models/fooditem.js')
, UserSchema = require('../models/user.js').schema
, User = require('../models/user.js').model
console.log(require('../models/user.js'));
var emailSchema = new Schema({
From : String,
Subject : FoodItemSchema,
Body : String,
Date: Date,
FoodItems : [FoodItemSchema],
Owner : { type : Schema.Types.ObjectId , ref: "User" }
});
module.exports = {
model: mongoose.model('Email', emailSchema),
schema : emailSchema
}
//user.js
var mongoose = require('mongoose')
,Schema = mongoose.Schema
, Email = require('../models/email.js').model
, EmailSchema = require('../models/email.js').schema
console.log(require('../models/email.js'));
var userSchema = new Schema({
googleID : String,
accessToken : String,
email : String,
openId: Number,
phoneNumber: String,
SentEmails : [EmailSchema]
// Logs : [{type: Schema.ObjectId, ref: 'events'}]
});
module.exports = {
model : mongoose.model('User', userSchema),
schema : userSchema
}
The first console.log() prints empty string and the second one prints as expected. I feel like I am trying to get the variables in the other schema even before they were created. Is there a common workaround for this? Or should I avoid double dependencies in my design?
Yes, you can create cross-references in Mongoose. But there is no way to create cyclic dependencies in Node.js. Though, you don't need to, because there is no need to require user schema in order to create a reference:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, FoodItemSchema = require('../models/fooditem.js');
var emailSchema = new Schema({
From: String,
Subject: FoodItemSchema,
Body: String,
Date: Date,
FoodItems: [FoodItemSchema],
Owner: { type: Schema.Types.ObjectId , ref: 'User' }
});
module.exports = {
model: mongoose.model('Email', emailSchema),
schema: emailSchema
}
You can define Schema Add statements to describe common attributes:
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
module.exports = exports = function productCodePlugin(schema, options) {
schema.add({productCode:{
productCode : {type : String},
description : {type : String},
allowed : {type : Boolean}
}});
};
then require the add statement into multiple schema definition files.
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, ObjectId = Schema.ObjectId
, productCodePlugin = require('./productCodePlugin');
var ProductCodeSchema = new Schema({
});
ProductCodeSchema.plugin(productCodePlugin);

Categories

Resources