英文:
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!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论