英文:
seeding mongoose data when both schemas ref each other
问题
我正在尝试为我的数据库填充数据,但我的模式相互引用,所以我不确定如何在其中一个之前填充另一个。
我想使用fakerjs来生成一些数据来操作,但我有点困惑如何着手。
这是我的模式,它们都非常简单并且相互引用:
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;
和另一个:
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;
如果它们彼此依赖,有人知道我该如何填充它们吗?
英文:
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?
答案1
得分: 2
Refs to children文档解释了这种情况。
例如("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();
    }
})();
调试日志:
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' }, {})
{
  _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
}
英文:
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
}
答案2
得分: 0
这就是我根据上面的答案得出的结果:
(async function main() {
await Promise.all([Category, Expense].map((m) => m.collection.drop()));
const newCategories = categories.map((v) => new Category(v));
const _ids = newCategories.map((item) => item._id);
const expenses = Array.from({ length: 100 }, () => ({
description: faker.lorem.sentence({ min: 3, max: 5 }),
price: faker.finance.amount({ min: 5, max: 50, dec: 2, symbol: "$" }),
date: faker.date.past({ years: 1 }),
category: faker.helpers.arrayElement(_ids),
})).map((v) => new Expense(v));
newCategories.forEach((category) =>
expenses.map((expense) => {
if (expense.category === category._id) {
category.expenses.push(expense._id);
}
})
);
await Promise.all(expenses.map((expense) => expense.save()));
await Promise.all(newCategories.map((category) => category.save()));
})();
不确定是否最高效,如果有更好的方法,请告诉我!
英文:
This is what I landed at thanks to the answer above:
(async function main() {
await Promise.all([Category, Expense].map((m) => m.collection.drop()));
const newCategories = categories.map((v) => new Category(v));
const _ids = newCategories.map((item) => item._id);
const expenses = Array.from({ length: 100 }, () => ({
description: faker.lorem.sentence({ min: 3, max: 5 }),
price: faker.finance.amount({ min: 5, max: 50, dec: 2, symbol: "$" }),
date: faker.date.past({ years: 1 }),
category: faker.helpers.arrayElement(_ids),
})).map((v) => new Expense(v));
newCategories.forEach((category) =>
expenses.map((expense) => {
if (expense.category === category._id) {
category.expenses.push(expense._id);
}
})
);
await Promise.all(expenses.map((expense) => expense.save()));
await Promise.all(newCategories.map((category) => category.save()));
})();
Not sure if this is most efficient, if anyone knows a better way, please lmk!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论