Fiftyone Enterprise Query Performance 实战:借助数据库索引与 Summary Fields 加速大规模数据集查询
【免费下载链接】fiftyoneRefine high-quality datasets and visual AI models项目地址: https://gitcode.com/GitHub_Trending/fi/fiftyone
Query Performance 是 FiftyOne Enterprise App(2.2.0 及以上版本)内置的查询加速能力,它通过自动利用数据库(MongoDB)索引,显著优化侧边栏过滤、计数与排序等操作在大规模数据集上的执行效率。本文以仓库中的官方文档 query_performance.rst 为主体,结合 collections.py、dataset.py、config.py 等源码实现,系统讲解索引与摘要字段(Summary Fields)的创建、更新、删除、禁用,以及企业级部署下的全局配置方案。读完本文,你将掌握在 FiftyOne Enterprise 中为超大数据集配置查询加速的完整实战路径。
Query Performance 是什么
Query Performance 是 FiftyOne Enterprise App 的内置功能,其核心思路是利用数据库索引来优化大规模数据集上的查询。当你在 App 侧边栏中对字段执行过滤、排序或查看计数时,如果底层数据库有对应的索引,查询就能走索引快速路径(源码注释中称之为 "Mongo-side fast path",见 queryPerformance.ts),避免全表扫描带来的性能瓶颈。
从源码结构看,该功能由前后端协同实现:后端负责索引与摘要字段的管理(SampleCollection.create_index、Dataset.create_summary_field等 API),前端负责将索引状态可视化(侧边栏闪电图标、Query Performance 面板、toast 提示)。
侧边栏的闪电图标:一眼识别已索引字段
App 的侧边栏被优化为"尽可能利用数据库索引"。凡是已建立索引的字段/属性,其名称旁边会显示闪电图标(lightning bolt icon),你可以据此快速判断哪些字段的过滤是"索引加速"的。
前端实现中,闪电图标的渲染逻辑位于 use-query-performance-icon.tsx:当queryPerformance开启、且该路径存在索引(或属于已过滤的索引字段)时,就显示闪电图标。上面这张 GIF 展示的正是 BDD100K 数据集 train split 上、对detections.detections.label字段建立索引后的效果(BDD100K 数据集见 bdd100k.rst)。
多字段过滤的黄金法则:当按多个字段过滤时,把第一个过滤条件放在已索引字段上,查询效率最高。因为索引可以快速收缩结果集,后续对未索引字段的过滤只需在较小的结果集上进行。
慢查询 toast 通知:系统自动提示你创建索引
当你执行了一个本可以从索引获益、但耗时超过数秒的过滤时,App 会弹出一条 toast 通知,提示你采取适当的优化动作:
点击"Create Index"会直接打开 Query Performance 面板,并预配置好一条索引或摘要字段的推荐,你只需确认并执行即可。
toast 的触发与展示逻辑见 QueryPerformanceToast.tsx:前端通过queryperformance事件携带path与isFrameField信息触发提示(QP_WAIT = 5151ms 为触发阈值常量),toast 展示时长为SHOWN_FOR = 10000ms。点击"View Documentation"等操作会打开对应的官方文档链接。
注意:点击 "Dismiss"(忽略)后,本次 App 会话期间该提示将不再出现。实现上它通过
sessionStorage持久化"隐藏"状态(见 QueryPerformanceToast.tsx),因此只对当前浏览器会话生效。
Query Performance 面板
Query Performance 面板是管理索引与摘要字段的统一入口,有两种手动打开方式:
- 点击Samples 标签页旁边的 "+" 图标;
- 点击侧边栏右上角的黄色闪电按钮(见 qp_tooltip.png 所示的入口提示)。
首次打开面板时,你会看到一个欢迎页;一旦你为数据集创建了至少一个自定义索引或摘要字段,面板就会切换为索引/摘要字段列表视图,展示当前数据集上已存在的全部索引与摘要字段(见 qp_home.png 欢迎页):
创建索引(Indexes)
点击面板右上角的Create Index按钮,即可随时为字段创建新索引。执行"Execute"后索引开始构建,面板摘要表中会显示"In progress"状态。索引创建完成后:
- 该字段在侧边栏中会出现闪电图标;
- 展开该字段的过滤组件执行查询时,速度会有明显提升。
创建索引的警告
警告:对于大型数据集,索引构建期间会对数据库性能产生显著影响。建议只索引你确实需要执行初始过滤的字段,并且在同时创建多个索引前先与部署管理员沟通确认。
通过 SDK 创建与管理索引
面板操作之外,你也可以通过 SDK 以编程方式管理索引。相关方法定义在 collections.py(SampleCollection类):
list_indexes()— 列出集合上的全部索引名(L10940);get_index_information(include_stats=False)— 获取索引信息字典,include_stats=True时额外包含每个索引的size(占用空间)、accesses(访问统计)与in_progress(构建状态),数据来自 MongoDB 的$indexStats聚合与collStats(L10953);create_index(field_or_spec, unique=False, force=False, wait=True, **kwargs)— 创建索引,支持embedded.field.name形式的嵌入字段路径;unique控制唯一性约束,force控制是否转换/替换已有索引(L11030);drop_index(field_or_name)— 删除已有索引。
官方用户指南 app.rst 的 Optimizing Query Performance 小节给出了完整的实战示例,例如对 BDD100K 数据集建索引并启动 App:
import fiftyone as fo import fiftyone.zoo as foz # 手动下载好的 BDD100K 源文件路径 source_dir = "/path/to/dir-with-bdd100k-files" dataset = foz.load_zoo_dataset( "bdd100k", split="train", source_dir=source_dir, ) dataset.create_index("detections.detections.label") session = fo.launch_app(dataset)以及针对多字段场景的最佳实践:只索引你打算做初始过滤的字段,并在添加样本之前声明索引(构建更快):
import fiftyone as fo dataset = fo.Dataset() # 索引顶层标量字段 dataset.create_index("camera_id") dataset.create_index("recorded_at") dataset.create_index("annotated_at") dataset.create_index("annotated_by") # 索引嵌入文档字段 dataset.create_index("ground_truth.detections.label") dataset.create_index("ground_truth.detections.confidence") # 注意:在添加样本前声明索引更快 dataset.add_samples(...) session = fo.launch_app(dataset)进阶索引策略:复合索引、通配索引与分组数据集
针对更复杂的场景,用户指南还给出了三类进阶方案:
复合索引(Compound Indexes):当需要多个过滤条件才能把结果集收缩到足够小时,复合索引非常有用。例如在 3000 万样本的数据集上执行"先按ground_truth.label过滤,再按clip_pred.label过滤,最后按created_at过滤并排序"的多阶段操作:
dataset = fo.load_dataset("big-image-ds") dataset.create_index( [("ground_truth.label", 1), ("clip_pred.label", 1), ("created_at", 1)] )需要注意的是:被复合索引覆盖的字段,其闪电图标会以实心橙色高亮,表示即使当前匹配结果很多,按这些字段过滤依然高效(见 app.rst)。
分组数据集(Grouped Datasets):对于分组数据集,应对每个需要过滤的字段创建以 group slice 名结尾的复合索引(字段本身 +group.name),以保证按活动 slice 匹配时网格结果与计数的性能:
dataset.create_index([("ground_truth.detections.label", 1), ("group.name", 1)])通配索引(Wildcard Indexes):字段较少的数据库可以用create_index("$**")一次性索引全部字段,或用create_index("ground_truth.detections.$**")索引某个嵌入文档的所有属性。但要注意:字段很多的大数据集使用全局通配索引会消耗大量内存、且性能可能劣于选择性索引;数值字段过滤器也不被通配索引支持(见 app.rst)。
创建摘要字段(Summary Fields)
Query Performance 面板还支持创建摘要字段:这是一种 sample 级别的字段,用于在直接查询底层字段因对象/帧数量过多而极慢的场景下高效执行查询。
典型场景:在视频数据集中查找"至少某一帧包含某类目标(如person)"的样本。如果直接过滤帧级字段,需要扫描海量帧数据;而摘要字段把"该样本所有帧中出现的唯一值/数值范围"预先汇总到 sample 级,查询就变成了对少量 sample 记录的索引检索。
点击面板右上角Create Index按钮,在弹窗中选择"Summary field"类型即可创建(见 qp_create_summary_field.png)。
警告:对于大型数据集,创建摘要字段可能需要几分钟时间。
通过 SDK 创建摘要字段
SDK 提供了完整的摘要字段管理 API,定义在 dataset.py 的Dataset类中:
list_summary_fields()— 列出数据集上的全部摘要字段(L2662);create_summary_field(path, field_name=None, sidebar_group=None, include_counts=False, group_by=None, read_only=True, create_index=True)— 创建摘要字段(L2687);update_summary_field(field_name)— 依据源字段当前值更新摘要字段(L3116);delete_summary_field(field_name, error_level=0)/delete_summary_fields(field_names, error_level=0)— 删除摘要字段。
create_summary_field的核心参数说明:
| 参数 | 默认值 | 说明 |
|---|---|---|
path | 必填 | 源字段路径,如frames.detections.detections.label |
field_name | 自动派生 | 存放摘要数据的 sample 级字段名,默认由path推导 |
sidebar_group | "summaries" | 摘要字段在 App 侧边栏所属的分组名;传False跳过分组修改 |
include_counts | False | 分类字段是否记录每个取值的计数(生成label+count两个子字段) |
group_by | None | 数值字段按某属性分组,生成逐属性的[min, max]区间 |
read_only | True | 是否将摘要字段标记为只读 |
create_index | True | 是否为摘要字段创建数据库索引 |
从源码看,摘要字段的类型由源字段决定(dataset.py L2762-L2792):字符串、布尔、ObjectId 等字段生成categorical摘要;浮点、整型、日期、时间字段生成numeric摘要;而 List 字段及未声明/不存在的字段会直接抛出ValueError。摘要字段的元信息(源路径、字段类型、是否含计数、最后修改时间等)通过字段的info字典中的_SUMMARY_FIELD_KEY标记保存(见 dataset.py L2797-L2821)。
源码 docstring 中的完整示例(dataset.py L2708-L2737):
import fiftyone as fo import fiftyone.zoo as foz from fiftyone import ViewField as F dataset = foz.load_zoo_dataset("quickstart-video") dataset.set_field("frames.detections.detections.confidence", F.rand()).save() # 为物体标签生成摘要字段 dataset.create_summary_field("frames.detections.detections.label") # 为置信度生成 [min, max] 摘要字段 dataset.create_summary_field("frames.detections.detections.confidence") # 生成标签摘要并附带计数 dataset.create_summary_field( "frames.detections.detections.label", field_name="frames_detections_label2", include_counts=True, ) # 生成按标签分组的置信度 [min, max] 摘要 dataset.create_summary_field( "frames.detections.detections.confidence", field_name="frames_detections_confidence2", group_by="label", ) print(dataset.list_summary_fields())更新摘要字段
由于摘要字段派生自其他字段的内容,只要源字段发生了修改,就必须更新摘要字段以保持同步。在面板的摘要字段操作列中点击更新图标,会打开一个弹窗,弹窗会根据数据集最近的变更情况给出"是否需要更新"的指导。
SDK 侧对应update_summary_field(field_name):它先做一次硬重载(避免连续更新多个摘要字段时出现 "weakly-referenced object no longer exists" 错误),校验字段确为摘要字段后,更新last_modified_at时间戳并重新填充摘要数据(见 dataset.py L3116-L3137)。
删除索引与摘要字段
点击面板操作列中对应条目的垃圾桶图标,即可删除自定义索引或摘要字段。SDK 侧对应drop_index(field_or_name)与delete_summary_field(field_name, error_level);其中error_level支持 0(无法删除时抛错)、1(记录警告)、2(静默忽略)三种取值(见 dataset.py L3139-L3163)。
禁用 Query Performance
Query Performance 默认对所有数据集开启,这也是大型数据集的推荐配置,以保证查询性能。但在某些场景下你可能希望关闭它:关闭后侧边栏会额外显示标签/取值计数等有用但计算代价更高的信息。
用户级开关(当前浏览器会话内生效)
有两种方式可以在浏览器中按数据集开关 Query Performance:
- 通过Samples 面板操作行中的齿轮图标,可对当前数据集在其生命周期内(当前浏览器中)启用/禁用(见 app-query-performance-disabled.gif);
- 通过Query Performance 面板右上角的 status 按钮(见 qp_config.png)。
从前端实现看,该开关按数据集 ID 持久化在sessionStorage中(queryPerformance-${datasetId}),刷新页面后依然保留,但关闭浏览器标签即失效(见 queryPerformance.ts L515-L524)。同时,前端还有两个自动降级逻辑值得注意(queryPerformance.ts L476-L491):
- 当数据集包含Mongo sample collection 之外的字段(如多模态数据集)时,Query Performance 会被强制关闭,回退到标准聚合路径;
- 当当前视图不是query performant view(即视图包含不受支持的 view stage)时,闪电快路径同样不会生效。
部署级全局配置(环境变量)
部署管理员可通过以下两个环境变量配置 Query Performance 的全局行为,它们在 config.py 的FiftyOneAppConfig中解析:
# 对所有新建数据集默认禁用 Query Performance FIFTYONE_APP_DEFAULT_QUERY_PERFORMANCE=false# 对所有用户完全禁用 Query Performance FIFTYONE_APP_ENABLE_QUERY_PERFORMANCE=false从源码可以看到这两个配置项的默认值均为true:
enable_query_performance(FIFTYONE_APP_ENABLE_QUERY_PERFORMANCE,默认True):功能总开关,关闭后前端queryPerformanceSetting直接返回false,所有数据集都无法使用该功能(config.py L434-L439、queryPerformance.ts L493-L513);default_query_performance(FIFTYONE_APP_DEFAULT_QUERY_PERFORMANCE,默认True):决定新建数据集的默认开启状态;用户未手动设置时,前端取该值作为回退(config.py L440-L445、queryPerformance.ts L471-L474)。
另外,FIFTYONE_APP_MAX_QUERY_TIME(默认 60)控制 App 的查询超时上限,与慢查询提示的触发逻辑同属 App 查询性能相关配置(见 config.py L452-L457)。
附:Query Performant View Stages
最后补充一个与索引是否生效密切相关的知识点:除了完整数据集,当你给视图添加以下 view stage 时,Query Performance 仍然保持激活(侧边栏继续显示闪电图标):
ExcludeFields/SelectFields:只要字段仍存在于 schema 中,索引性能就适用;SelectGroupSlices:查询可走快速路径,当展平视图包含全部分片时性能最优;GroupBy:当提供了order_by与order_by_key值,且在group_by与order_by字段上存在带唯一约束的复合索引、同时至少有一个以order_by字段开头的索引时,该 stage 即 query performant。
典型的"视频转分组数据集"配置示例(见 app.rst L695-L725):
import fiftyone as fo dataset = fo.load_dataset("frames-as-video") dataset.create_index([("video", 1), ("frame_number", 1)], unique=True) # 为过滤与排序创建 query performant 字段 dataset.create_index([("frame_number", 1), ("created_at", 1)]) dataset.create_index([("frame_number", 1), ("last_modified_at", 1)]) # 创建 "video" 视图并保存 videos = dataset.group_by( "video", order_by="frame_number", order_by_key=1, create_index=False ) dataset.save_view("videos", videos)此时侧边栏过滤基于每个分组的order_by_key样本(即上例中frame_number == 1的样本),分组级元数据存储在该 key 样本上,从而在大数据集上实现高效过滤。
【免费下载链接】fiftyoneRefine high-quality datasets and visual AI models项目地址: https://gitcode.com/GitHub_Trending/fi/fiftyone
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考