用一杯咖啡的时间学会 FilterQL:给数据做“精准筛选”的迷你语言

把 1000 条电影记录变成“2000 年后、评分 8.5 以上、按评分倒序”的 10 部佳片,只需要一行:

(genre == Action || genre == Comedy) && year >= 2000 && rating >= 8.5 | SORT rating desc | LIMIT 10

如果你写过 SQL 的 WHERE、用过 JavaScript 的 array.filter(),或者只是想把 API 返回的一大坨 JSON 迅速缩小到“我真正关心的那一小撮”,FilterQL 就是为你准备的“口袋瑞士军刀”。
它只有两项资产:

  • 一个仅 3 KB 左右的 TypeScript 库,安装即用;
  • 一份开放的语法规范,任何语言都能照着实现。

下面用问答方式带你完整体验:从安装、语法、实战到扩展,一个不落。


目录

  1. 一句话认识 FilterQL
  2. 安装与 30 秒上手
  3. 语法速查表(会用即走)
  4. 真实场景:CLI 工具里的“万能过滤器”
  5. 在浏览器或 Node 里写代码:细节拆解

    • 定义字段(schema)
    • 写查询(query)
    • 管道操作(排序、截取)
  6. 常见疑问 FAQ
  7. 高级:自定义操作 ROUND、FILTER 扩展
  8. 语言规范极简导航
  9. 小结与下一步

1. 一句话认识 FilterQL

FilterQL = 轻量级 WHERE 子句 + 链式数据管道

  • 语法接近日常英语:
    title == "Inception" && year >= 2010
  • 自带排序、分页:
    rating >= 8.5 | SORT rating desc | LIMIT 10
  • 不挑数据源:数组、JSON、CSV 转成对象数组都能用。

2. 安装与 30 秒上手

安装

# 推荐用 Bun
bun add filterql
# 或 npm / pnpm / yarn
npm install filterql

30 秒上手脚本

// demo.ts
import { FilterQL } from "filterql"

// 1. 描述数据字段
const schema = {
  title:  { type: "string", alias: "t" },
  year:   { type: "number", alias: "y" },
  rating: { type: "number" },
  genre:  { type: "string" },
  watched:{ type: "boolean", alias: "w" }
}

// 2. 创建实例
const fql = new FilterQL({ schema })

// 3. 随便来几条数据
const movies = [
  { title: "The Matrix", year: 1999, rating: 8.7, genre: "Action", watched: true },
  { title: "Inception",  year: 2010, rating: 8.8, genre: "Sci-Fi", watched: false },
  { title: "The Dark Knight", year: 2008, rating: 9.0, genre: "Action", watched: true }
]

// 4. 查询:想看已看过的动作片
const result = fql.filter(movies, "genre == Action && watched | SORT rating desc")
console.table(result)

运行:

bun demo.ts

输出:

┌---------┬-------------------┬------┬--------┬--------┬---------┐
│ (index) │      title        │ year │ rating │ genre  │ watched │
├---------┼-------------------┼------┼--------┼--------┼---------┤
│    0    │ "The Dark Knight" │ 2008 │   9    │"Action"│  true   │
│    1    │   "The Matrix"    │ 1999 │  8.7   │"Action"│  true   │
└---------┴-------------------┴------┴--------┴--------┴---------┘

3. 语法速查表

目的 写法示例 备注
等于 title == "Inception" 区分大小写
不区分大小写 title i== "inception" 前缀 i
包含 title *= "cep" 子串匹配
开头 title ^= "The"
结尾 title $= "Knight"
正则 title ~= "In.*ion" 标准正则
大于等于 rating >= 8.5 数字、日期都行
逻辑与 &&
逻辑或 `
取反 ! !watched 等价 watched == false
布尔简写 watched 直接写字段名 = 为 true
全匹配 * 不筛选,只做后续排序/截取
排序 SORT field [asc/desc] 默认升序
截取 LIMIT 10 取前 10 条

4. 真实场景:CLI 工具里的“万能过滤器”

想象你正在写一个 movie-cli 命令行,用户想这样用:

movie-cli '(genre == Action || genre == Comedy) && year >= 2000 && rating >= 8.5'

核心代码(仅 10 行):

// movie-cli.ts
import { FilterQL } from "filterql"

const data = await (await fetch("https://api.example.com/movies")).json()
const schema = {/* 同上 */}
const query = process.argv[2]          // 接收用户输入
const fql = new FilterQL({ schema })
console.table(fql.filter(data, query))

就这么简单,用户立刻拥有“类 SQL”的即席查询能力。


5. 在浏览器或 Node 里写代码:细节拆解

5.1 定义字段(schema)

const schema = {
  title:  { type: "string", alias: "t" },   // 支持别名
  year:   { type: "number", alias: "y" },
  rating: { type: "number" },
  genre:  { type: "string" },
  watched:{ type: "boolean", alias: "w" }
}
  • type 只能是 "string" | "number" | "boolean"
  • alias 可选,写查询时更短:y >= 2000 代替 year >= 2000
  • 数据里有多余字段完全没关系——FilterQL 只认 schema 声明的。

5.2 写查询(query)

  1. 单条件
    "rating >= 8.5"
  2. 多条件
    (genre == Action || genre == Comedy) && year >= 2000
  3. 使用别名
    y >= 2000 && r >= 8.5
  4. 布尔字段
    watched && !downloaded
  5. 空值检查
    rating != ""(把 null / undefined / “” 视为空)

5.3 管道操作(排序、截取)

语法:
查询 | 操作1 [参数] | 操作2 [参数]

内置操作只有两条,但已覆盖 80% 需求:

  • SORT
    SORT rating desc 降序
    SORT year asc 升序(默认)
  • LIMIT
    LIMIT 20 取前 20 条

链式示例:
year >= 2000 | SORT rating desc | LIMIT 10


6. 常见疑问 FAQ

问题 回答
大小写敏感吗? 默认敏感,用 i== / i*= 等前缀即可忽略大小写。
值里带空格怎么办? 用双引号:title == "The Dark Knight"
值里有双引号怎么办? 转义:"A movie with \"quotes\""
能过滤嵌套字段吗? 当前版本只支持一层对象,嵌套可用平铺或后期扩展。
查询语法错了会怎样? 库会抛明确的异常,告诉你哪一列、哪个字符出错。
支持 TypeScript 类型提示吗? 支持,源码已用 .ts 编写并导出全部类型。

7. 高级:自定义操作 ROUND、FILTER 扩展

当内置的 SORTLIMIT 不够时,你可以注册自己的大写操作。例如把评分四舍五入:

import type { OperationMap } from "filterql"

const customOperations: OperationMap = {
  ROUND: (data, args, { resolveField }) => {
    const field = resolveField(args[0])   // 把别名转回正式字段
    if (!field) throw new Error(`Unknown field '${args[0]}'`)
    return data.map(item => ({
      ...item,
      [field]: Math.round(item[field] as number)
    }))
  }
}

const fql = new FilterQL({ schema, customOperations })

// 使用
const rounded = fql.filter(movies, "* | SORT rating desc | ROUND rating")
console.table(rounded)

输出:

rating: 9 -> 9
rating: 8.8 -> 9
...

你可以写任何纯函数式操作:去重、分组、统计……只要满足签名 (data, args, helpers) => newData


8. 语言规范极简导航

如果你打算用 Python、Go、Rust 再实现一份 FilterQL,只需要记住 5 条核心语法规则:

  1. 查询结构
    filter | 操作1 | 操作2 ...

  2. 表达式优先级(高→低)
    ()!&&||

  3. 比较符
    == != *= ^= $= ~= >= <= 都可加 i 前缀

    • 无空格可裸写:Matrix
    • 有空格/括号/特殊字符必须 "双引号包裹"
    • 内部双引号用 \" 转义
  4. 操作

    • 名字必须全大写
    • 参数跟在名字后,空格分隔

完整 EBNF 见仓库 Language Specification 一节,不到 40 行,复制即可开工。


9. 小结与下一步

FilterQL 把“写复杂筛选”变成“写一句话”,但:

  • 「不是 SQL 的替代品」,而是 JS / TS 生态里轻如鸿毛的补充。
  • 「不会让你写正则写到头疼」,常用比较符一行搞定。
  • 「可扩展」,自定义操作让业务逻辑脱离查询字符串,保持整洁。

下一步建议:

  1. 把上面 CLI demo 打包成 npm create movie-cli 模板。
  2. 在浏览器端用 FilterQL 做表格即时搜索(几百 KB 的库对比 3 KB 的 FilterQL,加载速度肉眼可见)。
  3. 阅读源码 src/operation-evaluator/operations.ts,10 分钟就能学会写自定义操作。

祝你玩得开心,下次再见!