I have been working with MongoDB for just over a month, and I do not have much experience with Relational Databases as well. I would try to explain what I am trying to do below.
I have a schema in mongoose like this:
import mongoose, { model, Schema } from "mongoose";
import { IBudget } from "../types/budget";
import User from "./user.model";
const budgetSchema = new Schema<IBudget>({
user: { type: mongoose.Schema.Types.ObjectId, required: true, ref: User },
categories: {
type: [
{
title: { type: "string", required: true, trim: true },
amount: { type: "number", required: true, default: 0 },
color: { type: "string", required: true },
managed: { type: "boolean", required: true, default: true },
editable: { type: "boolean", required: true, default: true },
description: { type: "string", maxlength: 120 },
},
],
required: true,
maxlength: [12, "Cannot have more than 12 categories"],
minlength: [1, "Need at least 1 cateogry"],
},
month: {
type: Number,
required: [true, "Please add month"],
min: 1,
max: 12,
},
year: { type: Number, required: [true, "Please add the year"] },
});
const Budget = model("Budget", budgetSchema);
export default Budget;
With this, when I create budget document using Budget.create({...budgetObject})
, I get the following document created in the database.
I have another schema called Expense like this:
import mongoose, { model, Schema } from "mongoose";
import { IExpense } from "../types/expense";
import User from "./user.model";
const expenseSchema = new Schema<IExpense>(
{
title: { type: String, required: true, trim: true, maxlength: 40 },
description: { type: String, trim: true, maxlength: 260 },
expenseDate: { type: Date, default: Date.now },
category: {
type: String,
required: [true, "Please add the category name"],
},
user: { type: mongoose.Schema.Types.ObjectId, required: true, ref: User },
amount: { type: Number, required: true },
reverted: { type: Boolean, required: true, default: false },
},
{ timestamps: false }
);
const Expense = model("Expense", expenseSchema);
export default Expense;
My Question is: How do I refer the category of the expense document to one of the items in the categories array within the Budget document?
I know how to refer to other documents by using { type: mongoose.Schema.Types.ObjectId, required: true, ref: User },
as you have seen. But I don't understand how that would work for a subdocument or a document inside of an array field nested in another document.
I am not even sure whether this is possible, in which case, an alternative approach for the same behavior would be highly appreciated.
CodePudding user response:
It looks like you've learnt a lot in a month :-).
For your use case, instead of hardcoding the category
field in the budgetSchema
, you should first create a categorySchema
like:
import { model, Schema } from "mongoose";
import { ICategory } from "../types/category";
const categorySchema =
new Schema() <
ICategory >
{
title: { type: "string", required: true, trim: true },
amount: { type: "number", required: true, default: 0 },
color: { type: "string", required: true },
managed: { type: "boolean", required: true, default: true },
editable: { type: "boolean", required: true, default: true },
description: { type: "string", maxlength: 120 },
};
const Category = model("Category", categorySchema);
export default Category;
Then in your budgetSchema, your categories field will be an array of categorySchema
object ids as such:
import mongoose, { model, Schema } from "mongoose";
import { IBudget } from "../types/budget";
import User from "./user.model";
import Category from "./category.model";
const budgetSchema =
new Schema() <
IBudget >
{
user: { type: mongoose.Schema.Types.ObjectId, required: true, ref: User },
categories: [
{ type: mongoose.Schema.Types.ObjectId, required: true, ref: Category },
],
month: {
type: Number,
required: [true, "Please add month"],
min: 1,
max: 12,
},
year: { type: Number, required: [true, "Please add the year"] },
};
const Budget = model("Budget", budgetSchema);
export default Budget;
Since the categories are now documents of a separate schema, you'll now be able to reference them in your expenseSchema like this:
import mongoose, { model, Schema } from "mongoose";
import { IExpense } from "../types/expense";
import User from "./user.model";
import Category from "./category.model";
const expenseSchema =
new Schema() <
IExpense >
({
title: { type: String, required: true, trim: true, maxlength: 40 },
description: { type: String, trim: true, maxlength: 260 },
expenseDate: { type: Date, default: Date.now },
category: {
type: mongoose.Schema.Types.ObjectId,
required: [true, "Please add the category name"],
ref: Category,
},
user: { type: mongoose.Schema.Types.ObjectId, required: true, ref: User },
amount: { type: Number, required: true },
reverted: { type: Boolean, required: true, default: false },
},
{ timestamps: false });
const Expense = model("Expense", expenseSchema);
export default Expense;