data-seeding
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseData Seeding Skill
数据填充技能
Seed scripts live in .
packages/database/src/seed/种子脚本存放于目录下。
packages/database/src/seed/Running Seeds
运行种子脚本
bash
pnpm -F @sgcarstrends/database db:seed # Run all seeds
pnpm -F @sgcarstrends/database db:seed:cars # Seed specific tablebash
pnpm -F @sgcarstrends/database db:seed # 运行所有种子脚本
pnpm -F @sgcarstrends/database db:seed:cars # 为指定表填充数据Basic Seed Pattern
基础种子脚本模式
typescript
// packages/database/src/seed/cars.ts
import { db } from "../index";
import { cars } from "../db/schema";
import { nanoid } from "nanoid";
export async function seedCars() {
console.log("Seeding cars...");
const carData = [
{ id: nanoid(), make: "Toyota", model: "Camry", vehicleClass: "Sedan", fuelType: "Petrol", month: "2024-01", number: 150 },
{ id: nanoid(), make: "Honda", model: "Civic", vehicleClass: "Sedan", fuelType: "Petrol", month: "2024-01", number: 120 },
];
await db.insert(cars).values(carData);
console.log(`Seeded ${carData.length} cars`);
}typescript
// packages/database/src/seed/cars.ts
import { db } from "../index";
import { cars } from "../db/schema";
import { nanoid } from "nanoid";
export async function seedCars() {
console.log("Seeding cars...");
const carData = [
{ id: nanoid(), make: "Toyota", model: "Camry", vehicleClass: "Sedan", fuelType: "Petrol", month: "2024-01", number: 150 },
{ id: nanoid(), make: "Honda", model: "Civic", vehicleClass: "Sedan", fuelType: "Petrol", month: "2024-01", number: 120 },
];
await db.insert(cars).values(carData);
console.log(`Seeded ${carData.length} cars`);
}Main Seed Runner
主种子执行器
typescript
// packages/database/src/seed/index.ts
export async function seed() {
console.log("Starting database seed...");
await clearDatabase(); // Optional: clear existing data
await seedCars();
await seedCOE();
await seedPosts();
console.log("Database seeded successfully!");
}
async function clearDatabase() {
// Delete in reverse order of dependencies
await db.delete(posts);
await db.delete(coe);
await db.delete(cars);
}typescript
// packages/database/src/seed/index.ts
export async function seed() {
console.log("Starting database seed...");
await clearDatabase(); // 可选:清空现有数据
await seedCars();
await seedCOE();
await seedPosts();
console.log("Database seeded successfully!");
}
async function clearDatabase() {
// 按依赖逆序删除数据
await db.delete(posts);
await db.delete(coe);
await db.delete(cars);
}Seed with Faker.js
使用Faker.js生成填充数据
bash
pnpm -F @sgcarstrends/database add -D @faker-js/fakertypescript
import { faker } from "@faker-js/faker";
export async function seedRealisticCars(count = 100) {
const makes = ["Toyota", "Honda", "BMW", "Mercedes"];
const carData = Array.from({ length: count }, () => ({
id: nanoid(),
make: faker.helpers.arrayElement(makes),
model: faker.vehicle.model(),
month: faker.date.between({ from: "2020-01-01", to: "2024-12-31" }).toISOString().slice(0, 7),
number: faker.number.int({ min: 10, max: 500 }),
}));
// Batch insert for performance
const batchSize = 50;
for (let i = 0; i < carData.length; i += batchSize) {
await db.insert(cars).values(carData.slice(i, i + batchSize));
}
}bash
pnpm -F @sgcarstrends/database add -D @faker-js/fakertypescript
import { faker } from "@faker-js/faker";
export async function seedRealisticCars(count = 100) {
const makes = ["Toyota", "Honda", "BMW", "Mercedes"];
const carData = Array.from({ length: count }, () => ({
id: nanoid(),
make: faker.helpers.arrayElement(makes),
model: faker.vehicle.model(),
month: faker.date.between({ from: "2020-01-01", to: "2024-12-31" }).toISOString().slice(0, 7),
number: faker.number.int({ min: 10, max: 500 }),
}));
// 批量插入提升性能
const batchSize = 50;
for (let i = 0; i < carData.length; i += batchSize) {
await db.insert(cars).values(carData.slice(i, i + batchSize));
}
}Environment-Specific Seeds
环境专属填充脚本
typescript
export async function seed() {
const env = process.env.NODE_ENV || "development";
switch (env) {
case "development":
await seedDevelopment(); // Small, predictable dataset
break;
case "test":
await seedTesting(); // Minimal, deterministic data
break;
case "staging":
await seedStaging(); // Larger, production-like dataset
break;
}
}typescript
export async function seed() {
const env = process.env.NODE_ENV || "development";
switch (env) {
case "development":
await seedDevelopment(); // 小型可预测数据集
break;
case "test":
await seedTesting(); // 最小化确定性数据
break;
case "staging":
await seedStaging(); // 接近生产环境的大型数据集
break;
}
}Idempotent Seeds (Upsert)
幂等填充(Upsert操作)
typescript
await db.insert(cars).values(carData).onConflictDoUpdate({
target: cars.id,
set: { make: carData[0].make, number: carData[0].number, updatedAt: new Date() },
});typescript
await db.insert(cars).values(carData).onConflictDoUpdate({
target: cars.id,
set: { make: carData[0].make, number: carData[0].number, updatedAt: new Date() },
});Check Before Insert
插入前检查
typescript
export async function seedIfEmpty() {
const existing = await db.select().from(cars).limit(1);
if (existing.length > 0) {
console.log("Database has data, skipping seed");
return;
}
await seedCars();
}typescript
export async function seedIfEmpty() {
const existing = await db.select().from(cars).limit(1);
if (existing.length > 0) {
console.log("数据库已有数据,跳过填充");
return;
}
await seedCars();
}Transactions
事务处理
typescript
await db.transaction(async (tx) => {
await tx.insert(cars).values([...carData]);
await tx.insert(coe).values([...coeData]);
});typescript
await db.transaction(async (tx) => {
await tx.insert(cars).values([...carData]);
await tx.insert(coe).values([...coeData]);
});CLI for Selective Seeding
选择性填充的CLI工具
typescript
// packages/database/scripts/seed-cli.ts
const seeders = { cars: seedCars, coe: seedCOE, posts: seedPosts, all: seedAll };
const target = process.argv[2] as keyof typeof seeders;
if (!target || !seeders[target]) {
console.error("Usage: pnpm db:seed [cars|coe|posts|all]");
process.exit(1);
}
seeders[target]().then(() => process.exit(0)).catch(() => process.exit(1));typescript
// packages/database/scripts/seed-cli.ts
const seeders = { cars: seedCars, coe: seedCOE, posts: seedPosts, all: seedAll };
const target = process.argv[2] as keyof typeof seeders;
if (!target || !seeders[target]) {
console.error("Usage: pnpm db:seed [cars|coe|posts|all]");
process.exit(1);
}
seeders[target]().then(() => process.exit(0)).catch(() => process.exit(1));Best Practices
最佳实践
- Idempotent: Safe to run multiple times (use upserts or check-before-insert)
- Environment-Specific: Different data for dev/test/staging
- Batch Inserts: Use batching for large datasets
- Relationships: Seed parent tables first
- Transactions: Use for atomic seeding
- Logging: Provide clear progress feedback
- 幂等性:可安全多次运行(使用Upsert或插入前检查)
- 环境专属:为开发/测试/预发布环境提供不同数据
- 批量插入:处理大型数据集时使用批量操作
- 关联表顺序:先填充父表数据
- 事务处理:使用事务保证填充操作的原子性
- 日志反馈:提供清晰的进度提示
References
参考资料
- for schema details
packages/database/CLAUDE.md - See skill for migrations
schema-design
- 包含数据库架构细节
packages/database/CLAUDE.md - 查看技能了解迁移相关内容
schema-design