166. 精读《BI 搭建 - 筛选条件》

2023-10-09 09:10
文章标签 筛选 条件 搭建 精读 bi 166

本文主要是介绍166. 精读《BI 搭建 - 筛选条件》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

筛选条件是 BI 搭建的核心概念,我们大部分所说的探索式分析、图表联动也都属于筛选条件的范畴,其本质就是一个组件对另一个组件的数据查询起到筛选作用

筛选组件是如何作用的

我们最常见的筛选条件就是表单场景的查询控件,如下图所示:

若干 “具有输出能力” 的组件作为筛选组件,点击查询按钮时触发其作用组件重新取数。

注意这里 “具有输出能力” 的组件不仅是输入框等具有输入性质的组件,其实所有具备交互能力的组件都可以,甚至可以由普通组件承担筛选触发的能力:

一个表格的表头点击也可以触发筛选行为,或者柱状图的一个柱子被点击都可以,只要进行到这层抽象,组件间联动本质也属于筛选行为

同样重要的,筛选作用的组件也可以是具备输入能力的组件:

当目标组件是具备筛选能力组件时,这就是筛选联动场景了,所以 筛选联动也属于普通筛选行为。至于目标组件触发取数后,是否立即修改其筛选值,进而触发后续的筛选联动,就完全由业务特性决定了。

一个组件也可以自己联动自己筛选,比如折线图点击下钻的场景,就是自己触发了筛选,作用到自己的例子。

什么是筛选组件

任何组件都可以是筛选组件

可能最容易理解的是输入框、下拉框、日期选择器等具备输入特征的组件,这些组件只能说天然适合作为筛选组件,但不代表系统设计要为这些组件特殊处理。

扩大想一想,其实普通的按钮、表格、折线图等等 具有展示属性的组件也具有输入特性的一面,比如按钮被点击时触发查询、单元格被点击时想查询当前城市的数据趋势、折线图某条线被点击时希望自身从年下钻到月等等。

所以 不存在筛选组件这概念,而是任何组件都具有筛选的能力,因此筛选是一种任何组件都具有的能力,而不局限在某几个组件上,一旦这么设计,可以做到以下几点:

  1. 实现输入类组件到展示类组件的筛选,符合基本筛选诉求。

  2. 实现展示类组件到展示类组件的筛选,属于图表联动图表的高级功能。

  3. 实现输入类组件到输入类组件的筛选,属于筛选联动功能。

  4. 实现组件自身到自身的筛选,实现下钻功能。

下面介绍 bi-designer 的筛选条件设计。

筛选条件设计

基于上述分析,bi-designer 在组件元信息中没有增加所谓的筛选组件类型,而是将其设定为一种筛选能力,任何组件都能触发。

如何触发筛选

组件调用 onFilterChange 即可完成筛选动作:

import { useDesigner } from "@alife/bi-designer";const InputFilter = () => {const { onFilterChange } = useDesigner();return (<input onChange={(event) => () => onFilterChange(event.target.value)} />);
};

但这种开发方式违背了 低侵入 的设计理念,我们可以采用组件与引擎解构的方式,让输入框变更的时候直接调用 props.onChange ,这个组件保持了最大的独立性:

const InputFilter = ({ onChange }) => {return <input onChange={(event) => () => onChange(event.target.value)} />;
};

那渲染引擎怎么将 onFilterChange 映射到 props.onChange 呢?如下配置 DSL 即可:

{"props": {"onChange": {"type": "JSExpression","value": "this.onFilterChange"}}
}

筛选影响哪些组件

一般筛选组件会选择作用于的目标组件,类似下图:

这些信息会存储在筛选组件的组件配置中,即 componentInstance.props,筛选目标组件在 componentMeta.eventConfigs 组件元信息的事件中配置:

import { Interfaces } from "@alife/bi-designer";const componentMeta: Interfaces.ComponentMeta = {eventConfigs: ({ componentInstance }) =>componentInstance.props.targets?.map((target) => ({// 筛选取数type: "filterFetch",// 触发组件source: componentInstance.id,// 作用组件target: target.id,})),
};

如上所示,假设作用于组件存储在 props.targets 字段中,我们将其 map 一下都设置为 filterFetch 类型,表示筛选作用,source 触发源是自己,target 目标组件是存储的 target.id

这样当 source 组件调用了 onFilterChangetarget 组件就会触发取数,并在取数参数中拿到作用于其的筛选组件信息与筛选值。

组件如何感知筛选条件

组件取数是结合了筛选条件一起的,只要如上设置了 filterFetch,渲染引擎会自动在计算取数参数的回调函数 getFetchParam 中添加 filters 代表筛选组件信息,组件可以结合自身 componentInstancefilters 推导出最终取数参数:

最终,组件元信息只要写一个 getFetchParam 回调函数即可,可以自动拿到作用于它的筛选组件,而不用关心是哪些配置导致了关联,只要响应式的去处理筛选作用即可

import { Interfaces } from "@alife/bi-designer";const componentMeta: Interfaces.ComponentMeta = {// 组装取数参数getFetchParam: ({ componentInstance, filters }) => {// 结合 componentInstance 与 filters.map... 返回取数参数},
};

筛选组件间联动带来的频繁取数问题

对于筛选联动的复杂场景,会遇到频繁取数的问题。

假设国家、省、市三级联动筛选条件同时 filterFetch 作用于一个表格,这个表格取数的筛选条件需要同时包含国家、省、市三个参数,但我们又设置了 国家、省、市 这三个筛选组件之间的 filterFetch 作为筛选联动,那么国家切换后、省改变、联动市改变,这个过程筛选值会变化三次,但我们只想表格组件取数函数仅执行最后的一次,怎么办呢?

如上图所示,其实每个筛选条件在渲染引擎数据流中还存储了一个 ready 状态,表示筛选条件是否就绪,一个组件关联的筛选条件只要有一个 ready 不为 true,组件就不会触发取数

因此我们需要在筛选变化的过程中,总是保证一个筛选组件的 readyfalse,等筛选间联动完毕了,所有筛选器的 readytrue,组件才会取数,我们可以使用 filterReady 筛选依赖配置:

import { Interfaces, createComponentInstancesArray } from "@alife/bi-designer";const componentMeta: Interfaces.ComponentMeta = {eventConfigs: ({ componentInstance }) =>componentInstance.props.targets?.map((target) => ({// 筛选就绪依赖type: "filterReady",// 触发组件source: componentInstance.id,// 作用组件target: target.id,})),
};

这样配置后,当 source 组件触发 onFilterChange 后,target 组件的筛选 ready 会立即设置为 false,只有 target 组件取完数后主动触发 onFilterChange 才会将自己的 ready 重新置为 trueThat'a all,其他流程没有任何感知

若干筛选组件聚合成一个查询控件

除了联动外,也会存在防止频繁查询的诉求,希望将多个筛选条件绑定成一个大筛选组件,在点击 “查询” 按钮时再取数:

可以利用 筛选作用域 轻松实现此功能,只需要两步:

筛选组件设置独立筛选作用域

import { Interfaces } from "@alife/bi-designer";const componentMeta: Interfaces.ComponentMeta = {// 通过 componentInstance 判断,如果是全局筛选器内部,则设置 filterScopefilterScope: ({ componentInstance }) => ["my-custom-scope-name"],
};

这样,这批筛选组件就与其作用的组件属于不同的 筛选作用域 了,所以筛选不会对其立即生效,功能实现了一半。

确认按钮点击时调用 submitFilterScope

import { useDesigner } from '@alife/bi-designer'const componentMeta: Interfaces.ComponentMeta = {const { submitFilterScope } = useDesigner()// 点击确认按钮时,调用 submitFilterScope('my-custom-scope-name')
};

你可以在点击查询按钮后调用 submitFilterScope 并传入对应作用域名称,这样作用域内筛选组件就会立即对其 target 组件生效了。

至于确认按钮、UI 上的聚合,这些你可以写一个自定义组件去做,利用 ComponentLoader 把筛选组件聚合到一起加载,总之功能与 UI 是解耦的。

如果你对原理感兴趣,可以再多看一下这张图:

突破筛选作用域

然而实际场景中,可能存在更复杂的组合,见下面的例子:

筛选器 1 同时对 筛选器 2、表格 产生筛选作用 filterFetch,但对 表格 的作用希望通过查询按钮拦截住,而对 筛选器 2 的作用希望能立即生效,对于这个例子有两种方式解决:

最简单的方式就是将 筛选器 1、筛选器 2 设置为相同作用域 group1,这样就通过作用域分割自然实现了效果,而且这本质上是两个筛选器 UI 不在一起,但筛选作用域相同的例子

但是再变化一下,如果筛选器 2 也对表格产生筛选作用,那我们将 筛选器 1、筛选器 2 放入同一个 group1 等于对表格的查询都会受到 “查询” 按钮的控制,但 我们又希望筛选器 2 可以立即作用于表格

如图所示,我们只能将 筛选器 1 的筛选作用域设置为 group1,这样 筛选器 2 与 表格 属于同一个筛选作用域,他们之间筛选会立即生效,我们只要解决 筛选器 1 不能立即作用于 筛选器 2 的问题即可,可以通过 ignoreFilterScope 方式突破筛选作用域:

import { Interfaces } from "@alife/bi-designer";const componentMeta: Interfaces.ComponentMeta = {eventConfigs: ({ componentInstance }) =>componentInstance.props.targets?.map((target) => ({// 筛选取数type: "filterFetch",// 触发组件source: componentInstance.id,// 作用组件target: target.id,// 突破筛选作用域ignoreFilterFetch: true,})),
};

我们只要在 source: 筛选器1 target: 筛选器2filterFetch 配置中,将 ignoreFilterFetch 设置为 true,这个 filterFetch 就会忽略筛选作用域,实现立即 筛选器 1 立即作用到 筛选器 2 的效果。

总结

你还有哪些特殊的筛选诉求?可以用这套筛选设计解决吗?

这篇关于166. 精读《BI 搭建 - 筛选条件》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/ascoders/article/details/108591255
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/171811

相关文章

SpringBoot快速搭建TCP服务端和客户端全过程

《SpringBoot快速搭建TCP服务端和客户端全过程》:本文主要介绍SpringBoot快速搭建TCP服务端和客户端全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录TCPServerTCPClient总结由于工作需要,研究了SpringBoot搭建TCP通信的过程

Gradle下如何搭建SpringCloud分布式环境

《Gradle下如何搭建SpringCloud分布式环境》:本文主要介绍Gradle下如何搭建SpringCloud分布式环境问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录Gradle下搭建SpringCloud分布式环境1.idea配置好gradle2.创建一个空的gr

Linux搭建单机MySQL8.0.26版本的操作方法

《Linux搭建单机MySQL8.0.26版本的操作方法》:本文主要介绍Linux搭建单机MySQL8.0.26版本的操作方法,本文通过图文并茂的形式给大家讲解的非常详细,感兴趣的朋友一起看看吧... 目录概述环境信息数据库服务安装步骤下载前置依赖服务下载方式一:进入官网下载,并上传到宿主机中,适合离线环境

Java中Switch Case多个条件处理方法举例

《Java中SwitchCase多个条件处理方法举例》Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,:本文主要介绍Java中SwitchCase多个条件处理的相... 目录前言基本语法处理多个条件示例1:合并相同代码的多个case示例2:通过字符串合并多个case进阶用法使用

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

MySQL双主搭建+keepalived高可用的实现

《MySQL双主搭建+keepalived高可用的实现》本文主要介绍了MySQL双主搭建+keepalived高可用的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、测试环境准备二、主从搭建1.创建复制用户2.创建复制关系3.开启复制,确认复制是否成功4.同

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

使用DeepSeek搭建个人知识库(在笔记本电脑上)

《使用DeepSeek搭建个人知识库(在笔记本电脑上)》本文介绍了如何在笔记本电脑上使用DeepSeek和开源工具搭建个人知识库,通过安装DeepSeek和RAGFlow,并使用CherryStudi... 目录部署环境软件清单安装DeepSeek安装Cherry Studio安装RAGFlow设置知识库总