Apache Solrでファセット表示のタネ作り

最近、多くの検索サイトでは、検索した時に、絞り込みの助けになるような分類が横に表示されるものが増えています。Amazonや楽天では、検索語を含む商品のカテゴリやメーカー、価格帯などが出ますし、ジャパンサーチで検索すると所蔵機関がリストされたりします。これは「ファセット」と呼ばれている機能ですが、その名前を知らなくても、割とわかりやすく直感的に使えますね。検索結果とともにそういう情報もとりまとめるようなプログラムはさぞかしい難しいことだろう・・・と思いきや、2020年には、Apache SolrやElasticsearchなどのメジャーなフリーソフトの検索エンジンソフトではの標準的な機能の一つとして一般的に使えるようになっています。

さて、ではどういう風にしてファセットを取り出せばよいのかと言うと・・・ここでちょっと一工夫必要になるので、それも含めて説明します。

なお、ここからは、Power shell環境でのやり方は省略します。curlをきちんとインストールしておけば、あとは単に、curlをcurl.exeと書き換えるだけでできますので、そのようにご対応ください。

Apache Solrでは、curlでURLを渡すとJSON形式で欲しいデータが返ってくるようになっていました。ファセット表示のためのデータを取得する場合にも、同じような感じです。たとえば、「富山」という単語で検索して、それを含む資料名(ここではentryというフィールド名)とそれぞれのヒット件数をリストする」場合、以下のような感じでできるはずですが・・・

curl 'http://127.0.0.1:8983/solr/minhon3/select?q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22&facet.field=entry&facet=on&fl=entry&rows=10&facet.sort=count'

やってみると、何か変ですね。以下のような感じになってませんでしょうか?

{
  "responseHeader":{
    "status":0,
    "QTime":9,
    "params":{
      "q":"textConn:\"富山\"",
      "facet.field":"entry",
      "fl":"entry",
      "rows":"10",
      "facet":"on",
      "facet.sort":"count"}},
  "response":{"numFound":3,"start":0,"docs":[
      {
        "entry":["越中国立山并立山新道之図"]},
      {
        "entry":["愛知岐阜福井三県下大地震之図"]},
      {
        "entry":["方角場所附 [江戸大火]"]}]
  },
  "facet_counts":{
    "facet_queries":{},
    "facet_fields":{
      "entry":[
        "",2,
        "",2,
        "",2,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",1,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "",0,
        "ステキメッポー",0,
        "",0]},
    "facet_ranges":{},
    "facet_intervals":{},
    "facet_heatmaps":{}}}

これは、entryというフィールドのフィールドタイプがファセット向けの設定になっていないためにこのようになっています。一応、以下のURLで確認できますので、みてみますと・・・

curl 'http://localhost:8983/solr/minhon3/schema/fields'
    {
      "name":"entry",
      "type":"text_general"},

entryの対応は text_generalとなっていました。普通に文字列検索する場合にはこの方が便利なので、Apache Solrは文書登録時に自動的にこのように設定してしまいます。親切設計なのですが、こういう時はちょっと不便です。 そこで、これはこれとして検索できるように残したまま、しかしファセットとしてはうまく表示したい・・・という我々のために、Apache Solrでは、「異なるタイプのフィールドを作って中身をコピーする」という機能を用意してくれています。これは以下のようにしてできるようです。

まず、string タイプのフィールドの作成です。フィールド名は entry_st としておきましょうか。

curl -X POST -H 'Content-type: application/json' --data-binary '{
"add-field" : {
"name":"entry_st",
"type":"string",
"indexed":"true",
"stored":"true",
"required":"false",
"multiValued":"false" }}' http://localhost:8983/solr/minhon3/schema

次に、entryをentry_stにコピーします。

curl -X POST -H 'Content-type:application/json' --data-binary '{
  "add-copy-field":{
     "source":"entry",
     "dest":[ "entry_st" ]}
}' http://localhost:8983/solr/minhon3/schema

そうしましたら、一応、データを再アップロードして、インデックスを再構築してみます。

curl "http://localhost:8983/solr/minhon3/update?commit=true" --data-binary "@minhon3.json" -H 'Content-type:text/json;charset=utf-8'

そうしましたら、以下のURLにて、この entry_stをファセットとして表示してみましょう。

curl 'http://127.0.0.1:8983/solr/minhon3/select?q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22&facet.field=entry_st&facet=on&fl=entry_st&rows=10&facet.sort=count'

そうすると、以下のような感じできれいに資料名とヒット件数が表示されましたね。

(略)
  "facet_counts":{
    "facet_queries":{},
    "facet_fields":{
      "entry_st":[
        "愛知岐阜福井三県下大地震之図",1,
        "方角場所附 [江戸大火]",1,
        "越中国立山并立山新道之図",1,
        "[あら嬉し大安日にゆり直す]",0,
(略)

というわけで、ファセット表示のタネ作りはこれくらいです。

ここまでのところ、データはJSON形式で返ってくるので、見やすいという人も一部にはいると思いますが、普通の人にはあんまり見やすいものではないと思います。これを見やすい形に整形するということもそろそろやってみたいところですが、それは次回以降にしましょうか。