@dudousxd/nestjs-filter-dryzzle (英语)
Drizzle ORM 适应器 @dudousxd/nestjs-filter。 。 。 .
提供 DrizzleFilter, (中文(简体) ), DrizzleAdapter, (中文(简体) ), DrizzleQuery,以及 DrizzleFilterModule。 。 。 .
Postgres,MySQL和SQLite得到支持(每个方言都运行着交叉适应器合同套件).
安装
pnpm add @dudousxd/nestjs-filter @dudousxd/nestjs-filter-drizzle
同伴依赖性 : drizzle-orm 0.40 < 1.0, (中文(简体) ), @nestjs/common >=10, @nestjs/core 十点.
可选 : @nestjs/drizzle ^ 0.0.2 < 1.0,适配器默认使用其数据库.
快速启动
// schema.ts — the table object IS the entity
import { relations } from 'drizzle-orm';
import { integer, pgTable, serial, text } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
age: integer('age').notNull(),
role: text('role').notNull(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
status: text('status').notNull(),
authorId: integer('author_id').notNull(),
});
// relations() powers includes, dot-notation filters, whereHas(), aggregates and describe()
export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts) }));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
export const schema = { users, posts, usersRelations, postsRelations };
// user.filter.ts
import { Injectable } from '@nestjs/common';
import { Filterable, FilterFor } from '@dudousxd/nestjs-filter';
import { DrizzleFilter } from '@dudousxd/nestjs-filter-drizzle';
import { eq, gte } from 'drizzle-orm';
import { posts, users } from './schema.js';
@Injectable()
@Filterable({ entity: users })
export class UserFilter extends DrizzleFilter<typeof users> {
static readonly sort = ['name', 'age', 'posts.$count'];
static readonly search = ['name'];
static readonly includes = ['posts'];
@FilterFor('minAge')
applyMinAge(value: number) {
this.$query.where(gte(users.age, value));
}
@FilterFor('name')
applyName(value: string) {
this.whereILike('name', value); // escaped; ILIKE on Postgres, lower() LIKE elsewhere
}
@FilterFor('hasPublished')
applyHasPublished(value: boolean) {
if (value) this.$query.whereHas('posts', (p) => eq((p as typeof posts).status, 'published'));
}
}
// app.module.ts — the database comes from NestJS's @nestjs/drizzle (https://docs.nestjs.com/data/drizzle)
import { Module } from '@nestjs/common';
import { DrizzleModule } from '@nestjs/drizzle';
import { FilterModule } from '@dudousxd/nestjs-filter';
import { drizzleAdapter } from '@dudousxd/nestjs-filter-drizzle';
import { drizzle } from 'drizzle-orm/node-postgres';
import { schema } from './schema.js';
@Module({
imports: [
DrizzleModule.forRoot({ drizzle, connection: process.env.DATABASE_URL!, schema }),
// no `connection`/`db`: injects getDrizzleToken(name) from @nestjs/drizzle
FilterModule.forRoot({ inputNormalizer: 'camelCase', adapter: drizzleAdapter() }),
FilterModule.forFeature([UserFilter]),
],
controllers: [UsersController],
})
export class AppModule {}
已命名的连接 : drizzleAdapter({ name: 'reports' }) 页:1 DrizzleModule.forRoot({ name: 'reports', ... })。 。 。 .
@nestjs/drizzle 是一个可选的对等点:没有它,就通过自己的标志(drizzleAdapter({ connection: DRIZZLE }), (中文(简体) ),
提供来源a @Global() 模块)或实例(drizzleAdapter({ db })) (中文(简体) ).
// users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ApplyFilter } from '@dudousxd/nestjs-filter';
import type { DrizzleQuery } from '@dudousxd/nestjs-filter-drizzle';
@Controller('users')
export class UsersController {
@Get()
list(@ApplyFilter(UserFilter) q: DrizzleQuery<typeof users>) {
return q.execute(); // or q.executeAndCount(), q.count(), q.toSelect(), q.toSQL()
}
}
GET /users?minAge=18&name=al&sort=-age&include=posts&paginate[page]=0&paginate[size]=20
它如何映射到 Drizzle 上
| 概念 | MikroORM / TypeORM 适配器 | 滴滴适配器 |
|---|---|---|
| 实体 | 装饰类(User(中文(简体) ) | 表格对象 (users(中文(简体) ) |
| 元数据 | ORM 元数据登记册 | getTableColumns(table) +该 relations() 在你的计划 |
this.$query | oRM 的查询构建器 | DrizzleQuery - 累积器 SQL 条件、顺序、窗口、投影和包括,作为 " 一体行动 " 实现 db.select().from(table) 执行时 |
关系过滤器( E)posts.status, (中文(简体) ), @Relations, (中文(简体) ), related()(中文(简体) ) | 联 合 | 关联 EXISTS (…) 子点 |
排序/ 区分于一个路径( O) (manager.name(中文(简体) ) | 联 合 | 相關相關相關相關相關相關相關相關分母 |
include | 加入/ ORM 填充 | 批次 SELECT … WHERE fk IN (…) 按关系分列 row.posts[] 编号 : row.author (形状) db.query…findMany({ with }) 返回) |
findAndCount 共计 | ORM 计数 | count(*) 平起平起平起地 distinct(中文(简体) ) |
合计数(posts.$count(中文(简体) ) | 关联子点 | 关联子点 |
根查询从不加入, 因此它总是每个匹配的父返回一行 :
LIMIT编号 :OFFSET 财务报告和审定财务报表 COUNT(*) 不管请求涉及什么关系 都要保持精确.
DrizzleQuery API 密码
| 成员 | |
|---|---|
where(...conds) | 和SQL条件undefined 被忽略 —— 作曲 and()编号 :or() 自由参加) |
andWhere(cond | { col: value }) | 也接受平等地图(阵列) – IN, (中文(简体) ), null → IS NULL(中文(简体) ) |
whereHas(path, (target) => cond?) 编号 : whereDoesntHave(...) | 关联 EXISTS 超过关系路径( e)'posts.comments'(中文(简体) ) |
orderBy(...terms), (中文(简体) ), clearOrderBy(), (中文(简体) ), limit(n), (中文(简体) ), offset(n) | 顺序和窗口 |
select(fields), (中文(简体) ), addSelect(fields), (中文(简体) ), distinct() | 预测 |
include(...paths) | 已装入的关系 execute() |
columns, (中文(简体) ), table, (中文(简体) ), db, (中文(简体) ), dialect | 表格的列(可能别名)和执行上下文 |
execute(), (中文(简体) ), count(), (中文(简体) ), executeAndCount() | 运行它 |
toSelect(), (中文(简体) ), toSQL() | 已成形的细管构建器 / SQL + 参数 |
双倍关系过滤器
当过滤器通过时 @Relations (或 this.related(...)页:1 $query 目标a
别名 内部的表格 EXISTS 亚克瑞. 读取列通过 this.columns
(或 whereHas而不是此类过滤器中导入的表格对象:
@FilterFor('postStatus')
applyStatus(value: string) {
this.$query.where(eq(this.columns.status, value));
}
已计算字段
与其他适配器相同的三个源形式. 因为适配器从来没有加入,一个
字符串源中的不合格列总是表示根表的列; 一个函数
来源收到 { alias: <table name>, em: db } 并可能返回 SQL 字符串、 细雨
sql 片段,或选择的构建器(用作平板子:
@Filterable({
entity: users,
computed: {
doubleAge: '(age * 2)',
label: { source: `name || ' <' || role || '>'`, project: true },
},
})
export class UserFilter extends DrizzleFilter<typeof users> {
@Computed({ type: 'number' })
postCount() {
return sql`(select count(*) from ${posts} where ${posts.authorId} = ${users.id})`;
}
}
测试
不需要数据库:对照一个过滤器运行 DrizzleQuery 结束 drizzle.mock() 并申明SQL.
import { drizzle } from 'drizzle-orm/node-postgres';
const q = new DrizzleAdapter(drizzle.mock()).query(users);
await runner.apply(UserFilter, { name: 'al', minAge: 18 }, q);
expect(q.toSQL()).toEqual({
sql: 'select "id", "name", "age" from "users" where ("users"."name" ilike $1 and "users"."age" >= $2)',
params: ['%al%', 18],
});
FilterTestingModule + 键 makeMockQueryBuilder 也工作:模拟记录 ['where', SQL]
调用,您可以用方言调用new PgDialect().sqlToQuery(sql)) (中文(简体) ).
限制
- 仅细流 0.x (单位:千美元)
relations()(原始内容存档于2017-10-01). Drizzle 1.0'sdefineRelations尚未读取. - 无多对多 关系类型: 细雨模型化为相交表, 因此显示
作为一对一的接合点(
user.userTags[].tag) (中文(简体) ). - JSON 子路径 (单位:千美元)
metadata.tier)不能解决(MikroORM适配器支持它们). - 复合-关键关系 被跳过
include;过滤通过它们有效. - 矢量搜索 (单位:千美元)
search = { vector })是只使用 Postgres . - 代码 (单位:千美元)
@dudousxd/nestjs-filter-codegen通过TypeScript AST读取实体类; 它还没有得到filterFields从Drizzle桌子.
API 参考
DrizzleFilter<TTable>
抽象基础类. this.$query 是一个 DrizzleQuery<TTable>。帮助者: columns, (中文(简体) ),
whereLike, (中文(简体) ), whereILike, (中文(简体) ), whereBeginsWith, (中文(简体) ), whereEndsWith (都逃脱了价值).
DrizzleFilterModule.forRoot({ connection | db, schema?, dialect?, imports? })
登记册 DrizzleAdapter 作为过滤器适配器. connection 您的注射符号
细雨实例(由全球提供,或由以下所列模块提供) imports(三); db 通过
直接出现. schema 默认为该实例的设定.
drizzleAdapter(options) 和描述词一样 FilterModule.forRoot({ adapter })。 。 。 .
DrizzleAdapter
执行 FilterAdapter。 。 。 . adapter.query(table) 创建一个 DrizzleQuery 服务:
const q = adapter.query(users);
await runner.apply(UserFilter, input, q);
const { rows, total } = await q.executeAndCount();