PHP 访问 Saiku 的 API 小结

Saiku 是一个模块化的开源分析套件,它提供轻量级的OLAP(联机分析处理),并且可嵌入、可扩展、可配置。同时它还提供了基于 REST 协议的 API,使用户能够从外部获取到 Saiku 的分析结果。前一段时间研究了一下怎么样用 PHP 实现整个过程,其中参考了网上的一些资料,不知道是不是因为协议有更新的缘故,在 github 上搜到pstoellberger的一份代码,但是现在并不能运行,在他的基础上我做了一些修改,也能运行出来了,我的系统是 windows 7 。这个过程中我花了很多时间,现在分享出来,希望能提供一些帮助。

定义 curl_request 类,这是为了方便后面发起各种请求,这里我采用了 pstoellberger 的代码。

class curl_request {

    private static $opt = array(
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_USERAGENT      => 'Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
    );

     function guid(){

            mt_srand((double)microtime()*10000);//optional for php 4.2.0 and up.
            $charid = strtoupper(md5(uniqid(rand(), true)));
            $hyphen = chr(45);// "-"
            $uuid = chr(123)// "{"
                    .substr($charid, 0, 8).$hyphen
                    .substr($charid, 8, 4).$hyphen
                    .substr($charid,12, 4).$hyphen
                    .substr($charid,16, 4).$hyphen
                    .substr($charid,20,12)
                    .chr(125);// "}"
            return $uuid;

    }

    public function exec( $url, $method = 'get', $params = array() ){

            $ch = curl_init();
            $opts = self::$opt;

            switch( $method ) {

                    case 'get':
                            $url.= '?'.http_build_query( $params );
                    break;

                    case 'post':
                            if( count( $params ) > 0 ) {
                                    $opts[CURLOPT_POST] = count( $params );
                                    $opts[CURLOPT_POSTFIELDS] = http_build_query( $params );
                            }                                                                       
                    break;
            }

            $cookie_file = "./tmp/saiku.txt"; // 此处为 saiku 的 sessionId 存放的文件
            $opts[CURLOPT_COOKIEJAR] = realpath($cookie_file); // 获取实际的目录,用于读写
            $opts[CURLOPT_COOKIEFILE] = realpath($cookie_file);
            $opts[CURLOPT_URL] = $url;

            curl_setopt_array( $ch, $opts );

            $result = curl_exec($ch);
            $info = curl_getinfo($ch);

            return array( 'response' => $result, 'info' => $info );
    }
}

注意上一步中需要在当前目录下新建一个 tmp 文件夹,同时新建一个 saiku.txt (文件名随意,保持与代码中相同即可),第二个需要注意的地方就是 realpath 这个函数,他将相对路径转化成可读写的绝对地址。

  1. 开始实现如下四个流程:登录 -> 获取 repository -> 新建查询 ->返回结果,登录的代码:
$base = 'http://192.168.0.17:8080/saiku/rest/saiku/';
$x = new curl_request;
$username = 'admin';
$password = 'admin';

// 1.登录
$r = $x->exec( $base.'session/' );
$session = json_decode( $r[ 'response' ], true );

if( count( $session ) === 0 ) {
        // 用户没有登录,现在开始登录
        $r = $x->exec( $base.'session/', 'post', array( 'username' => $username, 'password' => $password ) );
        if( $r[ 'info' ][ 'http_code' ] == '200' ) {
                $r = $x->exec( $base.'session/' );
                $session = json_decode( $r[ 'response' ], true );
        }
} else {
        //var_dump( $session);
        //echo $session[ 'session_id' ];
}
  1. 获取 repository
$repName = 'sales.number';

// 2. 获取 repository
$qstring = $base.$session['username'].'/repository/'.urlencode($repName);

$r = $this->exec($qstring);
$res = json_decode($r['response'], true);
  1. 根据获取到的 repository ,新建 QueryModel,这里用到了 simplexml_load_string 函数,这是 PHP 用来读取 xml 文件的函数。
$xml = simplexml_load_string($res['xml']);
$attr = $xml->attributes();


// 3.新建 QueryModel, QueryModel可以复用
$new_query = $x->guid();
$new_query = trim($new_query,'{}');
$qstring = $base.$session['username'].'/query/'.$new_query;
$r = $x->exec( $qstring, 'post', array(
    'type' => $attr->type,
    'connection' => $attr->connection, 
    'cube' => $attr->cube,
    'catalog' => $attr->catalog,
    'schema' => $attr->schema,
    'formatter' => 'flattened',
    'xml' => $res['xml']
    ) 
);
$metadata = json_decode( $r[ 'response' ], true );

需要注意的是,这些 api 的一些参数都是 Saiku 文档里定义好的。

1417444447518748.png

就是我们要创建查询的 api,type 指的是传值的方式, form 指的是需要通过 curl 方式 post 过去的,path 则指的是直接写在访问地址里面的。这里要依据已有的 repository 创建新的查询,其中的原理,后文再详述。

  1. 获取新建查询的结果。
//4. 利用建立好的QueryModel, 提交MDX并取回结果
$qstring = $base.$session['username'].'/query/'.$new_query.'/result/flat';
$mdx = (string)$xml->MDX;

$r = $x->exec( $qstring, 'post', array('limit' => "0", 'mdx' => $mdx) );
$results = json_decode( $r[ 'response' ], true);

这样,就能取到最后得结果了,结果是一个 json 文件,再利用 php 做一些变换很容易就能得到符合我们期望结构的数据。

因为整个流程必须要按这么几步走下来才能出结果,关于原理,我自己琢磨了一下,不知道对不对,希望能和大家一起讨论,Saiku 里面的 repository 并不直接包含结果,在我们登录 saiku 系统点击按钮进行 saiku 文件的查询过程中会自动创建临时的 QueryModel,等用户退出后,这些临时的 QueryModel 会被销毁,所以在使用 api 模拟这个过程中我们也要按照这种思路来进行。我们先要根据获取到的 repository 的信息新建临时查询,然后再获取这个临时查询的结果。

这是我自己的理解,如有错误,恳请大家指正。

主要的参考文档是:

  1. Saiku REST 文档(这个好像不能打开了,给大家提供一个我们正在用的 saiku 的 REST 文档
  2. pstoellberger 的 gist(主要代码来源)

用到的代码见压缩包:
saiku-REST.rar

标签: saiku, api, php
返回文章列表 文章二维码 打赏
本页链接的二维码
打赏二维码