微风
一个类型安全查询编译器和服务层工具包. 解析URL-安全过滤字符串,编译为数据库查询(Drizzle ORM或生于SQL),并作曲截取器管道用于范围界定,软删除,以及审计线索——但投出零个例外.
?filter=status eq 'active' AND priority eq 'high'&sort=createdAt:desc&limit=50
安装
npm install @chromav/frizzle
Drizzle ORM是一种可选的对等依赖——只有在使用 Drizzle 代码生成器时才需要.
npm install drizzle-orm # if using @chromav/frizzle/drizzle
对等范围是 >=0.30.0 || >=1.0.0-beta.1。 RQB compileFilter 需要细雨-orm 1.0(β/rc),因为它的输出使用v2关系-query对象过滤语法(OR, (中文(简体) ), AND, (中文(简体) ), RAW),而细雨-orm 0.x没有. compileFilterToSQL 财务报告和审定财务报表 @chromav/frizzle/sql 整个范围的工作.
快速启动
过滤查询
import { isSuccess } from "@chromav/frizzle";
import { compileFilter } from "@chromav/frizzle/drizzle";
const result = await compileFilter(
"status eq 'active' and priority eq 'high'",
{ status: "status", priority: "priority" }
);
if (isSuccess(result)) {
const tasks = await db.query.tasks.findMany({
where: result.data,
});
}
compileFilter 目标 Drizzle 的关系查询(db.query.*)和需要细雨1.0(β/弧);见 安装。要添加您自己的条件,请将两者包入 AND —— 说 { AND: [{ workspaceId }, result.data] } - 而不是传播 result.data,可以让用户过滤器替换您的密钥.
过滤器 + 排序并进行计划验证
import { isSuccess } from "@chromav/frizzle";
import { compileFilter, compileSort } from "@chromav/frizzle/drizzle";
import type { Schema } from "@chromav/frizzle";
const schema: Schema = {
status: { type: "enum", enumValues: ["active", "pending", "done"] },
priority: { type: "enum", enumValues: ["low", "medium", "high"] },
title: { type: "string" },
createdAt: { type: "date" },
};
const columns = {
status: "status",
priority: "priority",
title: "title",
createdAt: "createdAt",
};
// Validates field names, operators, and value types before compiling
const filterResult = await compileFilter(
"status eq 'active' and title contains 'deploy'",
columns,
{ schema }
);
const sortResult = compileSort("createdAt:desc", columns);
if (isSuccess(filterResult) && isSuccess(sortResult)) {
const tasks = await db.query.tasks.findMany({
where: filterResult.data,
orderBy: sortResult.data,
});
}
SQL 构建器替代品( Drizzle)
import { isSuccess } from "@chromav/frizzle";
import { compileFilterToSQL } from "@chromav/frizzle/drizzle";
// Pass Drizzle column objects instead of strings
const result = compileFilterToSQL("status eq 'active'", {
status: tasks.status,
priority: tasks.priority,
});
if (isSuccess(result)) {
const rows = await db.select().from(tasks).where(result.data);
}
在代码中构建过滤器, 验证一次
有两个层次可以结合,每个层次都有自己的 and 编号 : or数字 :
| 你有过 | 与 | 实例 |
|---|---|---|
过滤树在汇编之前: parseFilter 返回,或从 f 建设者 | f.and, (中文(简体) ), f.or | 然后用简略的参数输入用户过滤器 validateFilter |
对象,在编译后: compileFilter 输出、范围、描述过滤器 | andWhere, (中文(简体) ), orWhere | 和一个编译的用户过滤器 { workspaceId } |
当可以时,更倾向于将树组合在一起:结果通过一个过滤器来验证(限制,计划).
import { f, isError, validateFilter } from "@chromav/frizzle";
import { generateDrizzleQuery } from "@chromav/frizzle/drizzle";
// f.and / f.or skip undefined, and group an or inside an and so the tree means what it says
const filter = f.and(
userFilter, // e.g. from parseFilter(params.filter)
region ? f.in("region", region.split(",")) : undefined,
);
// Schema checks plus the limits (maxParameters etc.) on the combined tree.
// On an error, reject the request: running the query without the filter
// would also drop the region restriction.
const valid = filter ? validateFilter(filter, schema) : undefined;
if (valid && isError(valid)) {
return badRequest(valid.error);
}
// One field map filters and sorts: `name` is filtered through a resolver, sorted by its column
const query = generateDrizzleQuery(
{ filter: valid?.data, sort: [{ field: "name", direction: "asc" }] },
db.select().from(countries).$dynamic(),
{ region: countries.region, name: { column: countries.name, where: nameOrAlias } },
{ db }, // passed to resolvers as their third argument
);
见 Schema 验证 财务报告和审定财务报表 Drizzle 整合。 。 。 .
原始 SQL 目标
对于数据库 Drizzle 不覆盖(例如:Trino, Spark, Iceberg):
import { compileSelect } from "@chromav/frizzle/sql";
import { sqliteDialect } from "@chromav/frizzle/sql";
const columns = { status: "status", priority: "priority", createdAt: "created_at" };
const { sql, parameters } = compileSelect("tasks", {
filter: { status: "active", priority: { gte: 3 } },
sort: { createdAt: "desc" },
take: 20,
}, columns, sqliteDialect);
// sql: SELECT * FROM "tasks" WHERE "status" = ? AND "priority" >= ? ORDER BY "created_at" DESC LIMIT ?
// parameters: ["active", 3, 20]
拦截管道
将贯穿各领域的关切编为可重复使用的拦截器:
import {
scopeQueryInterceptor,
softDeleteQueryInterceptor,
scopeWriteInterceptor,
softDeleteWriteInterceptor,
injectWriteInterceptor,
} from "@chromav/frizzle";
// Read pipeline: every query is scoped to workspace + excludes deleted
const queryInterceptors = [
scopeQueryInterceptor({ workspaceId: "ws-123" }),
softDeleteQueryInterceptor(),
];
// Write pipeline: scope + audit + soft-delete transform
const writeInterceptors = [
scopeWriteInterceptor({ workspaceId: "ws-123" }),
injectWriteInterceptor("user-456"),
softDeleteWriteInterceptor("user-456"),
];
查询语法
过滤操作符
| 运算符 | 类别 | 示例 |
|---|---|---|
eq, (中文(简体) ), ne | 比较 | status eq 'active' |
gt, (中文(简体) ), gte, (中文(简体) ), lt, (中文(简体) ), lte | 比较 | age gte 18 |
like, (中文(简体) ), ilike | 字符串( 子字符串) | name like 'john' |
contains, (中文(简体) ), starts_with, (中文(简体) ), ends_with | 字符串 | title contains 'deploy' |
in, (中文(简体) ), not_in | 阵列 | status in ['active', 'pending'] |
between | 范围 | age between [18, 65] |
is_null, (中文(简体) ), is_not_null | 无效检查 | assignedTo is_null |
符号别名也起作用 : =, (中文(简体) ), !=, (中文(简体) ), >, (中文(简体) ), >=, (中文(简体) ), <, (中文(简体) ), <=。 。 。 .
字符串运算符字面匹配值 : % 财务报告和审定财务报表 _ 普通字符,而不是通配符(title contains '50%' 找到文本 50%) (中文(简体) ).
逻辑操作符
status eq 'active' and priority eq 'high'
status eq 'active' or status eq 'pending'
(status eq 'active' or status eq 'pending') and priority eq 'high'
AND 绑定比 OR。父体优先.
排序和标记
sort=name:asc,createdAt:desc
limit=50
offset=100
排序方向必须是 asc 或者说 desc· ; limit 必须是从 1 到 1 的整数 maxLimit (默认 1000)和 offset 非负整数。 其他的都是个 INVALID_VALUE 错误.
限制
滤波器被封顶在32个等高的括号内(maxDepth),100个值(maxParameters,匹配 Cloudflare D1 的有限参数上限),和 1000 个比较(maxConditions) (中文(简体) ). 超越者返回 a LIMIT_EXCEEDED 错误。 超过每个电话,例如. compileFilter(expr, columns, { maxParameters: 50 })。见 限制。 。 。 .
处理错误
所有行动返回 Result<T> -没有例外.
import { isSuccess, isError } from "@chromav/frizzle";
const result = await compileFilter("bad ?? syntax", columns);
if (isError(result)) {
result.error.message; // "Expected operator, got '?'"
result.error.position; // 4
result.error.code; // "SYNTAX_ERROR"
result.error.suggestions; // Possible fixes (e.g. close field names, for UNKNOWN_FIELD)
}
条目点
| 导入 | 目录 |
|---|---|
@chromav/frizzle | 核心:编译器,解析器,AST类型,结果类型,计划类型,查询字符串解析器,描述器,截取器 |
@chromav/frizzle/drizzle | 滴入 ORM : compileFilter, (中文(简体) ), compileFilterToSQL, (中文(简体) ), compileSort, (中文(简体) ), DrizzleCodeGenerator |
@chromav/frizzle/sql | 原始 SQL : compileSelect, (中文(简体) ), compileFilter, (中文(简体) ), compileFilterNode, (中文(简体) ), compileInsert, (中文(简体) ), compileUpdate, (中文(简体) ), compileDelete, (中文(简体) ), Dialect, (中文(简体) ), sqliteDialect |
核心包的依赖性为零. @chromav/frizzle/drizzle 需求 drizzle-orm 作为同伴的依赖. @chromav/frizzle/sql 没有依赖关系.
说明
Frizzle在到达数据库适配器之前,使用通过拦截管流出的数据库不可知中间表示.
QueryDescriptor 描述读取操作:
import { mergeDescriptors } from "@chromav/frizzle";
import type { QueryDescriptor } from "@chromav/frizzle";
// A QueryDescriptor is a plain object; mergeDescriptors combines two
const base: QueryDescriptor = { filter: { workspaceId: "ws-1" } };
const descriptor = mergeDescriptors(base, {
filter: { status: "active" },
sort: { createdAt: "desc" },
take: 20,
skip: 0,
select: { id: true, title: true, status: true },
include: { assignee: true },
});
WriteDescriptor 描述一个写入操作:
import { createWriteDescriptor, mergeWriteDescriptor } from "@chromav/frizzle";
// Insert
const insert = createWriteDescriptor("insert", { title: "New task", status: "backlog" });
// Update
const update = mergeWriteDescriptor(
createWriteDescriptor("update", { status: "done" }),
{ targetIds: ["task-1", "task-2"] },
);
// Delete
const remove = mergeWriteDescriptor(
createWriteDescriptor("delete"),
{ targetIds: ["task-3"] },
);
参考截取器
共同事务层模式的可再使用实施。 全部从主出口 @chromav/frizzle 进入点.
查询拦截器
| 截取器 | 说明 |
|---|---|
scopeQueryInterceptor(fields) | 在每个查询中添加租户/工作空间过滤器 |
softDeleteQueryInterceptor(config?) | 不包括被软删除的行isDeleted: false(中文(简体) ) |
defaultIncludesInterceptor(defaults) | 深层默认关系包括 |
extraScopeInterceptor(scopeFn) | 从工厂中添加特定实体过滤条件 |
写入截取器
| 截取器 | 说明 |
|---|---|
scopeWriteInterceptor(fields) | 将范围值输入到每个写入中 |
softDeleteWriteInterceptor(userId, config?) | 变换删除 更新方式 isDeleted: true |
injectWriteInterceptor(userId, config?) | 喷出 createdById 插入时, updatedById 更新 |
extraScopeWriteInterceptor(scopeFn) | 插入额外范围字段 |
所有截取器都是通用的(可配置的列名,通用范围字段)并只使用frizzle自己的类型——0外在依赖.
SQL 目标
这个 @chromav/frizzle/sql 出入口编译了参数化的SQL字符串的描述符. 它支持任何 SQL 数据库通过 Dialect 接口.
对话框接口
import type { Dialect } from "@chromav/frizzle/sql";
const trinoDialect: Dialect = {
name: "trino",
quoteIdentifier: (name) => `"${name}"`,
parameter: (index) => `$${index + 1}`,
booleanLiteral: (v) => v ? "TRUE" : "FALSE",
supportsILike: true,
escapeLikePattern: (p) => p.replace(/[%_\\]/g, "\\$&"),
};
写入操作
import { compileInsert, compileUpdate, compileDelete } from "@chromav/frizzle/sql";
import { createWriteDescriptor, mergeWriteDescriptor } from "@chromav/frizzle";
import { sqliteDialect } from "@chromav/frizzle/sql";
const insert = createWriteDescriptor("insert", { title: "Task", status: "backlog" });
const { sql, parameters } = compileInsert("tasks", insert, sqliteDialect);
// sql: INSERT INTO "tasks" ("title", "status") VALUES (?, ?)
const update = mergeWriteDescriptor(
createWriteDescriptor("update", { status: "done" }),
{ targetIds: ["t-1"] },
);
const { sql, parameters } = compileUpdate("tasks", update, sqliteDialect);
// sql: UPDATE "tasks" SET "status" = ? WHERE "id" = ?
建筑
URL query string
↓ parseQueryString()
QueryDescriptor (database-agnostic IR)
↓ interceptor pipeline (scope, soft-delete, inject, ...)
QueryDescriptor (enriched)
↓ adapter
├── @chromav/frizzle/drizzle → Drizzle RQB findMany/findFirst
└── @chromav/frizzle/sql → parameterized SQL string
文档
核心
- 开始 ——两种方法,结构概述,每个步骤产生什么
- 说明 – 查询描述器和写入描述器作为数据库不可知的IRs
- 截取器 ——管道概念,参考实施,自写
- 查询语言 – 完整的语法参考,每个操作符都有示例
- Schema 验证 – 字段类型,操作员相容性,运行时谜题
- 处理错误 - 结果类型、错误代码、弹性模式
- 查询谱 (EBNF) - 正式语法
数据库目标
- Drizzle 整合 ——RQB vs SQL,列映射,嵌入路径,解析器
- Drizzle 代码生成器 ——审定规则,增加运算符,测试覆盖范围
- SQL 目标 —— Raw SQL 编译器,方言界面,所有编译器和操作器
指南
许可证
麻省理工学院