cube.js 上下文实践的一些说明

cube.js 提供了比较多的上下问支持,SECRUITY_CONTEXT,COMPILE_CONTEXT,FILTER_PARAMS,SQL_UTILS
但是在使用的时候可能会有好多问题,主要是原因是cube.js 对于编译的cache 以及不同context 的声明周期不一样

SECURITY_CONTEXT 的使用

推荐基于filter 模式使用,因为cube.js 对于schema 的编译是基于独立vm 处理的,而且就没有暴露相关的全局对象
只暴露的,所以不能直接引用,不然会报错的
参考几个定义

 
const CONTEXT_SYMBOLS = {
  USER_CONTEXT: 'securityContext',
  SECURITY_CONTEXT: 'securityContext',
  FILTER_PARAMS: 'filterParams',
  SQL_UTILS: 'sqlUtils'
};

参考compiler 处理

vm.runInNewContext(file.content, {
        view: (name, cube) => cubes.push(Object.assign({}, cube, { name, fileName: file.fileName })),
        cube:
          (name, cube) => (
            !cube ?
              this.cubeFactory({ ...name, fileName: file.fileName }) :
              cubes.push(Object.assign({}, cube, { name, fileName: file.fileName }))
          ),
        context: (name, context) => contexts.push(Object.assign({}, context, { name, fileName: file.fileName })),
        dashboardTemplate:
          (name, template) => dashboardTemplates.push(Object.assign({}, template, { name, fileName: file.fileName })),
        addExport: (obj) => {
          exports[file.fileName] = exports[file.fileName] || {};
          exports[file.fileName] = Object.assign(exports[file.fileName], obj);
        },
        setExport: (obj) => {
          exports[file.fileName] = obj;
        },
        asyncModule: (fn) => {
          asyncModules.push(fn);
        },
        require: (extensionName) => {
          if (self.extensions[extensionName]) {
            return new (self.extensions[extensionName])(this.cubeFactory, self);
          } else {
            const foundFile = self.resolveModuleFile(file, extensionName, toCompile, errorsReport);
            if (!foundFile && this.allowNodeRequire) {
              if (extensionName.indexOf('.') === 0) {
                extensionName = path.resolve(this.repository.localPath(), extensionName);
              }
              // eslint-disable-next-line global-require,import/no-dynamic-require
              return require(extensionName);
            }
            self.compileFile(
              foundFile,
              errorsReport,
              cubes,
              exports,
              contexts,
              dashboardTemplates,
              toCompile,
              compiledFiles
            );
            exports[foundFile.fileName] = exports[foundFile.fileName] || {};
            return exports[foundFile.fileName];
          }
        },
        COMPILE_CONTEXT: R.clone(this.compileContext || {})
      }, { filename: file.fileName, timeout: 15000 });
  • 参考SECURITY_CONTEXT使用
cube(`Orders`, {
  sql: `SELECT * FROM orders WHERE ${SECURITY_CONTEXT.email.filter('email')}`,
  dimensions: {
    date: {
      sql: `date`,
      type: `time`
    }
  }
});
  • 一些缺点
    因为SECURITY_CONTEXT主要是checkAuth 处理的,如果需要关联扩展需要改动代码

COMPILE_CONTEXT

主要属于schema 编译的时候,这个问题就比较多了,主要场景还是多租户,但是因为cube.js
编译api 的cache 以及COMPILE_CONTEXT执行一次的特性,很多时候容易出现使用问题
比如,有可能COMPILE_CONTEXT的数据不存在,可能会造成undefined的问题

 
const { securityContext: { total_amount,user_id,bucket } } = COMPILE_CONTEXT;
const filter_count = total_amount
const usercontext = SECURITY_CONTEXT
const user_id2 = JSON.stringify(COMPILE_CONTEXT)
cube(`demoapp`, {
    sql: ` SELECT
    *
FROM 
    transactions AS ts
    where  total_amount  > ${filter_count}
`,
    measures: {
        average_spend_per_customer: {
            sql: `${sum}/${count}`,
            type: `number`
        },
        count: {
            sql: `total_amount`,
            type: `count`,
        },
        sum: {
            sql: `total_amount`,
            type: `sum`,
        }
    },
    dimensions: {
        total_amount: {
            sql: `total_amount`,
            type: `number`
        },
        customer_id: {
            sql: `customer_id`,
            type: `string`
        },
        transaction_date: {
            sql: `transaction_date`,
            type: `time`,
            shown: false
        },
        event_id: {
            sql: `event_id`,
            type: `string`
        }
    }
});

FILTER_PARAMS

主要是进行数据过滤的,但是也要注意使用

  • 参考格式
FILTER_PARAMS.<CUBE_NAME>.<FILTER_NAME>.filter(expression)

注意filter_name 是必须存在的(就是维度指标)

总结

如果真的需要进行数据动态过滤处理,推荐使用FILTER_PARAMS以及SECURITY_CONTEXT
但是SECURITY_CONTEXT因为数据来源的特性,需要额外注意,一般情况(非多租户场景)不
推荐使用COMPILE_CONTEXT
FILTER_PARAMS 以及SECURITY_CONTEXT 的使用推荐基于filter(不然也会面临编译api cache 的问题)

说明

目前cube.js 还是缺少一些数据源自定义过滤的能力,官方也在计划中,最好的进行过滤还是推荐
使用以上的几个上下文

参考资料

https://cube.dev/docs/cube#context-variables-filter-params

上一篇:webpack高级概念Tree Shaking (树摇)(系列三)


下一篇:达芬奇架构NPU