Search code examples
mongodbmongoose

seeding mongoose data when both schemas ref each other


I'm trying to seed data for my db but both my schema reference each other so I'm not sure how it's possible to seed one before the other.

I want to use fakerjs to have some data to play with, but I'm a bit confused how I'd approach it.

Here are my Schemas, they're super simple and both ref each other:

import mongoose from "mongoose";
import { loadType } from "mongoose-currency";

const Schema = mongoose.Schema;
loadType(mongoose);

const CategorySchema = new Schema(
  {
    name: String,
    expenses: [
      {
        type: mongoose.Schema.Types.ObjectId,
        ref: "Expense",
      },
    ],
  },
  { timestamps: true, toJSON: { getters: true } }
);

const Category = mongoose.model("Category", CategorySchema);

export default Category;

And the other:

import mongoose from "mongoose";
import { loadType } from "mongoose-currency";

const Schema = mongoose.Schema;
loadType(mongoose);

const ExpenseSchema = new Schema(
  {
    name: String,
    price: {
      type: mongoose.Types.Currency,
      currency: "USD",
      get: (v) => v / 100,
    },
    date: {
      type: Date,
    },
    category: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Category",
    },
  },
  { timestamps: true, toJSON: { getters: true } }
);

const Expense = mongoose.model("Expense", ExpenseSchema);

export default Expense;

Does anyone know how I'd go about seeding them if they both rely on each other?


Solution

  • Refs to children documentation explains this situation.

    E.g. ("mongoose": "^7.3.1")

    // @ts-nocheck
    import mongoose from 'mongoose';
    import util from 'util';
    import { config } from '../../config';
    
    const CategorySchema = new mongoose.Schema({
        name: String,
        expenses: [
            {
                type: mongoose.Schema.Types.ObjectId,
                ref: 'Expense',
            },
        ],
    });
    const Category = mongoose.model('Category', CategorySchema);
    
    const ExpenseSchema = new mongoose.Schema({
        name: String,
        category: {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'Category',
        },
    });
    const Expense = mongoose.model('Expense', ExpenseSchema);
    
    (async function main() {
        mongoose.set('debug', true);
        try {
            await mongoose.connect(config.MONGODB_URI);
            await Promise.all([Category, Expense].map((m) => m.collection.drop()));
            // seed
            const [c1, c2] = [{ name: 'c1' }, { name: 'c2' }].map((v) => new Category(v));
            const [e1, e2, e3] = [
                { name: 'e1', category: c1._id },
                { name: 'e2', category: c1._id },
                { name: 'e3', category: c2._id },
            ].map((v) => new Expense(v));
    
            c1.expenses.push(e1, e2);
            c2.expenses.push(e3);
    
            await Promise.all([c1, c2].map((v) => v.save()));
            await Promise.all([e1, e2, e3].map((v) => v.save()));
    
            // populate
            const c = await Category.findOne({ name: 'c1' }).populate('expenses').exec();
            console.log(c?.toObject());
            const e = await Expense.findOne({ name: 'e1' })
                .populate({ path: 'category', populate: { path: 'expenses' } })
                .exec();
            console.log(util.inspect(e?.toObject(), false, null));
        } catch (error) {
            console.error(error);
        } finally {
            await mongoose.connection.close();
        }
    })();
    

    Debug logs:

    Mongoose: categories.drop()
    Mongoose: expenses.drop()
    Mongoose: categories.insertOne({ name: 'c1', expenses: [ ObjectId("649d8f7d038538fabd2e1ce3"), ObjectId("649d8f7d038538fabd2e1ce4") ], _id: ObjectId("649d8f7d038538fabd2e1ce1"), __v: 0}, {})
    Mongoose: categories.insertOne({ name: 'c2', expenses: [ ObjectId("649d8f7d038538fabd2e1ce5") ], _id: ObjectId("649d8f7d038538fabd2e1ce2"), __v: 0}, {})
    Mongoose: expenses.insertOne({ name: 'e1', category: ObjectId("649d8f7d038538fabd2e1ce1"), _id: ObjectId("649d8f7d038538fabd2e1ce3"), __v: 0}, {})
    Mongoose: expenses.insertOne({ name: 'e2', category: ObjectId("649d8f7d038538fabd2e1ce1"), _id: ObjectId("649d8f7d038538fabd2e1ce4"), __v: 0}, {})
    Mongoose: expenses.insertOne({ name: 'e3', category: ObjectId("649d8f7d038538fabd2e1ce2"), _id: ObjectId("649d8f7d038538fabd2e1ce5"), __v: 0}, {})
    Mongoose: categories.findOne({ name: 'c1' }, {})
    Mongoose: expenses.find({ _id: { '$in': [ ObjectId("649d8f7d038538fabd2e1ce3"), ObjectId("649d8f7d038538fabd2e1ce4") ], [Symbol(mongoose#trustedSymbol)]: true }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined })
    {
      _id: new ObjectId("649d8f7d038538fabd2e1ce1"),
      name: 'c1',
      expenses: [
        {
          _id: new ObjectId("649d8f7d038538fabd2e1ce3"),
          name: 'e1',
          category: new ObjectId("649d8f7d038538fabd2e1ce1"),
          __v: 0
        },
        {
          _id: new ObjectId("649d8f7d038538fabd2e1ce4"),
          name: 'e2',
          category: new ObjectId("649d8f7d038538fabd2e1ce1"),
          __v: 0
        }
      ],
      __v: 0
    }
    Mongoose: expenses.findOne({ name: 'e1' }, {})
    Mongoose: categories.find({ _id: { '$in': [ ObjectId("649d8f7d038538fabd2e1ce1") ], [Symbol(mongoose#trustedSymbol)]: true }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined })
    Mongoose: expenses.find({ _id: { '$in': [ ObjectId("649d8f7d038538fabd2e1ce3"), ObjectId("649d8f7d038538fabd2e1ce4") ], [Symbol(mongoose#trustedSymbol)]: true }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined })
    {
      _id: new ObjectId("649d8f7d038538fabd2e1ce3"),
      name: 'e1',
      category: {
        _id: new ObjectId("649d8f7d038538fabd2e1ce1"),
        name: 'c1',
        expenses: [
          {
            _id: new ObjectId("649d8f7d038538fabd2e1ce3"),
            name: 'e1',
            category: new ObjectId("649d8f7d038538fabd2e1ce1"),
            __v: 0
          },
          {
            _id: new ObjectId("649d8f7d038538fabd2e1ce4"),
            name: 'e2',
            category: new ObjectId("649d8f7d038538fabd2e1ce1"),
            __v: 0
          }
        ],
        __v: 0
      },
      __v: 0
    }