I am trying to make a store app and have created two mongoose schemas. Order.js and Product.js, I want to reference the product schema as a property of the order schema.
Order.js
const mongoose = require('mongoose');
const Product=require('../models/product.js');
const orderSchema = new mongoose.Schema(
{
date: {
type:Date,
default: Date.now},
customerName: String,
customerAddress: String,
creditCard: Number,
products:[] //product model
},
{ timestamps: true }
);
product.js
const mongoose = require('mongoose');
const productSchema = new mongoose.Schema(
{
name: String,
price: Number,
category: String
},
{ timestamps: true }
);
const Product = mongoose.model('Product', productSchema);
module.exports= Product;
This is my approach when it comes to nested schemas in mongoose :
first you have to create an object of your Product schema , to avoid duplicating your Product model , you can create an object :
product
with your fields , then create your Product schema by simply const Product = new Schema(product); , then you can import the product object here in your order.js and declare your schema as follows :
import product;
const Order = new Schema({
......
......
order : {
type : product ,
......
}
})
Related
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;
I have 2 separate files, one encapsulates the Slot Schema, and another for the Location Schema. I'm trying to have a field in the Slot Schema that references the location Schema.
const mongoose = require('mongoose')
const locationSchema = require('./location')
const slotSchema = mongoose.Schema({
time: {
required: true,
type: String
},
typeOfSlot:{
required: true,
type: String
},
academic_mem_id:{
required: true,
default: null,
type: Number
},
course_id:{
required: true,
type: Number
},
location: [ locationSchema] // adjust
});
module.exports = mongoose.model('slots', slotSchema)
In a separate File:
const mongoose = require('mongoose')
const locationSchema = mongoose.Schema({
name:{
type:String,
required: true
},
capacity:{
type: Number,
required: true
},
type:{
type:String,
required:true
}
});
module.exports = mongoose.model('location', locationSchema)
I get this error when I run :
throw new TypeError('Invalid schema configuration: ' +
^
TypeError: Invalid schema configuration: `model` is not a valid type within the array `location`.
I'd really appreciate it if you'd help me figure out why the code above is wrong.
I want to export both the model and the Schema.
You are not exporting the locationSchema, but the location model. That is something entirely different and that is the reason you get the model is not a valid type within the array error.
Export only the schema and create/export the model in a separate file, e.g. locationModel.
const mongoose = require('mongoose')
const { Schema } = mongoose;
const locationSchema = new Schema({
name:{
type:String,
required: true
},
capacity:{
type: Number,
required: true
},
type:{
type:String,
required:true
}
});
module.exports = locationSchema;
Or if you want to keep both in the same file and export both:
module.exports = {
locationSchema,
locationModel,
};
And import them like so:
const { locationSchema, locationModel } = require('path/to/location.js');
That's wrong way referencing to other models.
First, you don't need require locationSchema, you can refer to that module in Schema. In your Slot Schema write this instead of your location field
location: {
type: mongoose.Schema.ObjectId,
ref: "location"
}
I want to get Products details when user order their item so I saved in Order schema where I passed ref of my product schema in productId
here is my Schema :
const Schema = mongoose.Schema
const OrderSchema=new Schema({
productId:[{_id:{type:Schema.Types.ObjectId,ref:'products'},quantity:{type:Number}}],
status:{type:Number,required:true},
deliverDate:{type:String},
totalAmount:{type:Number,default:0},
userId:{type:Schema.Types.ObjectId,ref:'userDetail'},
Address:{type:Object,required:true},
totalquantity:{type:Number,default:0},
name:{type:String},
method:{type:String,required:true},
txnId:{type:String},
pushToken:{type:String},
coordinates: {
type: [Number, Number],
index: '2d'
},
},{timestamps: true})
module.exports=mongoose.model('orders',OrderSchema)
I want to fetch an Order which populates productId with all products details:
Order.findById(id)
.then(order=>{
res.json({
orders:order
})
}).catch(err=>{console.log(err);next("Network error")})
How can I populate productId?
I have a subdocument that is nested as an array. Inside that subdocument I have references to other models. Using the .Find and .Populate methods I can receive the entire objects for single models referenced in the subdocument (check out Stop below) but not an array of model instances, Facts/Recommendations. For Facts/Recommendations I receive an array of object _ids. I can probably just take those _ids and make another query but this seems messy.
Is there a way to populate the array? Do I need to use Aggregate + $lookup? Is there a more Mongo way to restructure the Schema to simplify this?
Thank you for all and any help!
My subdocument called TourStop:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const TourStopSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref: 'Stop',
required:[true, 'must have a Stop'],
},
mandatory: {
type:Boolean,
default: false
},
check_in_time: {
type: Number,
default: 0
},
order: {
type:Number,
required: [true, 'Must have an order']
},
facts: [{
type: Schema.Types.ObjectId,
ref: 'Fact'
}],
recommendations: [{
type: Schema.Types.ObjectId,
ref: 'Recommendation'
}]
});
module.exports = TourStopSchema;
TourStops lives inside of Tour:
const mongoose = require('mongoose');
const TourStopSchema = require('../subdocs/tour_stop');
const Schema = mongoose.Schema;
const tourSchema = new Schema({
name: {
type:String,
required:[true,'Name is required!'],
unique: true
},
city: {
type: String,
required: [true, 'City is required!']
},
tourStops:[TourStopSchema]
});
const Tour = mongoose.model('Tour', tourSchema);
module.exports = Tour;
Stop Schema which is populated just fine.
const mongoose = require('mongoose');
const LocationSchema = require('../subdocs/location');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const stopSchema = new Schema({
name:{
type: String,
required:[true,'Must have a name!']
},
location:LocationSchema,
categories: {
type: [String],
default:[]
},
images:{
type:[ImageSchema],
default:[]
}
});
const Stop = mongoose.model('Stop', stopSchema);
module.exports = Stop;
And Fact Schema which is not populated correctly, instead returns an array of strings with the _ids
Fact:
const mongoose = require('mongoose');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const factSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref:'Stop',
required:[true, 'A Fact must have a Stop!'],
},
description: {
type: String,
required: [true,'A Fact must have a description!']
},
images: {
type:[ImageSchema],
default:[]
}
});
const Fact = mongoose.model('Fact', factSchema);
module.exports = Fact;
And I'm running a test to check that the schema is properly hooked up to retrieve all the attributes of a TourStop:
it('saves a full relation graph', (done) => {
User.findOne({ first_name: 'Dean' })
.populate({
// in that user find the tours property and load up all tours
path: 'tours',
// inside of all those tours, find the tourstops property and load all associated stops
populate: {
path: 'tour_stops.facts',
model: 'Fact'
},
populate: {
path: 'tour_stops.stop',
model: 'Stop'
}
})
// .populate('tours.tour_stops[0].facts')
.then((user) => {
// console.log(user.tours[0].tour_stops[0].stop);
console.log(user.tours[0].tour_stops[0]);
// console.log(Array.isArray(user.tours[0].tour_stops[0].facts))
assert(user.first_name === 'Dean' );
assert(user.tours.length === 1);
assert(user.tours[0].name === "Awesome New Tour");
assert(user.tours[0].tour_stops[0].stop.name === 'Jaffa Clock Tower');
// assert(user.tours[0])
// assert(user.blogPosts[0].title === 'JS is Great');
// assert(user.blogPosts[0].comments[0].content === 'Congrats on great post!' );
// assert(user.blogPosts[0].comments[0].user.name === 'Joe' )
done();
})
})
You can use the following code to populate tours, stop, facts and recommendations.
Note in model property, we should not give string value, but the model itself. So you need to import them to your code.
User.findOne({ first_name: "Dean" })
.populate({
path: "tours",
populate: {
path: "tourStops.stop",
model: Stop
}
})
.populate({
path: "tours",
populate: {
path: "tourStops.facts",
model: Fact
}
})
.populate({
path: "tours",
populate: {
path: "tourStops.recommendations",
model: Recommendation
}
})
.then(user => {
console.log(user);
});
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);