Superset如何拼接SQL并执行(下)

在上半章之中,我们看了下Superset如何利用入参中的参数拼接SQL。

在下半章之中,我们会查看Superset如何将不同类型的图表封装为一个框架。

这里我们以Bar图为例,查看下如何实现的

涉及到的框架相关的接口是http://localhost:9000/superset/explore_json/

对应的代码在core.py文件中explore_json函数中

其中主要的入口在

viz_obj = get_viz(

datasource_type=cast(str, datasource_type),

datasource_id=datasource_id,

form_data=form_data,

force_cached=True,

force=force,

)

payload = viz_obj.get_payload()

获取到查询结果。

在get_viz函数其中则是根据图表类型,获取到BaseViz的子类,初始化类对象返回

def get_viz(

form_data: FormData,

datasource_type: str,

datasource_id: int,

force: bool = False,

force_cached: bool = False,

) -> BaseViz:

viz_type = form_data.get(“viz_type”, “table”)

datasource = DatasourceDAO.get_datasource(

db.session,

DatasourceType(datasource_type),

datasource_id,

)

viz_obj = viz.viz_types[viz_type](

datasource, form_data=form_data, force=force, force_cached=force_cached

)

return viz_obj

viz.viz_types 则是所有BaseViz的子类

def get_subclasses(cls: Type[BaseViz]) -> Set[Type[BaseViz]]:

return set(cls.__subclasses__()).union(

[sc for c in cls.__subclasses__() for sc in get_subclasses(c)]

)

viz_types = {

o.viz_type: o

for o in get_subclasses(BaseViz)

if o.viz_type not in config[“VIZ_TYPE_DENYLIST”]

}

主要的查询接口在get_payload函数中,而这个函数位于BaseViz类中,方便继承。

在get_payload函数中,主要核心的代码则是调用本类中的get_df_payload函数

payload = self.get_df_payload(query_obj)

在get_df_payload之中,则是首先拼接 query_obj对象

然后拼接cache_key,查看缓存。

当获取不到缓存的时候,利用get_df接口进行查询,并在查询之后放入缓存。

我们贴上述的相关代码。

if not query_obj:

query_obj = self.query_obj()

if cache_key and cache_manager.data_cache and not self.force:

cache_value = cache_manager.data_cache.get(cache_key)

if cache_value:

stats_logger.incr(“loading_from_cache”)

try:

df = cache_value[“df”]

self.query = cache_value[“query”]

self.applied_template_filters = cache_value.get(

“applied_template_filters”, []

)

self.status = QueryStatus.SUCCESS

is_loaded = True

stats_logger.incr(“loaded_from_cache”)

except Exception as ex: # pylint: disable=broad-except

logger.exception(ex)

logger.error(

“Error reading cache: %s”,

utils.error_msg_from_exception(ex),

exc_info=True,

)

logger.info(“Serving from cache”)

df = self.get_df(query_obj)

if self.status != QueryStatus.FAILED:

stats_logger.incr(“loaded_from_source”)

if not self.force:

stats_logger.incr(“loaded_from_source_without_force”)

is_loaded = True

对于query_obj函数,是类似拼接query_context的函数,在父类中分别获取到了groupby,metics,时间字段,时间相关filter,order by,limit等信息,最后拼接为一个dict返回

这个函数也可以被子类进行继承,诸如在Bar图之中,子类实现的query_obj 除了调用父类接口之外,还进行了自己校验逻辑的封装

query_obj = super().query_obj()

if len(query_obj[“groupby”]) < len(self.form_data.get(“groupby”) or []) + len(

self.form_data.get(“columns”) or []

):

raise QueryObjectValidationError(

_(“Can’t have overlap between Series and Breakdowns”)

)

if not self.form_data.get(“metrics”):

raise QueryObjectValidationError(_(“Pick at least one metric”))

if not self.form_data.get(“groupby”):

raise QueryObjectValidationError(_(“Pick at least one field for [Series]”))

然后到了get_df函数之中

则是直接调用了数据集相关的datasource的query函数进行查询后返回

self.results = self.datasource.query(query_obj)

这里的query函数,我们在上一节中讲解过了,这里不再赘述。

在获取到了df之后,就可以进行返回。

如果想要对df进行一些后置处理操作,比如bar图中的breakdown操作,就可以实现get_data函数。

if self.status != QueryStatus.FAILED:

payload[“data”] = self.get_data(df)

那么从上面框架中可以看出

如果我们想要增加一个属于自己的chart type,可以在这个框架之中。通过继承BaseViz,声明自己的viz_type

并可以进行客制化操作,诸如实现query_obj 函数修改,get_df函数修改查询逻辑。

cache_key函数修改缓存生成。 get_data修改查询后的dataframe格式。

发表评论

邮箱地址不会被公开。 必填项已用*标注