当两个模式相互引用时,种子化Mongoose数据

huangapple go评论65阅读模式
英文:

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!

huangapple
  • 本文由 发表于 2023年6月29日 04:16:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576464.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定