npx skills add https://github.com/lobehub/lobehub --skill drizzledrizzle.config.tssrc/database/schemas/src/database/migrations/postgresql,并启用 strict: true位置:src/database/schemas/_helpers.ts
timestamptz(name):带时区的时间戳createdAt(), , :标准时间戳列广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
updatedAt()accessedAt()timestamps:包含所有三个时间戳的对象,便于展开users, session_groups)user_id, created_at)id: text('id')
.primaryKey()
.$defaultFn(() => idGenerator('agents'))
.notNull(),
ID 前缀使实体类型可区分。对于内部表,使用 uuid。
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
...timestamps, // 从 _helpers.ts 展开
// 返回数组(对象风格已弃用)
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
export const insertAgentSchema = createInsertSchema(agents);
export type NewAgent = typeof agents.$inferInsert;
export type AgentItem = typeof agents.$inferSelect;
export const agents = pgTable(
'agents',
{
id: text('id')
.primaryKey()
.$defaultFn(() => idGenerator('agents'))
.notNull(),
slug: varchar('slug', { length: 100 })
.$defaultFn(() => randomSlug(4))
.unique(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
clientId: text('client_id'),
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
...timestamps,
},
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
);
export const agentsKnowledgeBases = pgTable(
'agents_knowledge_bases',
{
agentId: text('agent_id')
.references(() => agents.id, { onDelete: 'cascade' })
.notNull(),
knowledgeBaseId: text('knowledge_base_id')
.references(() => knowledgeBases.id, { onDelete: 'cascade' })
.notNull(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
enabled: boolean('enabled').default(true),
...timestamps,
},
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
);
始终使用 db.select() 构建器 API。切勿使用 db.query.* 关系型 API(findMany, findFirst, with:)。
关系型 API 会生成使用 json_build_array 的复杂横向连接,这种连接方式脆弱且难以调试。
// ✅ 推荐
const [result] = await this.db
.select()
.from(agents)
.where(eq(agents.id, id))
.limit(1);
return result;
// ❌ 不推荐:使用关系型 API
return this.db.query.agents.findFirst({
where: eq(agents.id, id),
});
// ✅ 推荐:显式 select + leftJoin
const rows = await this.db
.select({
runId: agentEvalRunTopics.runId,
score: agentEvalRunTopics.score,
testCase: agentEvalTestCases,
topic: topics,
})
.from(agentEvalRunTopics)
.leftJoin(agentEvalTestCases, eq(agentEvalRunTopics.testCaseId, agentEvalTestCases.id))
.leftJoin(topics, eq(agentEvalRunTopics.topicId, topics.id))
.where(eq(agentEvalRunTopics.runId, runId))
.orderBy(asc(agentEvalRunTopics.createdAt));
// ❌ 不推荐:使用带 `with:` 的关系型 API
return this.db.query.agentEvalRunTopics.findMany({
where: eq(agentEvalRunTopics.runId, runId),
with: { testCase: true, topic: true },
});
// ✅ 推荐:select + leftJoin + groupBy
const rows = await this.db
.select({
id: agentEvalDatasets.id,
name: agentEvalDatasets.name,
testCaseCount: count(agentEvalTestCases.id).as('testCaseCount'),
})
.from(agentEvalDatasets)
.leftJoin(agentEvalTestCases, eq(agentEvalDatasets.id, agentEvalTestCases.datasetId))
.groupBy(agentEvalDatasets.id);
当你需要一个父记录及其子记录时,使用两个查询,而不是关系型的 with::
// ✅ 推荐:两个简单查询
const [dataset] = await this.db
.select()
.from(agentEvalDatasets)
.where(eq(agentEvalDatasets.id, id))
.limit(1);
if (!dataset) return undefined;
const testCases = await this.db
.select()
.from(agentEvalTestCases)
.where(eq(agentEvalTestCases.datasetId, id))
.orderBy(asc(agentEvalTestCases.sortOrder));
return { ...dataset, testCases };
关于详细的迁移指南,请参阅 db-migrations 技能。
每周安装量
471
代码仓库
GitHub 星标数
74.3K
首次出现
2026年1月27日
安全审计
安装于
opencode425
codex420
gemini-cli418
github-copilot402
cursor360
kimi-cli358
drizzle.config.tssrc/database/schemas/src/database/migrations/postgresql with strict: trueLocation: src/database/schemas/_helpers.ts
timestamptz(name): Timestamp with timezonecreatedAt(), updatedAt(), accessedAt(): Standard timestamp columnstimestamps: Object with all three for easy spreadusers, session_groups)user_id, created_at)id: text('id')
.primaryKey()
.$defaultFn(() => idGenerator('agents'))
.notNull(),
ID prefixes make entity types distinguishable. For internal tables, use uuid.
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
...timestamps, // Spread from _helpers.ts
// Return array (object style deprecated)
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
export const insertAgentSchema = createInsertSchema(agents);
export type NewAgent = typeof agents.$inferInsert;
export type AgentItem = typeof agents.$inferSelect;
export const agents = pgTable(
'agents',
{
id: text('id')
.primaryKey()
.$defaultFn(() => idGenerator('agents'))
.notNull(),
slug: varchar('slug', { length: 100 })
.$defaultFn(() => randomSlug(4))
.unique(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
clientId: text('client_id'),
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
...timestamps,
},
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
);
export const agentsKnowledgeBases = pgTable(
'agents_knowledge_bases',
{
agentId: text('agent_id')
.references(() => agents.id, { onDelete: 'cascade' })
.notNull(),
knowledgeBaseId: text('knowledge_base_id')
.references(() => knowledgeBases.id, { onDelete: 'cascade' })
.notNull(),
userId: text('user_id')
.references(() => users.id, { onDelete: 'cascade' })
.notNull(),
enabled: boolean('enabled').default(true),
...timestamps,
},
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
);
Always usedb.select() builder API. Never use db.query.* relational API (findMany, findFirst, with:).
The relational API generates complex lateral joins with json_build_array that are fragile and hard to debug.
// ✅ Good
const [result] = await this.db
.select()
.from(agents)
.where(eq(agents.id, id))
.limit(1);
return result;
// ❌ Bad: relational API
return this.db.query.agents.findFirst({
where: eq(agents.id, id),
});
// ✅ Good: explicit select + leftJoin
const rows = await this.db
.select({
runId: agentEvalRunTopics.runId,
score: agentEvalRunTopics.score,
testCase: agentEvalTestCases,
topic: topics,
})
.from(agentEvalRunTopics)
.leftJoin(agentEvalTestCases, eq(agentEvalRunTopics.testCaseId, agentEvalTestCases.id))
.leftJoin(topics, eq(agentEvalRunTopics.topicId, topics.id))
.where(eq(agentEvalRunTopics.runId, runId))
.orderBy(asc(agentEvalRunTopics.createdAt));
// ❌ Bad: relational API with `with:`
return this.db.query.agentEvalRunTopics.findMany({
where: eq(agentEvalRunTopics.runId, runId),
with: { testCase: true, topic: true },
});
// ✅ Good: select + leftJoin + groupBy
const rows = await this.db
.select({
id: agentEvalDatasets.id,
name: agentEvalDatasets.name,
testCaseCount: count(agentEvalTestCases.id).as('testCaseCount'),
})
.from(agentEvalDatasets)
.leftJoin(agentEvalTestCases, eq(agentEvalDatasets.id, agentEvalTestCases.datasetId))
.groupBy(agentEvalDatasets.id);
When you need a parent record with its children, use two queries instead of relational with::
// ✅ Good: two simple queries
const [dataset] = await this.db
.select()
.from(agentEvalDatasets)
.where(eq(agentEvalDatasets.id, id))
.limit(1);
if (!dataset) return undefined;
const testCases = await this.db
.select()
.from(agentEvalTestCases)
.where(eq(agentEvalTestCases.datasetId, id))
.orderBy(asc(agentEvalTestCases.sortOrder));
return { ...dataset, testCases };
See the db-migrations skill for the detailed migration guide.
Weekly Installs
471
Repository
GitHub Stars
74.3K
First Seen
Jan 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode425
codex420
gemini-cli418
github-copilot402
cursor360
kimi-cli358
Android 整洁架构指南:模块化设计、依赖注入与数据层实现
902 周安装