
Perspective Python Virtual Server 全指南直连 DuckDB / ClickHouse / Polars 与自定义数据源【免费下载链接】perspectiveA data visualization and analytics component, especially well-suited for large and/or streaming datasets.项目地址: https://gitcode.com/GitHub_Trending/pe/perspectiveVirtual Server 是 Perspective 的一项关键特性它允许perspective-viewer直接连接外部数据源如 DuckDB、ClickHouse、Polars而无需将数据加载进 Perspective 自带的引擎。查询会被翻译成外部数据库原生可执行的 SQL 或其他查询语言由外部引擎执行后仅返回当前视图所需的数据切片。本文将围绕 Python 生态下的虚拟服务器展开先讲清其工作原理再逐一给出 DuckDB、ClickHouse、Polars 三个内置实现的完整安装与运行示例最后深入VirtualServerHandler接口演示如何为任意数据源实现自定义虚拟服务器。为什么需要 Virtual ServerPerspective 内置的数据引擎本身已足够强大但在以下场景中把数据交给外部数据源处理反而更合理数据集太大无法整体放进浏览器内存或单个进程数据已经存在于某个数据库或 DataFrame 中希望避免重复拷贝一份进 Perspective希望充分利用数据库原生的查询优化能力谓词下推、列存扫描、并行聚合等数据源有 WASM 构建版本如duckdb/duckdb-wasm希望直接在浏览器端查询而不把数据灌入 Perspective 自身的引擎。Virtual Server 的核心理念是由 handler 充当翻译层。它实现了一套 Perspective 会回调的接口把 Perspective 的视图配置group by、sort、filter、aggregate 等翻译成外部系统的查询语言通常是 SQL执行查询后以列式数据返回结果。因为 handler 讲的是标准的 Perspective Client 协议所以它可以运行在任何 Client 能运行的地方——进程内、Web Worker 或远程服务器上。从整体架构看数据流非常简单示意图见 Virtual Servers 概念页┌──────────────────────────────────────────────────┐ │ perspective-viewer │ └──┬───────────────────────────────────────────────┘ │ ┌──────────────────────────────────────────────────┐ └──►│ Perspective Virtual Server Handler │ └──┬───────────────────────────────────────────────┘ │ ┌──────────────────────────────────────────────────┐ └──►│ External DB (DuckDB, ClickHouse, …). │ └──────────────────────────────────────────────────┘viewer 与虚拟服务器 handler 的通信方式和它与普通 Perspective Server 的通信方式完全一致。handler 通过一个features对象对外宣告自身能力支持哪些操作viewer 的 UI 会根据这份能力声明自动调整——不支持的操作对应控件会被禁用从而保证 UI 不会暴露不可用的功能。环境准备本指南面向 Python 侧的实现。使用虚拟服务器前需要安装 Python 包perspective-python提供perspective.virtual_servers模块与各 WebSocket handler按数据源安装对应的驱动包duckdb、clickhouse-connect或polars安装一个 Web 框架用于承载 WebSocket文档与示例主要使用 Tornado仓库同样提供了 Starlette 与 AIOHTTP 两种 handler。以 Tornado 为例的依赖组合pip install perspective-python duckdb tornado浏览器端则使用 JavaScript 客户端连接const websocket await perspective.websocket(ws://localhost:8080/websocket); const table await websocket.open_table(my_table); document.getElementById(viewer).load(table);perspective.websocket接受一个 WebSocket URLopen_table(name)通过该 socket 按名字请求托管在服务端的 Table。真正执行的是 Python 侧的 handler浏览器拿到的只是一个代理数据按需分片传输因此可以支撑远大于浏览器内存的庞大数据集。DuckDB Virtual ServerPerspective 为 DuckDB。安装pip install perspective-python duckdb用法暴露一个 DuckDB 数据库给浏览器客户端import duckdb import tornado.web import tornado.ioloop from perspective.virtual_servers.duckdb import DuckDBVirtualServer from perspective.handlers.tornado import PerspectiveTornadoHandler # Create DuckDB connection and load data conn duckdb.connect() conn.execute(CREATE TABLE my_table AS SELECT * FROM data.parquet) # Create virtual server backed by DuckDB server DuckDBVirtualServer(conn) # Serve over WebSocket app tornado.web.Application([ (r/websocket, PerspectiveTornadoHandler, {perspective_server: server}), ]) app.listen(8080) tornado.ioloop.IOLoop.current().start()浏览器端连接与渲染const websocket await perspective.websocket(ws://localhost:8080/websocket); const table await websocket.open_table(my_table); document.getElementById(viewer).load(table);实现要点从 DuckDB 类型到 Perspective 类型从源码看DuckDB 虚拟服务器由三部分组成见 rust/perspective-python/perspective/virtual_servers/duckdb.pyDuckDBVirtualServer会话工厂new_session(callback)为每个 WebSocket 连接创建一个DuckDBVirtualSessionDuckDBVirtualSession包装perspective.VirtualServer(DuckDBVirtualServerHandler(db))把客户端消息交给 handler 处理并把响应回调回去DuckDBVirtualServerHandler真正的翻译器内部使用GenericSQLVirtualServerModel生成 SQL例如table_make_view会把视图配置拼成CREATE TEMPORARY TABLE语句执行view_get_data则执行查询并把结果以 Arrow IPC 流写回客户端。值得注意的类型映射逻辑duckdb_type_to_pspDuckDB 的类型系统远比 Perspective 的六种视觉相关类型integer、float、boolean、date、datetime、string丰富因此需要显式转换。例如BIGINT及更宽的类型会映射为float因为 Perspective 的integer是 32 位与Int64 → Float64的强制转换保持一致TIME映射为datetime未知类型不会报错而是按string渲染并打一条 warning 日志。源码注释特别提醒该映射必须与perspective-client中coerce_column的 Arrow 类型强制转换逻辑保持一致否则会出现列声明为integer但数据被强转为Float64导致数值过滤器被引擎拒绝这类不一致问题。Window functions窗口函数窗口列直接使用 DuckDB 自身的函数名——对外宣告的名字会原样写入OVER子句。Perspective 的 DuckDB 虚拟服务器支持以下窗口函数来自 DuckDB 虚拟服务器文档源码中的完整注册表见 duckdb.py类别支持函数聚合Aggregatingsumavgcountminmaxproductmedian偏差/方差Deviation / variancestddev_sampstddev_popvar_sampvar_pop导航Navigationfirst_valuelast_valuenth_valuelaglead排名Rankingrow_numberrankdense_rankpercent_rankcume_distntilePerspective 自研diffrate几个从源码可以确认的实现细节每个窗口函数声明了frames接受的窗口框架类型rows、range、cumulative与result_type。由于 DuckDB 的count/排名函数返回BIGINT而 Perspective 的 32 位integer装不下这些函数的输出列类型统一声明为floatlag、lead、nth_value、ntile带offset参数ntile的参数是分桶数而非行偏移diff与rate是 Perspective 独有的窗口操作DuckDB 中没有对应原生函数SQL 翻译会基于lag和first_value合成实现对非数值类型字符串、布尔、日期等窗口函数只开放count/min/max、导航类与排名类函数因为算术聚合对它们无定义。完整可运行示例仓库中的 examples/python-duckdb-virtual/server.py 给出了一个更完整的实战形态它从superstore.parquet建表、同时托管静态文件并显式设置了websocket_max_message_size大数据集下务必调大db duckdb.connect(:memory:perspective) db.sql( f SET default_null_orderNULLS_FIRST_ON_ASC_LAST_ON_DESC; CREATE TABLE data_source_one AS SELECT * FROM {INPUT_FILE}; , ) virtual_server perspective.virtual_servers.duckdb.DuckDBVirtualServer(db) app tornado.web.Application( [ ( r/websocket, perspective.handlers.tornado.PerspectiveTornadoHandler, {perspective_server: virtual_server}, ), (r/node_modules/(.*), StaticFileHandler, {path: ../../node_modules/}), ( r/(.*), StaticFileHandler, {path: ./, default_filename: index.html}, ), ], websocket_max_message_size100 * 1024 * 1024, ) app.listen(3000)ClickHouse Virtual ServerPerspective 也为 ClickHouse。安装pip install perspective-python clickhouse-connect用法暴露 ClickHouse 表给浏览器客户端import clickhouse_connect import tornado.web import tornado.ioloop from perspective.virtual_servers.clickhouse import ClickhouseVirtualServer from perspective.handlers.tornado import PerspectiveTornadoHandler # Connect to ClickHouse client clickhouse_connect.get_client(hostlocalhost) # Create virtual server backed by ClickHouse server ClickhouseVirtualServer(client) # Serve over WebSocket app tornado.web.Application([ (r/websocket, PerspectiveTornadoHandler, {perspective_server: server}), ]) app.listen(8080) tornado.ioloop.IOLoop.current().start()浏览器端连接与渲染与 DuckDB 完全相同的协议仅数据源不同const websocket await perspective.websocket(ws://localhost:8080/websocket); const table await websocket.open_table(my_table); document.getElementById(viewer).load(table);实现说明与注意事项从 rust/perspective-python/perspective/virtual_servers/clickhouse.py 的源码可以看到ClickHouse 处理器与 DuckDB 处理器结构一致同样由ClickhouseVirtualServer会话工厂与ClickhouseVirtualServerHandlerSQL 翻译器组成其FILTER_OPS、聚合函数列表与 DuckDB 版本基本相同。不过源码注释中也明确指出一个值得注意的现状ClickHouse 处理器的窗口函数清单是从 DuckDB 处理器继承而来尚未针对真实 ClickHouse 做过审计。例如 ClickHouse 自己的导航函数是lagInFrame/leadInFrame排名函数集合也有差异这些都需要在对外宣告前进行验证。因此在使用窗口函数这类高级特性时建议以你的 ClickHouse 版本实际支持的 SQL 为准。Polars Virtual ServerPolars 虚拟服务器与前两者的不同之处在于它查询的是进程内的 Polars DataFrame内存数据而非外部数据库服务器。这也体现在构造方式上——传入的是一个{表名: DataFrame}字典。安装pip install perspective-python polars用法暴露 Polars DataFrames 给浏览器客户端import polars as pl import tornado.web import tornado.ioloop from perspective.virtual_servers.polars import PolarsVirtualServer from perspective.handlers.tornado import PerspectiveTornadoHandler # Load data into Polars DataFrames df pl.read_parquet(data.parquet) # Create virtual server backed by Polars (dict of name - DataFrame) server PolarsVirtualServer({my_table: df}) # Serve over WebSocket app tornado.web.Application([ (r/websocket, PerspectiveTornadoHandler, {perspective_server: server}), ]) app.listen(8080) tornado.ioloop.IOLoop.current().start()浏览器端连接与渲染const websocket await perspective.websocket(ws://localhost:8080/websocket); const table await websocket.open_table(my_table); document.getElementById(viewer).load(table);实现说明Polars 处理器不依赖GenericSQLVirtualServerModel而是在 Python 层面直接操作 DataFrame见 rust/perspective-python/perspective/virtual_servers/polars.pyget_hosted_tables()直接返回self.tables.keys()即构造时传入的字典键聚合通过AGG_MAP把 Perspective 的聚合名映射为 Polars 表达式方法例如avg/mean→e.mean()、first/any_value/arbitrary→e.first()处理器内部维护self.views临时视图缓存与self.view_schemas用于跟踪按视图配置生成的结果表及其 schema。实现自定义 Virtual Server内置的 DuckDB、ClickHouse、Polars 实现已经覆盖了常见场景但 Virtual Server 的真正价值在于可扩展性你可以通过继承VirtualServerHandler把 Perspective 连接到任意数据源。整体需要三块拼图完整指南见 实现自定义 Virtual Server一个继承VirtualServerHandler的 handler负责翻译查询用perspective.VirtualServer包装 handler让它拥有协议处理能力一个提供new_session(callback)的会话工厂对象WebSocket handler 用它为每个连接的客户端分配独立会话。第一步实现 handlerVirtualServerHandler从perspective.virtual_servers导入。接口定义见 rust/perspective-python/perspective/virtual_servers/init.py只有get_hosted_tables、table_schema、table_size、table_make_view、view_delete和view_get_data是必需的其余方法都有默认实现。from perspective.virtual_servers import VirtualServerHandler class MyHandler(VirtualServerHandler): def __init__(self, db): self.db db def get_features(self): return { group_by: True, split_by: False, sort: True, filter_ops: { string: [, !, contains], float: [, !, , ], }, aggregates: { float: [sum, avg, count], string: [count], }, } def get_hosted_tables(self): return [my_table] def table_schema(self, table_name): return {name: string, price: float} def table_size(self, table_name): return 1000 def table_make_view(self, table_name, view_name, config): # Translate config (group_by, sort, filter, etc.) into a query # against your data source. Store the query keyed by view_name # for later data retrieval. pass def view_delete(self, view_name): # Clean up resources for this view. The UI does this automatically, # and can recover if a view dies early. pass def view_get_data(self, view_name, config, viewport, data): # Serialize the rectangular slice viewport of the temporary table # view_name into data, a push-only VirtualDataSlice. Once a # type has been pushed for a column name it must not change. pass可选方法默认行为与用途方法默认值用途get_features()仅columns声明支持哪些 UI 控件——见下文 Features 声明view_schema(view_name, config)复用table_schema临时表视图的 schema当它与源表不同时覆写view_size(view_name)复用table_size临时表的行数当它与源表不同时覆写table_validate_expression(view_name, expression)允许所有表达式对表达式列做类型检查需在get_features中开启expressionsview_get_min_max(view_name, column_name, config)不支持返回列值的(min, max)元组——渐变gradient与 sparkbar 列样式的前置条件第二步会话工厂WebSocket handler 每次建立连接都会调用new_session(callback)一次因此作为perspective_server传入的对象必须提供这个方法。标准做法是用perspective.VirtualServer包装你的 handler它负责协议编解码并为每个客户端返回一个会话import perspective class MyVirtualSession: def __init__(self, callback, db): self.session perspective.VirtualServer(MyHandler(db)) self.callback callback def handle_request(self, msg): self.callback(self.session.handle_request(msg)) class MyVirtualServer: def __init__(self, db): self.db db def new_session(self, callback): return MyVirtualSession(callback, self.db)第三步挂载到 Web 框架MyVirtualServer实例可以像普通Server一样传给 Tornado、Starlette 或 AIOHTTP 的 handler。以 Tornado 为例from perspective.handlers.tornado import PerspectiveTornadoHandler app tornado.web.Application([ (r/websocket, PerspectiveTornadoHandler, { perspective_server: MyVirtualServer(db), }), ])内置的 DuckDB、ClickHouse、Polars 三个实现完全遵循这一结构会话工厂 →VirtualServer包装 handler → 传入 WebSocket handler是最佳的学习参考。Features 声明控制 UI 与查询能力get_features()返回的对象告诉 Perspective 这个虚拟服务器支持哪些查询操作viewer 只会展示受支持操作的控件详见 Virtual Servers 概念页 的 Features 声明小节。字段定义如下字段类型说明group_bybool是否支持 group-by 聚合split_bybool是否支持 split-by透视/行列转置sortbool是否支持排序expressionsbool是否支持计算表达式filter_opsdict列类型 → 支持的过滤运算符列表aggregatesdict列类型 → 支持的聚合函数列表on_updatebool是否支持更新回调设置group_by: False不仅会隐藏Group ByUI 控件还会阻止该字段出现在后续传给table_make_view的config字典中——也就是说 features 声明直接约束了 handler 实际会收到的查询配置形态。DuckDB 处理器还额外声明了group_rollup_mode[rollup, flat, total]与split_rollup_mode[flat, rollup]Polars 处理器则只声明了group_rollup_mode。WebSocket 安全与传输限制PerspectiveTornadoHandler的源码文档见 rust/perspective-python/perspective/handlers/tornado.py明确提示了几点实操注意事项大消息处理大数据集时可能需要调大tornado.web.Application的websocket_max_message_size示例中设置为 100 MB并可通过 handler 的max_buffer_size参数调整缓冲上限check_origin路由配置中可传check_origin: True决定是否接受来自其他 origin 的 WebSocket 请求注意该 handler 默认的check_origin直接返回True安全边界PerspectiveTornadoHandler是参考实现没有内置认证、授权、origin 强制校验或限流不应直接暴露到不受信任的网络生产部署必须在更外层补齐安全措施。参考资源概念详解Virtual Servers 架构与工作原理内置实现DuckDB · ClickHouse · Polars自定义实现实现自定义 Virtual Server接口定义rust/perspective-python/perspective/virtual_servers/init.py内置实现源码duckdb.py · clickhouse.py · polars.pyWebSocket handlerrust/perspective-python/perspective/handlers/tornado.py完整可运行示例python-duckdb-virtual/server.py · python-clickhouse-virtual/server.py · python-polars-virtual/server.py【免费下载链接】perspectiveA data visualization and analytics component, especially well-suited for large and/or streaming datasets.项目地址: https://gitcode.com/GitHub_Trending/pe/perspective创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考