Long Text From @ryan5500

more than 140 character

なぜissetは配列にないindexを渡してもエラーにならないのか

フォームから渡された値があるか確認するのに、こんなコードを書いた。

1
2
3
4
if (isset($_POST['comment']))
{
  ...
}

このコード、よく見ると変だ。$_POST['comment']がない場合、issetが関数だと考えると、関数を評価する前に$_POST['comment']が評価され、Undefined indexのNoticeエラーが出るべき。例えば次のようなコードをインタラクティブモードで実行すると、Noticeエラーが出る。

1
2
explode(',' $_POST['value']);
// => PHP Notice:  Undefined index: value in php shell code on line 1

なぜissetの場合、Noticeエラーを出さないのか?

調べると、issetは関数ではなく、ifやelseのようなキーワードの仲間であることがわかった。関数を評価する前の、字句解析の時点で別モノとして扱われる。関数っぽい外見だが中身はキーワード、というのは他にもいくつかある。emptyunsetなど。

このスライドに字句解析後のトークンとしてT_ISSETが含まれている。 http://ja.scribd.com/doc/18171982/PHP-Compiler-Internals#page=17

コードレベルだと、Zend/zend_language_parser.yで処理されている。 https://github.com/php/php-src/blob/094d409b3d34c51f49e0121e5ccfe8b2a717aaf6/Zend/zend_language_parser.y#L195

ElasticsearchへriverでMySQLのデータを流し込むのにハマって知ったコツ

ElasticsearchへMySQLのデータを流し込みたかったのでriverプラグインを使ったが、いくつかハマりどころがあったのでメモとして残す。

Elasticsearchに関連するツールのバージョンは要確認

関連するプラグインやライブラリのバージョンが少しでも違うと動かなくなる。

  • Elasticsearch 1.3.4の場合: riverは1.3.4.4, kuromojiは2.3.0
  • Elasticsearch 1.4.0の場合: riverは1.4.0.3.Beta1, kuromojiは2.4.0

mysql-connector-javaは最新を入れた方がよい。elasticsearch 1.4.0の時は5.1.33を入れた。

また、Macの場合jdk-1.7.0以上でないと動かないので、jdk7を入れる。 http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-macosx-x64.dmg

mappingについて

mappingなくても動くし書かなくていいかな、と思ってた。 でも運用する場合、フィールド毎にanalyzerの設定等を行う場合があり、どのみち必要になる。 最初からmappingは書こう。ただし、読み込むテーブルの全てのカラムでなく、必要なカラムだけで良い。

joinテーブルではSQLのエイリアスでフィールド名をキレイに

joinしたテーブルを読み込む場合、sql側でエイリアスを使うとフィールド名をキレイにできた。

{
  "type": "jdbc",
  "jdbc": {
    "url": "jdbc:mysql://localhost/test",
    "user": "root",
    "password": "",
    "sql": "select p.id product_id, p.name, product_name, od.quantity from products p inner join order_details od on p.id = od.product_id"
    "index": "test",
    "type": "order",
    "type_mapping": {
        "order": {
            "properties": {
                "product_id": { "type": "long", "store":"yes" },
                "product_name": { "type":"string", "store":"yes", "index":"analyzed"},
                "quantity": { "type": "long", "store":"yes" }
            }
        }
    }
}

この例だと、productsテーブルのIDコラムはidという名前で登録されてしまうところだが、product_idというエイリアスを使えばtype_mappingでもその名前で登録できる。

riverプラグインがデータを流し込んでくれません!

riverは実行時に、river自体が使うインデックス(デフォルトだと_river)と、それに加えてデータを流し込むインデックス(デフォルトだとjdbc)を作成する。

動作を試しているときにanalyzer等の設定を修正したので、反映のためにjdbcインデックスを削除し、もう一回riverでDBを流し込むコマンドを打っても再度jdbcが作成されない。 ここでは、_riverインデックスも削除する必要がある。これすごい不親切で結構ハマった。

analyzerの設定もriverの設定ファイル内に書く

Elasticsearchの本を読むとanalyzerの設定用にcurlでjsonデータを送ったりしているが、riverでデータを流し込む場合、そのjsonデータにanalyzerを設定する。

以下のjsonのindex_settingsの部分がそれに当たる。

{
  "type": "jdbc",
  "jdbc": {
    "url": "jdbc:mysql://localhost/test",
    "user": "root",
    "password": "",
    "sql": "select p.id product_id, p.name, product_name, od.quantity from products p inner join order_details od on p.id = od.product_id",
    "index": "test",
    "type": "order",
    "index_settings": {
      "index": {
        "analysis": {
          "tokenizer": {
            "kuromoji" : {
              "type":"kuromoji_tokenizer"
            }
          },

          "filter" : {
            "synonym" : {
                "type" : "synonym",
                "synonyms_path": "synonyms.txt"
            }
          },

          "analyzer": {
            "kuromoji": {
              "type":"custom",
              "tokenizer": "kuromoji"
            },
            "kuromoji_synonym": {
              "type":"custom",
              "tokenizer": "kuromoji",
              "filter" : ["synonym"]
            }
          }
        }
      },
      "type_mapping": { ... }
    }
}

synonyms.txt等の設定ファイルは$ES_HOME/configからの相対パスへ配置

synonyms filterを使う場合もriverの設定ファイルに記述するので、コマンドを叩くパスにsynonyms.txtがあればいいんじゃないか、なんて思ってた。 ドキュメント読んだら$ES_HOME/configの下にないと読み込んでくれませんってちゃんと書いてありました。。

curlコマンドを打つときは@表記を使ったほうがラク

入力するjsonデータをコマンドラインで打つのではなく、jsonデータをファイルにまとめ-d @test.jsonのような形でcurlに渡そう。

$ curl -XPUT http://localhost:9200/_river/my_jdbc_river/_meta -d @test.json