ytake Hatena

Web Application Developer

PHPでビッグデータを操作しよう!Presto編 2

ytake.hateblo.jp

*上記の続き

異なるデータベース、NoSQLなどを結合できるということが理解できたと思います。

それではPHPのアプリケーションから実際に利用してみましょう。

PHP Prestodb Client

PHPのPrestoクライアントライブラリは、古いものがありますが、
自分の用途に合わなかった(最新のPHP向けにしたかったなど)為、
新たに作り、公開しました。

*新しく作ったもの

github.com

*以前からある古いもの

github.com

prestoはpdoのようにネイティブのコネクタはなく、
REST APIによる操作が可能です。
以前からあるもののスタイルではなく、javaのクライアントに近い実装にしました。

早速クライアントを利用して、前回のクエリを発行します。

Install

PHP7.0以上にのみ対応していますので、7.0以上の環境で実行してください。

installはcomposerを利用してください。
下記のコマンドを実行するだけでOKです。

$ composer require ytake/php-presto-client

Usage

prestoに問い合わせるクライアントのインスタンス生成は次の通りです。

<?php

$query = "SELECT _key, _value, test_id, test_name, created_at 
FROM my_tests.testing.tests AS myttt 
INNER JOIN red_tests.test.string AS redttt ON redttt._key = myttt.test_name 
WHERE myttt.test_name = 'presto'";

$client = new \Ytake\PrestoClient\StatementClient(
    new \Ytake\PrestoClient\ClientSession('http://127.0.0.1:8080/', 'my_tests'),
    $query
);

ClientSessionクラスに接続先と、第二引数にカタログ名を指定します。
カタログ名を指定すると、そのカタログ名を省略できるようになります。

ClientSessionクラスのインスタンスをStatementClientクラスに与えると、
Prestoに問い合わせる準備が整います。

一番簡単に利用できるのは、Ytake\PrestoClient\ResultsSession 経由で結果を取得する方法です。

<?php
$resultSession = new \Ytake\PrestoClient\ResultsSession($client);
$result = $resultSession->execute()->yieldResults();

/** @var \Ytake\PrestoClient\QueryResult $row */
foreach($result as $row) {
    foreach($row->yieldData() as $yieldRow) {
        if($yieldRow instanceof \Ytake\PrestoClient\FixData) {
            var_dump($yieldRow->offsetGet('_key'), $yieldRow['_key']);
        }
    }
}

yieldResultで様々なデータが返却されます。
Prestoはデータを取得するまでに複数回リクエストをおくる必要があり、
Queryの結果取得以外にも様々なデータを分割して返却する為です。

Prestoから返却されるQueryの結果は Ytake\PrestoClient\FixData で返却されますので、
これを取得します。
このクラスは ArrayAccess を実装していますので、
一般的な配列へのアクセスと同等にカラムの値を取得できます。

前回利用したクエリの結果は次のようになります。

class Ytake\PrestoClient\FixData#20 (5) {
  public $_key =>
  string(6) "presto"
  public $_value =>
  string(7) "awesome"
  public $test_id =>
  int(1)
  public $test_name =>
  string(6) "presto"
  public $created_at =>
  string(23) "2017-09-15 03:49:30.000"
}

接続にユーザーなどが必要であれば、ClientSessionクラスのメソッドが利用できます。
Prepared Statementは現在のバージョンではサポートされていない為、
利用できませんが(ユーザーに近いフロントから利用するものではない為)
サポートされていた以前のバージョンではPreparedStatementクラスがそのまま使えると思います。

利用方法のまとめ

簡単な利用方法をまとめると次のようになります。

<?php

require_once __DIR__ . '/vendor/autoload.php';

$query = "SELECT _key, _value, test_id, test_name, created_at 
FROM my_tests.testing.tests AS myttt 
INNER JOIN red_tests.test.string AS redttt ON redttt._key = myttt.test_name 
WHERE myttt.test_name = 'presto'";

$client = new \Ytake\PrestoClient\StatementClient(
    new \Ytake\PrestoClient\ClientSession('http://127.0.0.1:8080/', 'my_tests'),
    $query
);
$resultSession = new \Ytake\PrestoClient\ResultsSession($client);
// yield results instead of returning them. Recommended.
$result = $resultSession->execute()->yieldResults();

/** @var \Ytake\PrestoClient\QueryResult $row */
foreach($result as $row) {
    foreach($row->yieldData() as $yieldRow) {
        if($yieldRow instanceof \Ytake\PrestoClient\FixData) {
            var_dump($yieldRow->offsetGet('_key'), $yieldRow['_key']);
        }
    }
}

オブジェクト マッピング

PDOのFETCH_CLASSのように、任意のオブジェクトで変更することもできます。

PHP: PDOStatement::fetch - Manual

任意のオブジェクトで返却したい場合、カラムをクラスのプロパティとして作成します。
指定したカラム以外の値は返却されませんので、
以下のようにすると、プロパティに記述した2つのカラム以外は返却されません。
尚、プロパティはpublic, protected, privateのいずれでも値が挿入されますので、
アプリケーションの設計に合わせて利用してください。

<?php

class Testing
{
    /** @var string */
    private $_key;

    /** @var string */
    private $_value;

    /**
     * @return string
     */
    public function key()
    {
        return $this->_key;
    }

    /**
     * @return string
     */
    public function value(): string
    {
        return $this->_value;
    }
}

あとはyieldObjectメソッドにクラス名を指定して値を取得します。

<?php

$resultSession = new \Ytake\PrestoClient\ResultsSession($client);
$result = $resultSession->execute()->yieldResults();

/** @var \Ytake\PrestoClient\QueryResult $row */
foreach ($result as $row) {
    foreach ($row->yieldObject(Testing::class) as $item) {
        if ($item instanceof Testing) {
            var_dump($item);
        }
    }
}

これでPHPのアプリケーションからPrestoを操作することができるようになりました。
管理画面や、分析用途に活用してみてください。