WrenAI wren-core-wasm 完全指南:在浏览器中通过语义层直接查询 Parquet 的开源 WebAssembly SQL 引擎
发布时间:2026/9/14 3:34:34来源:尧图网络
WrenAI wren-core-wasm 完全指南在浏览器中通过语义层直接查询 Parquet 的开源 WebAssembly SQL 引擎【免费下载链接】WrenAIGenBI (Generative BI) for AI agents, an open-source, governed text-to-SQL through an open context layer that turns natural-language questions into trusted dashboards, charts, and SQL across 20 data sources, such as BigQuery, Snowflake, PostgreSQL, ClickHouse, Amazon Redshift, Databricks and more.项目地址: https://gitcode.com/GitHub_Trending/wr/WrenAI导读本文围绕 WrenAI 开源仓库中的wren-core-wasm子项目展开讲解如何将基于 Apache DataFusion 编译而来的语义化 SQL 引擎嵌入浏览器无需任何后端服务即可在页面内通过 MDLModeling Definition Language语义层直接查询 Parquet、JSON 与 CSV 数据。读完本文你将掌握 npm/CDN 安装方式、Inline / URL / Node.js 三种数据加载模式、本地开发服务器选择、完整的 TypeScript API 用法含cubeQuery语义层查询以及从 Rust 源码构建 WASM 二进制并理解其底层执行原理的方法。什么是 wren-core-wasmwren-core-wasm是 WrenAI 语义引擎的 WebAssembly 版本其核心定位是Browser-native semantic SQL engine将 Apache DataFusion 编译为 WASM在浏览器中直接加载 MDL 语义层定义并对 Parquet / JSON / CSV 数据执行 SQL 查询全程无需服务器参与。从 Rust 源码 可以看出它使用上游官方 DataFusion而非 Canner fork原因是 WASM 版本直接通过 DataFusion 执行查询不需要 SQL unparser 与方言转译。引擎内部持有 DataFusionSessionContext与解析后的AnalyzedWrenMDL所有查询都在浏览器内完成数据不出页面。整个 crate 的开发分为四个里程碑见 lib.rs 顶部注释M1DataFusion WASM 编译 内存查询M2Parquet 文件上传与浏览器内查询M3wren-core 语义层MDL 计划重写M4npm 包 TypeScript API 封装当前仓库已完整实现上述四个阶段包括语义层重写与cubeQuery结构化查询能力。安装通过 npm 安装npm install wrenai/wren-core-wasm包名wrenai/wren-core-wasm当前仓库版本为0.4.1见 package.json要求 Node.js 16产物包含 ESM 模块dist/index.js与类型声明dist/index.d.ts。通过 CDN 直接使用script typemodule import { WrenEngine } from https://unpkg.com/wrenai/wren-core-wasm0.3.0/dist/index.js; /script重要提示请使用 unpkg不要用 jsDelivr。jsDelivr 免费 CDN 的单文件大小限制为 50 MB而该 WASM 二进制原始体积约68 MBgzip 后约 14 MB在 jsDelivr 上拉取.wasm会直接返回 403。这一点在 README 与 AGENT_GUIDE.md 中均有明确警示。由于二进制体积较大页面加载时应显示 loading 指示器避免用户在WrenEngine.init()期间误以为页面卡死。快速开始三种数据加载模式WrenEngine的数据加载分为两种形态外加 Node.js 特殊场景Inline Mode与URL Mode。选择逻辑很简单——数据总量在 ~50 MB 以下、且希望规避 HTTP Range/CORS 问题就用 Inline数据已经很大或在 CDN 上就用 URL。Inline Mode推荐用于本地开发与打包型 DashboardInline 模式下数据直接由 JavaScript 注册进引擎没有任何服务器依赖也没有 Range/CORS 的地雷。总数据量在 ~50 MB 以内时这是阻力最小的路径。import { WrenEngine } from wrenai/wren-core-wasm; const engine await WrenEngine.init(); // 注册 JSON 数据为表 await engine.registerJson(orders, [ { id: 1, customer: Alice, amount: 100 }, { id: 2, customer: Alice, amount: 250 }, { id: 3, customer: Bob, amount: 120 }, ]); // 或用 ArrayBuffer 注册 Parquet const response await fetch(orders.parquet); await engine.registerParquet(orders, await response.arrayBuffer()); // 或注册 CSV —— 支持字符串或字节可选 schema / 分隔符 / 引号 await engine.registerCsv(orders, id,customer,amount\n1,Alice,100\n2,Bob,200); const mdl { catalog: wren, schema: public, models: [ { name: Orders, tableReference: { table: orders }, columns: [ { name: id, type: INTEGER }, { name: customer, type: VARCHAR }, { name: amount, type: DOUBLE }, ], primaryKey: id, }, ], relationships: [], views: [], }; // source 传空字符串表示使用预注册的表 await engine.loadMDL(mdl, { source: }); const rows await engine.query(SELECT * FROM Orders LIMIT 10);注意顺序Inline 模式中必须先registerJson/registerParquet/registerCsv再调用loadMDL并且注册操作必须逐个串行执行——WASM 引擎是单线程的并发注册并不安全见 AGENT_GUIDE.md。URL Mode数据量大或已托管在 CDN 时URL 模式下数据存放在 HTTP 服务器上DataFusion 通过HTTP range requests读取每个 Parquet 文件先读 footer再按需读取 row group。服务器必须支持Range:头否则请退回 Inline 模式。await engine.loadMDL(mdl, { source: https://your-cdn.com/data/ }); const rows await engine.query(SELECT customer, sum(amount) AS total FROM Orders GROUP BY customer); console.table(rows); // [{ customer: Alice, total: 350 }, { customer: Bob, total: 120 }]从 lib.rs 的实现可见URL 模式下引擎为每个模型注册一个 DataFusionListingTable其 URL 固定拼接为{source}/{bare_table_name}.parquet。这意味着MDL 的tableReference必须使用裸表名如orders而不是完整 URL若不同 schema 下存在同名裸表如raw.orders与staging.orders二者会静默冲突到同一个{source}/orders.parquet——Phase 2 假设扁平的 Parquet 布局更丰富的 schema 映射如{source}/{schema}/{name}.parquet被列为 Phase 4 工作URL 模式要求服务器同时支持CORS与Range 请求source仅接受http:///https://前缀s3://、gs://属于 Phase 4当前会落入 local mode 并快速报错。Node.js 用法WrenEngine.init()默认通过import.meta.url获取 WASM 二进制这在 Node 中会解析为file://URL而 Node 的undicifetch 不支持file://因此init()会直接抛错。解决办法是直接把二进制以BufferSource形式传入import { readFileSync } from node:fs; import { WrenEngine } from wrenai/wren-core-wasm; const buf readFileSync( node_modules/wrenai/wren-core-wasm/dist/wren_core_wasm_bg.wasm ); const engine await WrenEngine.init({ wasmUrl: buf.buffer.slice(buf.byteOffset, buf.byteOffset buf.byteLength), });这一模式非常适合单元测试、CI 冒烟检查以及任何非浏览器环境如node --test。如何选择本地开发服务器URL 模式依赖 DataFusion 的ListingTable它会通过 HTTP range requests 读取 Parquet。如果开发服务器不支持Range:头footer 读取之后请求会静默挂起。服务器Range 支持备注python -m http.server❌ 不支持Python 内置URL 模式请避开python -m RangeHTTPServer✅ 支持pip install rangehttpservernpx serve⚠️ 单区间基于sirv当请求范围越过 EOF 时可能返回416npx http-server✅ 支持默认带 CORScaddy file-server✅ 支持生产可用Vite⚠️ 单区间同样基于sirv存在与npx serve相同的416边界问题webpack-dev-server⚠️ 单区间多区间 range 请求会退化为返回整个资源快速自检命令curl -I -H Range: bytes0-1023 http://localhost:PORT/file.parquet应返回HTTP/1.1 206 Partial Content而不是200。如果只能使用不支持 Range 的服务器就改用Inline 模式用fetch()把每个文件拉取一次再通过registerParquet注册。仓库自带的示例服务器 serve.mjs 是一个现成参考实现它默认监听 8787 端口提供 CORS 头Access-Control-Allow-Origin: *、Access-Control-Allow-Headers: Range、Access-Control-Expose-Headers: Content-Range, Content-Length, Accept-Ranges、Accept-Ranges: bytes以及单区间206 Partial Content响应并做了路径穿越防护拒绝..逃逸根目录的请求。运行仓库自带示例examples/目录包含可直接运行的浏览器演示见 examples它们直接从本地pkg/导入 WASM 构建产物因此总是反映当前源码状态——在迭代 Rust 或 TypeScript 代码时非常方便。# 构建 WASM 二进制示例用 debug 构建即可 just build-wasm-dev # 启动带 CORS Range 支持的静态开发服务器 just serve服务器启动时会打印所有演示地址相关逻辑见 serve.mjs演示URL展示内容Inline datahttp://localhost:8787/examples/inline.htmlregisterJson 原生 SQLquery()URL modehttp://localhost:8787/examples/url-mode.html通过 HTTP range requests 读取远程 ParquetCDN smoke testhttp://localhost:8787/examples/test-cdn.html从 unpkg 加载已发布包Cube quickstarthttp://localhost:8787/examples/cube-quickstart.html最小化cubeQuery()—— 三个预设查询group-by、filter、time bucketCube explorerhttp://localhost:8787/examples/cube-explorer.html表单驱动的CubeQuery构建器 —— 选择 measures/dimensions、添加 filters、选择粒度与日期范围CSV quickstarthttp://localhost:8787/examples/csv-quickstart.html针对data/中真实文件的registerCsv()—— 自动推断 schema、自定义分隔符TSV、带显式 schema 的无表头 CSVCube quickstart 与 explorer 的区别Quickstart加载单个order_metricscube点击按钮时触发写死的cubeQuery()调用。阅读其源码可以看到最小化的端到端 cube 示例见 cube-quickstart.html包括 MDL 中内联的 cube 定义、派生度量如avg_order revenue / order_count以及按状态分组的默认查询。Explorer交互式页面复选框/下拉表单实时生成CubeQueryJSON与结果并列展示可添加任意数量的 filters覆盖全部 12 种FilterOperator取值。演示数据分布在多个 region / customer / month 维度上分组结果非平凡。在 Rust 侧修改代码后重新执行just build-wasm-dev并刷新页面即可——示例直接从pkg/wren_core_wasm.js导入。API 参考SDK 的全部类型定义与实现位于 sdk/src/index.ts。WrenEngine.init(options?)初始化引擎并加载 WASM 二进制。每个页面生命周期调用一次。static async init(options?: WrenEngineOptions): PromiseWrenEngine选项类型说明wasmUrlstring \| URL \| BufferSourceWASM 二进制来源。默认通过import.meta.url解析同目录下的wren_core_wasm_bg.wasm。engine.loadMDL(mdl, profile)加载 MDL 清单以启用语义层查询重写。async loadMDL(mdl: object, profile: WrenProfile): Promisevoid参数类型说明mdlobjectMDL 清单会被 JSON 序列化profile.sourcestringhttps://...走 URL 模式使用预注册表其他非空字符串走本地模式source的三种取值对应 lib.rs 中load_mdl的三个分支http(s)://前缀触发 URL 模式空字符串触发向后兼容的 fallback 模式从各模型的tableReference自动探测其余非空值进入本地模式。本地模式下若某模型的物理表缺失loadMDL会立即返回Unresolved models: [...]错误而不是推迟到查询时报错相关行为有test_local_source_missing_table_error测试验证。engine.registerParquet(name, data)将 Parquet 文件注册为具名表。Inline 模式下需在loadMDL之前调用。async registerParquet(name: string, data: ArrayBuffer): Promisevoidengine.registerJson(name, data)将 JSON 数据注册为具名表。同样需在loadMDL之前调用。async registerJson(name: string, data: object[]): PromisevoidRust 侧实现会把 JSON 数组转换为 NDJSON每行一个对象再交由 Arrow JSON reader 推断 schema 并生成 RecordBatch见 lib.rs 中的json_array_to_ndjson与register_json。engine.registerCsv(name, data, options?)将 CSV 数据注册为具名表。data可以是字符串按 UTF-8 处理或任意BufferSourceArrayBuffer / TypedArray / Node Buffer。默认首行为表头schema 从前 1000 行推断。async registerCsv( name: string, data: string | BufferSource, options?: CsvReadOptions, ): Promisevoid选项camelCase类型默认值说明headerbooleantrue首行是否为表头。delimiterstring,字段分隔符单个 ASCII 字符。quotestring\引号字符单个 ASCII 字符。escapestring未设置转义字符单个 ASCII 字符。terminatorstring\n、\r\n均可记录终止符单个 ASCII 字符。batchSizenumber8192RecordBatch 大小。inferRowsnumber1000用于推断的扫描行数。设置了schema时忽略。schemaCsvSchemaColumn[]自动推断显式 Arrow schema{ name, type, nullable? }[]。Schema 列类型大小写不敏感int8/int16/int32/int64、uint8/uint16/uint32/uint64、float32/float64、boolean、string别名utf8/varchar/text、date/date32/date64、timestamp及timestamp_{s,ms,us,ns}。Rust 侧还额外接受一组别名int/integer/bigint/long/float/double/real/number/bool映射关系见 lib.rs 中的arrow_schema_from_columns。单字符选项delimiter/quote/escape/terminator只取字符串首字节且必须是 ASCII——非 ASCII 分隔符会被拒绝对应测试test_register_csv_rejects_multibyte_delimiter。engine.query(sql)通过语义层执行 SQL 查询返回解析后的结果对象数组。async query(sql: string): PromiseRecordstring, unknown[]返回结果可直接用于 Chart.js、D3、Recharts 等前端图表库AGENT_GUIDE.md 给出了与各库对接的示例。engine.cubeQuery(query)对已加载的 MDL 执行结构化 cube 查询SDK 封装见 index.tsRust 侧cube_query会先通过 wren-core 将 CubeQuery 翻译为 SQL再走与query()相同的执行路径。聚合类查询建议优先用它代替手写 SQL——cube 层会替你组装GROUP BY、DATE_TRUNC和WHERE子句。调用前必须先loadMDL。async cubeQuery(query: CubeQueryInput): PromiseRecordstring, unknown[]CubeQueryInput结构{ cube, measures, dimensions?, timeDimensions?, filters?, limit?, offset? }。其中Granularityyear|quarter|month|week|day|hour|minuteFilterOperator12 种eq、neq、in、not_in、gt、gte、lt、lte、contains、starts_with、is_null、is_not_null。in/not_in用数组传valueis_null/is_not_null省略valueTimeDimensionInput.dateRange为[起, 止)左闭右开区间。时间桶结果以dimension__granularity列名暴露例如created_at__month相关断言见 sdk/tests/index.test.mjs。engine.listCubes()列出已加载 MDL 中定义的 cube返回{ name, baseObject, measures, dimensions, timeDimensions, hierarchies }[]。适合 Agent 在调用cubeQuery之前先探查可查询对象。同样要求先loadMDL。engine.free()释放 WASM 内存。引擎不再使用时调用。从源码构建 WASM前置条件Rust 工具链、wasm-pack、Node.js 16。cd wren-core-wasm # 安装 TypeScript 开发依赖 npm install # 构建 WASM 二进制需要 wasm32-unknown-unknown target wasm-pack build --target web --release # 构建 TypeScript 封装并组装 dist/ npm run build:dist # 运行集成测试 npm test # 仅做类型检查 npm run typecheck仓库的 justfile 提供了等价的高层命令just build构建全部、just build-wasm-devdebug 构建编译更快、just serve起示例服务器、just size报告 WASM 二进制原始与 gzip 体积、just clean清理产物。macOS 注意事项在 macOS 上构建 WASM 可能需要为 C 依赖安装 LLVMbrew install llvm CC_wasm32_unknown_unknown/opt/homebrew/opt/llvm/bin/clang \ AR_wasm32_unknown_unknown/opt/homebrew/opt/llvm/bin/llvm-ar \ CFLAGS_wasm32_unknown_unknown--targetwasm32-unknown-unknown \ wasm-pack build --target web --releasejustfile 中的build-wasm与build-wasm-dev已内置了这段 macOS 分支逻辑。底层依赖要点从 Cargo.toml 可以看到几个关键工程决策DataFusion 53 关闭default-features默认特性会启用 zstdC 库无法编译到 WASMParquet 的读取改为自管字节流 → Arrow RecordBatch → MemTableParquet 只启用snapSnappy与lz4二者是纯 Rust 实现WASM 兼容tokio 仅启用rtmacros单线程current_threadruntime没有 I/O driverchrono 开启wasmbind特性用js_sys::Date替代SystemTime。引擎初始化时将 DataFusion 的target_partitions设为 1WASM 单线程并把 session 时区设为 UTC00:00使浏览器的时间戳推断与原生侧create_wren_ctx保持一致。query()通过runtime.block_on(...)驱动执行——DataFusion 的物理算子如CoalescePartitionsExec任何多分区计划如UNION ALL都会经过它内部会调用tokio::task::spawn若不在 tokio runtime 上下文内执行会触发there is no reactor running恐慌0.4.0 版本的回归问题现已有test_union_all_does_not_trap等测试兜底。注意事项速查query()返回Recordstring, unknown[]可直接配合 Chart.js、D3、Recharts 使用MDL 的tableReference使用裸表名如orders不是完整 URL模型名大小写敏感SQL 中请用双引号FROM Orders而非FROM Orders本地开发数据在 ~50 MB 以下时优先 Inline 模式——它能彻底消除 HTTP Range/CORS 这一类问题URL 模式要求 HTTP 服务器同时支持 CORS 与 Range 请求Node 中必须给WrenEngine.init()传wasmUrl: BufferSource——Node 的 fetch 无法加载file://URLInline 模式下先注册数据、再loadMDL且注册需串行WASM 二进制约 68 MB 原始 / 约 14 MB gzip务必显示加载进度。许可wren-core-wasm采用Apache-2.0许可见 package.json 与 Cargo.toml。想进一步了解 MDL 语义层的完整定义可阅读仓库中的 wren-mdl/mdl.schema.json想了解语义引擎核心可进入 core/wren-core/core/src/mdl 目录继续深入。【免费下载链接】WrenAIGenBI (Generative BI) for AI agents, an open-source, governed text-to-SQL through an open context layer that turns natural-language questions into trusted dashboards, charts, and SQL across 20 data sources, such as BigQuery, Snowflake, PostgreSQL, ClickHouse, Amazon Redshift, Databricks and more.项目地址: https://gitcode.com/GitHub_Trending/wr/WrenAI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网