I'm building a Mongoose schema for a dating app.
I want each person document to contain a reference to all the events they've been to, where events is another schema with its own models in the system. How can I describe this in the schema?
var personSchema = mongoose.Schema({
firstname: String,
lastname: String,
email: String,
gender: {type: String, enum: ["Male", "Female"]}
dob: Date,
city: String,
interests: [interestsSchema],
eventsAttended: ???
});
You can do so by using Population
Population is the process of automatically replacing the specified
paths in the document with document(s) from other collection(s). We
may populate a single document, multiple documents, plain object,
multiple plain objects, or all objects returned from a query.
Suppose your Event Schema is defined as follows:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var eventSchema = Schema({
title : String,
location : String,
startDate : Date,
endDate : Date
});
var personSchema = Schema({
firstname: String,
lastname: String,
email: String,
gender: {type: String, enum: ["Male", "Female"]}
dob: Date,
city: String,
interests: [interestsSchema],
eventsAttended: [{ type: Schema.Types.ObjectId, ref: 'Event' }]
});
var Event = mongoose.model('Event', eventSchema);
var Person = mongoose.model('Person', personSchema);
To show how populate is used, first create a person object,
aaron = new Person({firstname: 'Aaron'})
and an event object,
event1 = new Event({title: 'Hackathon', location: 'foo'}):
aaron.eventsAttended.push(event1);
aaron.save(callback);
Then, when you make your query, you can populate references like this:
Person
.findOne({ firstname: 'Aaron' })
.populate('eventsAttended') // only works if we pushed refs to person.eventsAttended
.exec(function(err, person) {
if (err) return handleError(err);
console.log(person);
});
To reference the ObjectId of one table in another table refer below code
const mongoose = require('mongoose'),
Schema=mongoose.Schema;
const otpSchema = new mongoose.Schema({
otpNumber:{
type: String,
required: true,
minlength: 6,
maxlength: 6
},
user:{
type: Schema.Types.ObjectId,
ref: 'User'
}
});
const Otp = mongoose.model('Otp',otpSchema);
// Joi Schema For Otp
function validateOtp(otp) {
const schema = Joi.object({
otpNumber: Joi.string().max(6).required(),
userId: Joi.objectId(), // to validate objectId we used 'joi-objectid' npm package
motive: Joi.string().required(),
isUsed: Joi.boolean().required(),
expiresAt: Joi.Date().required()
});
// async validate function for otp
return schema.validateAsync(otp);
}
exports.Otp = Otp;
exports.validateOtp = validateOtp;
List item
var personSchema = mongoose.Schema({
firstname: String,
lastname: String,
email: String,
gender: {
type: String,
enum: ["Male", "Female"]
}
dob: Date,
city: String,
interests: [interestsSchema],
eventsAttended[{
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Place"
}],
**//ref:"Places"...you have put the other model name**
*OR*
eventsAttended[{
type: mongoose.Types.ObjectId,
required: true,
ref: "Place"
}],
});
Related
I am having following two schemas in mongodb
var userSchema = new Schema({
first_name: String,
last_name: String,
email: String
});
var messageSchema = new Schema({
sender: {type: Schema.Types.ObjectId, ref: 'users' },
receiver: {type: Schema.Types.ObjectId, ref: 'users' },
message: String,
sentOn: Date,
});
var Users = mongoose.model('users', userSchema);
var Messages = mongoose.model('messages', messageSchema);
How can I query to get the all users from 'users' collection and their last message (single) from 'messages' collection?
So I am currently extremely confused when it comes to mongoose populating.
My data structure is as follows: User has an array of Clients, a Client has an array of loans. I am using User.find and populating the client, no problem. But then how do I populate the loans that is inside of the client? I have tried this:
Basically, the goal is to pass in an array of Clients, each client will contain a loan. But for now, I want that loan object to be empty. The Client is being populated correctly, however, the Loan reference is being passed as undefined.
app.get("/loans", IsLoggedIn, function(req, res) {
User.findById(req.user._id).populate({path: "clients", populate: { path: "loans", model: "loan"}}).exec(function(err, user){
if(err){
console.log(err);
} else{
var amountRepaid = calcRepaid(user.clients.loans);
console.log(user.clients.loans);
res.render("index", {clients: user.clients, amountRepaid: amountRepaid});
}
});
However it doesn't seem to work, my models are listed below and any help is appreciated!
models:
Client:
var mongoose = require("mongoose");
var clientSchema = mongoose.Schema({
loans: [{
type: mongoose.Schema.Types.ObjectId,
ref: "loan"
}],
emailAdderess: String,
firstname: String,
lastname: String,
contactNumber: String ,
dateCreated: {type: Date, default: Date.now},
gender: String,
})
module.exports = mongoose.model("Client", clientSchema);
User:
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = mongoose.Schema({
username: String,
password: String,
firstname: String,
lastname: String,
clients: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Client"
}]
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Loan:
var mongoose = require("mongoose");
var LoanSchema = mongoose.Schema({
firstName: String,
lastName: String,
email: String,
contactNumber: Number,
amountBorrowed: Number,
length: String,
address: String,
dateDue: Date,
gender: String,
date: { type: Date, default: Date.now },
amountRepaid: { type: Number, default: 0},
loanRepaid: {type: Boolean, default: false}
})
module.exports = mongoose.model("loan", LoanSchema);
Try this:
.populate({
path: 'clients',
populate: {
path: 'clients.loans'
}
})
I have a Mongoose schema Employee. In that I want to store a field (phone number) related to office for the employee, only if he/she is eligible for office, which is only for two levels "senior" and "c-level".
The schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
var EmployeeSchema = new Schema({
name: String,
designation: String,
level: {
type: String,
enum: ["intern", "junior", "mid-level", "senior", "c-level"],
required: true,
},
phoneNo: { type: String, required: true },
officePhoneNo: { type: String, required: true } // How to require only if the level is senior or c-level?,
});
Appreciate your help.
Thanks
In Mongoose you can pass a function in required that can return true/false depending on some condition.
It's also possible to depend required of a field on other fields, which is level in your case. That is, you can optionally required a field. Here's how:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const levels = ["intern", "junior", "mid-level", "senior", "c-level"];
const levelsEligibleForOffice = ["senior", "c-level"];
var EmployeeSchema = new Schema({
name: String,
designation: String,
level: {type: String, enum: levels, required: true},
phoneNo: {type: String, required: true},
officePhoneNo: {type: String, required: isEligibleForOffice}
});
function isEligibleForOffice(){
if(levelsEligibleForOffice.indexOf(this.level) > -1){ //"this" contains the employee document at the time of required validation
return true;
}
return false;
}
I have users.js schema with a embeded document array pets. For each user, a user can have multiple pets(usually no more than 3 I would think).
For each pet, there would be a daily chart. So it would be many daily charts for a pet. I have read with embedded documents that each array element is indexed. In daily.js, how can I reference the pet it would belong to for the populate() function?
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
username: { type: String, required: true, unique: true },
location: String,
pets: [{ name: 'string', animalType: 'string'}], //could have more than one pet
created_at: Date,
updated_at: Date
});
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var dailySchema = new Schema({
tite: String,
_pet: { type: Number, ref: 'User.pet' }, // not sure how to reference name in user.pets[#] array
created_at: Date,
updated_at: Date
});
Quoting
Sorry to disappoint but that is an anti-pattern. Populate can't populate from another collection's subdocs - the reason why you're getting that error is that there's no model for boards.
So it may be not good patten to reference to embedded document. It could be better to separate pet from User as one schema
var PetSchema = new Schema ({
name: 'string',
animalType: 'string'
});
And the UserSchema and DailySchema will be
var userSchema = new Schema({
...
pets: [{ type: Schema.Types.ObjectId, ref: 'Pet' }], //could have more than one pet
});
var dailySchema = new Schema({
_pet: { type: Number, ref: 'Pet' }, // not sure how to reference name in user.pets[#] array
});
I'm trying to reference one model to other with Mongoose doing this:
//product model
var productSchema = new mongoose.Schema({
id: {type: Number, unique: true, required: 'product_id'},
sku: {type: String, unique: true, required: 'product_sku'},
name: {type: String, required: 'product_name'},
short_description: {type: String},
details: [{
type: String
}],
categories: [{
type: String
}, {min: 1, max: 5}],
});
module.exports = mongoose.model('products', productSchema);
//order model
var orderSchema = new mongoose.Schema({
id: {type: String, unique: true},
date: {type: Date, default: Date.now},
products_sold: {type: mongoose.Schema.Types.ObjectId, ref: 'products'},
});
module.exports = mongoose.model('orders', orderSchema);
I've already created a product with id: 1:
"_id": ObjectId('55a1ce54b058d142051ca61d'),
"id": 1,
"sku": "p0001",
"name": "test1",
"short_description": "test",
//etc
If I do:
var newOrder = new Order({
id: 'order001',
products_sold: 1,
//etc
});
newOrder.save(function(err, data) {
if(err){
console.log(err);
}else{
console.log(data);
}
});
It fails with error:
message: 'orders validation failed',
name: 'ValidationError',
errors:
{ products_sold:
{ [CastError: Cast to ObjectID failed for value "1" at path "products_sold"]
How can I have products_sold in my order filled with product data that has id 1?
Thanks in advance
You are specifying that the products_sold field is of type ObjectId, but you are trying to set a Number as its value. In order to store references to documents in other collections, you must store the _id of the referenced document, not an arbitrary id field that you have in the Schema. You will either need to change the type for _id in your products database to use integers (and then manually specify what that _id field's value is when creating a new product), or you need to store the product's _id value in the order document's product_sold field.
Response to comment:
Here is updated code for specifying your own unique _id field:
Product Model
var productSchema = new mongoose.Schema({
_id: {type: Number, unique: true, required: 'product_id'},
sku: {type: String, unique: true, required: 'product_sku'},
name: {type: String, required: 'product_name'},
short_description: {type: String},
details: [String],
categories: [{
type: String
min: 1,
max: 5
}],
}, {_id: false});
module.exports = mongoose.model('products', productSchema);
In the productSchema, you will see that there is an _id specified, and it is set to required, and has no default value. This should make it so that Mongo will use whatever Number you specify as it's _id.
You need to pass objectId of the product in product_sold , rather than number.