SolrをPHPで使う(検索編)

前回の記事でSolrをPHPから使う準備ができました。
今回はPHPで Solr の検索を実行する方法です。

検索結果のオブジェクトは以下のような構造になっています。

SolrObject Object
(
    [responseHeader] => SolrObject Object
        (
            [zkConnected] => 1
            [status] => 0
            [QTime] => 0
            [params] => SolrObject Object
                (
                    [q] => *:*
                    [indent] => on
                    [start] => 0
                    [rows] => 2
                    [version] => 2.2
                    [wt] => xml
                )

        )

    [response] => SolrObject Object
        (
            [numFound] => 52
            [start] => 0
            [docs] => Array
                (
                    [0] => SolrDocument Object
                        (
                            [_hashtable_index:SolrDocument:private] => 24804
                        )

                    [1] => SolrDocument Object
                        (
                            [_hashtable_index:SolrDocument:private] => 5340
                        )

                )

        )

)

docs という配列の要素である SolrDocument オブジェクトは SolrDocumentField オブジェクトを複数含んでいます。SolrDocument 内のすべての SolrDocumentField をたどるために current() や next() などのメソッドを利用します。

SolrObject ベースの検索結果は意外と扱いづらいので、JSON で扱えるようにしてみます。

SolrClient 作成時のオプションとして wt=json を追加しています。

getRawResponse() の応答は以下の通りです。

{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":0,
    "params":{
      "q":"*:*",
      "indent":"on",
      "start":"0",
      "rows":"2",
      "version":"2.2",
      "wt":"json"}},
  "response":{"numFound":52,"start":0,"numFoundExact":true,"docs":[
      {
        "id":"0553573403",
        "cat":["book"],
        "name":"A Game of Thrones",
        "price":7.99,
        "price_c":"7.99,USD",
        "inStock":true,
        "author":"George R.R. Martin",
        "author_s":"George R.R. Martin",
        "series_t":"A Song of Ice and Fire",
        "sequence_i":1,
        "genre_s":"fantasy",
        "_version_":1730974266238697472,
        "price_c____l_ns":799},
      {
        "id":"0553579908",
        "cat":["book"],
        "name":"A Clash of Kings",
        "price":7.99,
        "price_c":"7.99,USD",
        "inStock":true,
        "author":"George R.R. Martin",
        "author_s":"George R.R. Martin",
        "series_t":"A Song of Ice and Fire",
        "sequence_i":2,
        "genre_s":"fantasy",
        "_version_":1730974266279591936,
        "price_c____l_ns":799}]
  }}

これを json_decode() することで、通常の PHP 配列として扱うことができます。

Array
(
    [responseHeader] => Array
        (
            [zkConnected] => 1
            [status] => 0
            [QTime] => 0
            [params] => Array
                (
                    [q] => *:*
                    [indent] => on
                    [start] => 0
                    [rows] => 2
                    [version] => 2.2
                    [wt] => json
                )

        )

    [response] => Array
        (
            [numFound] => 52
            [start] => 0
            [numFoundExact] => 1
            [docs] => Array
                (
                    [0] => Array
                        (
                            [id] => 0553573403
                            [cat] => Array
                                (
                                    [0] => book
                                )

                            [name] => A Game of Thrones
                            [price] => 7.99
                            [price_c] => 7.99,USD
                            [inStock] => 1
                            [author] => George R.R. Martin
                            [author_s] => George R.R. Martin
                            [series_t] => A Song of Ice and Fire
                            [sequence_i] => 1
                            [genre_s] => fantasy
                            [_version_] => 1730974266238697472
                            [price_c____l_ns] => 799
                        )

                    [1] => Array
                        (
                            [id] => 0553579908
                            [cat] => Array
                                (
                                    [0] => book
                                )

                            [name] => A Clash of Kings
                            [price] => 7.99
                            [price_c] => 7.99,USD
                            [inStock] => 1
                            [author] => George R.R. Martin
                            [author_s] => George R.R. Martin
                            [series_t] => A Song of Ice and Fire
                            [sequence_i] => 2
                            [genre_s] => fantasy
                            [_version_] => 1730974266279591936
                            [price_c____l_ns] => 799
                        )

                )

        )

)

SolrをPHPで使う(インストール編)

PHPではPECLで Solr Extension が用意されています。
https://www.php.net/manual/ja/book.solr.php

Solr Extension をインストールするためには、まず PECL を使えるようにします。(以下、Debian での手順です)

$ sudo apt install php-pear php-dev
$ sudo pecl channel-update pecl.php.net
$ ecl search solr

Matched packages, channel pecl.php.net:
=======================================
Package Stable/(Latest) Local
solr    2.5.1 (stable)        The Apache Solr PHP extension is an extremely fast,
                                  light-weight, feature-rich library that allows PHP applications to
                                  communicate easily and efficiently with Apache Solr server instances
                                  using an object-oriented API.

Solr Extension をコンパイルするのに必要なライブラリをインストールします。

$ sudo apt install libcurl4-openssl-dev libxml2-dev
$ mkdir -p /tmp/libcurl4/include
$ ln -s /usr/include/x86_64-linux-gnu/curl /tmp/libcurl4/include
$ mkdir -p /tmp/libxml2/include
$ ln -s /usr/include/libxml2/libxml /tmp/libxml2/include/libxml2

PECL が想定しているディレクトリ構成に合わせるためにシンボリックリンクを張っています。

ここまで準備ができたらインストール。

$ sudo pecl install solr
(略)
Build process completed successfully
Installing '/usr/lib/php/20190902/solr.so'
install ok: channel://pecl.php.net/solr-2.5.1
configuration option "php_ini" is not set to php.ini location
You should add "extension=solr.so" to php.ini

途中で libcurl と libxml2 の include パスを聞かれるので、それぞれ /tmp/libcurl /tmp/libxml2 を指定します。

コンパイルが正常に終了したら PHP から利用できるように設定します。

$ sudo sh -c "echo extension=solr.so > /etc/php/7.4/mods-available/solr.ini"
$ sudo ln -s /etc/php/7.4/mods-available/solr.ini /etc/php/7.4/cli/conf.d/30-solr.ini
$ sudo ln -s /etc/php/7.4/mods-available/solr.ini /etc/php/7.4/apache2/conf.d/30-solr.ini
$ sudo service apache2 restart

ガチャガチャの紹介

最近になってガチャガチャにハマり込んでしまい、コレクションが集まってきたので、一部紹介したいと思います。

今年24歳になりますが、私が小さい頃はガチャガチャは100円でできるイメージでした。たしかガチャガチャの機械には50円玉のマークも書かれていた記憶があるので、50円の物もあったのでしょうか?

でも最近は大体が300円で、200円のガチャガチャは安く感じますし、普通に400円、500円のガチャガチャもあります。高すぎる。

でもすごくクオリティが高くて、可愛くてついつい全種類揃えたくなったり、目当ての商品が出るまでやってしまったりとまんまとガチャガチャの罠にハマっています。

まずひとつ目は、ガチャガチャにはまるきっかけとなったキティちゃんシリーズ

もともとガチャガチャには特に興味なかったのですが、姪っ子がアンパンマンのガチャガチャにハマり、一緒にガチャガチャがあるところにいくと、私はキティちゃんが大好きなのでついでにキティちゃんのガチャガチャがあればついついやっていました。

気づいたら行く先々でガチャガチャがあれば足を止めてラインナップを確認するようになり、他のガチャガチャにも手を出すようになるのです。。

可愛くて楽しい。音がなるシリーズ

とりあえず、ミニチュアに目がなくて、その上本物と同じ音がなるのでリアリティもすごいです!

インターホンは白いインターホンが欲しかったのですが、なかなかでなくて意図せず全種類コンプリートしてしまいました。。

早押しクイズのガチャガチャは4種類あったのですが、ちょうどマルとバツがでてすごく嬉しかったのを覚えています。

いつも食べている、食べ物シリーズ

「サイゼリア」「ヤマザキパン」「雪印メグミルク」「辛いラーメン」の4種類です。

雪印メグミルクのさけるチーズが大好きなのですが、当てることができませんでした。父6Pチーズが大好きなのでプレゼントしようと思います。

ヤマザキパンのガチャガチャでは、毎朝食べているロイヤルブレッドを狙っていたのですが、なんとこちらは400円のガチャガチャで、当たるまでやるには勇気がなく2回で諦めてしまいました。

でもダブルソフトも北海道チーズ蒸しケーキも大好きなので、大切にしようと思います。

最後に、「ガチャガチャのガチャ」

なんと、ガチャガチャのガチャです。しかも200円です。安い。

1回で一つの機械が出てくるのですが、中に小さいカプセルが入っていて回すとちゃんと出てきますし、本当に存在する種類のガチャガチャの表紙がはいっていて、2つ以上になると、上下左右に連結することもできます。

ガチャガチャをすきになると、道端でこの機械が目に入るだけでテンションが上がるのですが、自宅に小さいガチャガチャパークが作れるのでこれをみると、いつでも楽しい気分になります。もっとたくさん連結させたいです。

大人になったらガチャガチャの前をスルーしてしまいがちですが、もし次にガチャガチャを見つけたときは是非見てみてください。見るだけでも色々あって面白いです。

私は今は「からあげクン」のガチャガチャを探しているので、もしみつけたら教えていただけるとうれしいです!!

0.1 + 0.1 は 0.2 ではない

こんにちは。開発担当のマットです。

コンピューターはとても力強い計算機です。
毎秒毎秒、何億回も計算をできる、素晴らしい機械ですけれども、それでも 0.1 + 0.1 のような簡単な計算を正確にできないことは驚きの事実です。

2進法と10進法の違い

我々人間は、だいたい10進法を使います。
(一部の文化で、12進法や26進法などを使うこともあるようですが)

数える時は 0 で始まる。その次は 1, 2, 3, 4, 5, 6, 7, 8, 9。
合わせて、10個の記号を使います。
そのため、「十」以上を数える場合、左に位を上げます。
百と千などは「10×10」と「10x10x10」となります。

つまり、6308 の数は、
     6 x (10 x 10 x 10)
+   3 x (10 x 10)
+   0 x 10
+   8 x 1
です。

コンピュータは、2進法を使います。
数える時は 0 で始まる。その次は 1。その2記号だけです。
でも、同じ
つまり、25 の数は、 
     1 x (2 x 2 x 2 x 2) 
+   1 x (2 x 2 x 2)
+   0 x (2 x 2)
+   0 x 2
+   1 x 1
なので、2進法で書くと、25(二十五) は 「11001」 と表します。
(それは一万一千一ではなく、二十五です。別の書き方をしているだけです。)

2進法は1679年に、ドイツで開発されました。

浮動小数点」を書く時は?

233や3122 のような数字を「整数」と呼びます。
逆に、0.524 や 0.216 のような小数点のある数字を「浮動小数点数」と呼びます。

整数と同じようですが、2 を使うではなく、分数ですので、1/2 を使います。
つまり、 0.125 は
     0 x (1/2)
+   0 x (1/2 x 1/2)
+   1 x (1/2 x 1/2 x 1/2)
なので、2進法で浮動小数点数として書くと、0.125 は 001 と書きます。

ところが、2進法で、綺麗に表せない数字もあります。
例えば、10進法で、1 を 3 に割ると、0.33333333333…. になると同様、
0.1 を浮動小数点数として書く場合、
0.1 = 0.00011001100110011001100110011… になります。
(最後の「0011」の部分が永遠に繰り返します)

永遠に計算できない罠

残念ながら、コンピューターは「永遠」や「無限」の概念を理解できません。
無限に長い数字をメモリーに入れることはできませんので、省略する必要があります。

そのため、0.1 (10分の1)をメモリーに保存する場合、
0.0001100110011001101 として保存されます。

その値は、正確に 0.1 (10分の1) ではなく、0.1000003814697265625 です。
省略しているため、値が微妙にズレてしまいました。

人間として、0.1に0.1を足せば、0.2になるだろう、と思いがちです。
プログラマーも人間なので、その前提でコードを書いてしまいます。
このようなことをすると、変なエラーと予期できない動作が発生してしまいます。

まとめ

コンピューターは完全に思う通りに動くと思いたいですが、あくまでも物理的な機械なので、物理的な限界もあります。

プログラマーになるには、コーディング法や言語の特徴を覚える必要はもちろんありますが、匠と同様に、失敗をしたくない場合、道具として使っているコンピューターのことも、深く理解しなければなりません。

とても面白いと思います。

続・漫画、小説の電子書籍はhontoがおすすめ

hontoを利用して記念すべき1000冊を超えたので再レビューです。

電子書籍

「最近読んだ本を開く」の機能を見つけたのでたまに落ちる点については特に問題なくなりました。今のところiPad Air(第四世代)で見る限りだと1000冊超えても特に問題はなしで冊数が多いからたまにSyncに時間がかかるぐらいでhontoを使ってての不満点はありません。たくさん買ってるので続きが出ると通知されて続刊リストに通知されるのは非常に助かります。

よくを言えばマイリストを作るときに1巻ずつ表示されるが追加するとシリーズ丸ごと登録されるのでシリーズ毎に追加できて読んだ順も反映されると尚良いですが今のままでも十分便利。あと買ってて続きはいいかなとなる本もあるのでそういったものは続刊リストから省ければと思います。こっちは設定あるのか不明です。

前回に続き不満点はないので漫画や小説など続き物がある娯楽系についてはhontoが一押しです!