フリーソフトで快適TEI/XML(Oxygenを使わない道)

TEI/XMLは、基本的には裏側で勝手にタグがついたりそれに対応した表示が勝手に行われてくれるものであり、個別には色々なソリューションがあります。 しかし一方で、「どういうタグをつけるか」「どういう風に構造化するか(=それにあわせてどういうタグをどう使うか)」といったことを 考える際には、今のところ、XMLを直に書いた方がやりやすいです。それを支援してくれる強力なツールとしてOxygen XML Editorというものがあり、 その簡単さと便利さゆえに、世界中どこに行っても、TEI/XMLの入門者向けにはOxygen XML Editorを使う、という風になっています。

しかしながら、特に発展途上国の研究者には、地元にTEIを広めるにはOxygenは高すぎる、そもそも個人で有料ソフトウェアを購入する という習慣がない、というようなことを言われたりしますし、我々としても、やはり節約できるところはなるべく節約したいところです。 そこで、高機能テキストエディタでなんとかならないのか…と思っていたところ、異体字セレクタセレクタを作っている大学院生の王一凡さんが VScode(マイクロソフト製のフリーの高機能テキストエディタ。プログラミングで最近非常によく使われている)でのやり方を調べてくださいました。こちらにその概要があ りますので、これをみただけでできる人はそのまま取り組んでみてください。ただ、こちらは概要であり、私がやってみても結構とまどったので、 もう少しわかりやすくなるように、その導入手順をもう少し詳しく、以下に説明してみたいと思います。

作業の流れは以下のようになります。

  1. VSCodeのインストール
  2. Javaのインストール  2-1. Javaホームの設定
  3. XMLプラグインのインストール
  4. 設定ファイルの編集
  5. 日本語TEIスキーマの組み込み

では、さっそくやってみましょう。

1. VSCodeのインストール

VSCodeの本家サイトからインストールしてください。 これは、なんてことないですね。ここでつまづくようだと、色んな意味でかなり厳しいですが、大丈夫だと思います。

2. Javaのインストール

Javaは、Mac やWindows上に仮想マシンを作って、その上で共通言語のプログラムを動かせるもので、一度プログラムを書けば 色んなOS上にそのまま対応でき、大規模共同開発にも向いているため、広く使われています。Oxygen XML EditorもJavaですし、 最近は、銀行のシステムでも使われているそうです。ここでは、VSCodeのプラグインがJavaの仮想マシン上で動くものに なっているため、以下のリンクからJavaの仮想マシンをインストールします。

Javaのインストール頁

さて、インストールができたら、次に、少し設定をする必要があります。 「Javaがどこにあるか」をコンピュータがぱっと簡単に探せるようにしておくのです。それが 次項です。

2-1. Javaホームの設定

Javaホームの設定は、微妙にややこしいです。まず、Javaがどこにインストールされているかを確認する必要があります。

Windowsの場合は、以下のサイトを見ながら「JAVA_HOME」を設定してください。

www.javadrive.jp

(なお、ここでは「どこのフォルダにJavaがインストールされたか」を確認する必要があります。 たとえば筆者のパソコンでは、C:\Program Files\Java\jdk-13.0.1 となっています。)

Macの場合は、「どこのフォルダにJavaがインストールされたか」を簡単に確認できるようになっているそうです。 全体の作業としては「インストール場所を確認」⇒「確認したパス(Path)をJAVA_HOMEとして設定」という風になります。 たとえば以下のサイトをみて設定してみてください。

qiita.com

3. XMLプラグインのインストール

さて、いよいよ、VSCodeのXMLプラグインのインストールです。 VSCodeを開いた状態で、以下の図のように、画面左側のメニューの「Extension」をクリックしてから、検索窓に「xml」と入力してみてください。

f:id:digitalnagasaki:20200214024406p:plain

そうすると、「Red Hat」という制作者(提供者?)が付された「XML」というプラグインが、おそらく最上位に表示されるはずです。(上の図でも出てますね。)そうしましたら その横にある「Install」というボタンをクリックしてインストールしてください。

これでインストールは完了するはずです。

  1. 設定ファイルの編集

次に、JAVA_HOMEの設定の次に難しい作業に入ります。 設定ファイルの編集です。

settings.jsonというファイルを編集すればいいのですが、このファイルの場所を確認するのが微妙にややこしいです。

以下の図のように、ExtensionでXMLを表示しておいて(これは上でやりましたね)、設定ボタンをクリックします。そうするとサブメニューが開きますので、 そこで「拡張機能の設定を構成します」を選んでください。

f:id:digitalnagasaki:20200214025003p:plain

そうすると、「設定」というタブが以下の図のように開きますので「拡張機能」をクリックしてからさらに、「XML Configuration」をクリックして選択してください。

f:id:digitalnagasaki:20200214025256p:plain

ここで、このタブの内容を下の方へと見ていくと「settings.jsonで編集」というリンクが時々出てきます。そのいずれかをクリックするとsettings.jsonが開きます。ここに JAVA_HOMEで設定した値を入力するのです。

入力する内容は、以下のURLに書いてある通りですのでそちらをご覧ください。

scrapbox.io

たとえば私のPC環境ですと、settings.jsonの内容は以下のようになります。

{
    "terminal.integrated.shell.windows": "C:\\Windows\\System32\\wsl.exe",
    "window.zoomLevel": 2,
    "xml.java.home": "C:\\Program Files\\Java\\jdk-13.0.1",
    "workbench.colorTheme": "Default Light+"

}

これで、VSCodeを再起動すれば、XMLプラグインが使えるようになっているはずです。

次に、(日本語)TEIスキーマの割り当てについても確認してみましょう。

5. 日本語TEIスキーマの組み込み

日本語TEIスキーマを組み込むと、タグやタグの属性の説明を日本語で確認できるようになります。これには、 日本語TEIスキーマを準備するという作業と、それを、TEI/XMLの文書に適用させるという作業が必要になります。 前者はTEIの基本であるRomaからダウンロードできます。が、これも手順がちょっと ややこしいので、少しやり方を書いておきます。

5-1. 以下のサイトにアクセスする

Roma: generating customizations for the TEI

5-2. 「Reduce」にチェックボックスを入れてから「Start」ボタンをクリック

5-3. 「Language」タブをクリックして選択してから「日本語」のチェックボタン(radio)にクリックして選択

5-4. 「Schema」タブをクリックしてから、表示されるセレクトメニューのうち、「W3C schema (in ZIP Archive)」を選んでから「Generate」ボタンをクリックしてください。

そうすると、zipアーカイブされたファイルが手に入ります。これをしかるべきところに保存して、zipを元に戻します。

zipを元に戻すと、フォルダができて、その中にいくつかのファイルが入っているという形になると思います。これはそのままにしておいてください。

次に、ダウンロードしたスキーマファイルをどうすればいいか、については、上述の王一凡さんの頁にてご確認ください。(後ほど、元気が残っていたらこの先ももう少し解説します…)

Flask/Python3からApache Solrに問い合わせるには

さて、ここまでApache Solrへの問い合わせの仕方を色々とみてきました。検索結果はJSON形式だったりCSV形式だったり、いくつか選ぶことはできるものの、人に優しい感じではありません。 しかしながら、コンピュータにはとても優しいもので、ということは、コンピュータを介して人に優しくさせることはできるはずです。

やり方としては無数にあります。個人的にはJavascriptでJSONを読み込んでWebブラウザに表示してしまうのが好きなのですが、今回、「みんなで翻刻」の検索システムで使ったFlask/Python3の場合のやり方を少しみてみましょう。

Flask/Python3の導入・設定・基本的な書き方は、あちこちのサイトをご覧ください。一通りできるようになった上での話になりますが、

  1. Flask/Python3からApache Solrに問い合わせる
  2. Apache Solrから返戻されたデータを受け取る
  3. 受け取ったデータを適宜整形する

といったことができるようになるといいですね。以下に、そのやり方について少し見てみましょう。

1. Flask/Python3からApache Solrに問い合わせる

この問い合わせは、要するにFlask/Python3で問い合わせ用URLを作って、それをApache Solrに投げればいいだけなのですが、 ここでいくつか注意点があります。

1-a. XSS脆弱性を作らないようにすること

「検索」は、不特定多数のユーザが送ってきた文字列を検索結果としてWeb頁に表示することになります。そこで、 変なJavascript込みの検索文字列が送られてきたときに、それをそのまま表示してしまうと、Javascriptをその検索サイト 上で実行されてしまうことになります。flaskではテンプレートエンジンjinja2でそこら辺をうまく処理してくれている ようですが、よく注意するようにしてください。

1-b. 検索文字列のエンコード

Apache Solrに対してURLで検索問い合わせを行う場合、マルチバイト文字列(要するに日本語など)を送出しても うまく処理してくれません。これはURLエンコードしておく必要があります。一部の制御文字も同様です。たとえば前回記事で登場した以下のURL

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'

のうちの「q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22」の部分に注目してください。ここでは「:」が「%3A」に置き換えられていて、次に「"」が 「%22」に置き換えられ、その後、「富山」が「%E5%AF%8C%E5%B1%B1」に置き換えられています。検索問い合わせ用URLを作る際には このようにしてURLエンコードしなければなりません。(そうしないと、単にエラーが返ってきます)。

このための対策はpython3では比較的簡単です。 urllib.parse.quote()という関数がありますので、これを使って変換してやればいいだけです。もちろん、urllibモジュールのインポートも忘れてはいけません。

そうすると、たとえばAND 検索をしたい場合には、ANDの前後に空白を入れる必要がありますので、空白記号をURLエンコードして「'%20AND%20」という風にする必要がありますね。 この点もちょっと注意が必要ですが、どのタイミングでURLエンコードすればうまくいくのか、適宜試してみてください。

検索文字列がうまくできたら、次はurllib.request.urlopen(()で問い合わせして、返戻された結果を変数に入れておきましょう。たとえば以下のような感じですね。

base_url = 'http://127.0.0.1:8983/solr/minhon3/select?q='
query_fld = 'textConn%3A'
query_opt = '&facet.field=entry&facet=on&fl=entry&rows=10&facet.sort=count'
query_key = urllib.parse.quote('富山')

query_url = base_url +query_fld+query_key+query_opt

result = urllib.request.urlopen(query_url)

2. Apache Solrから返戻されたデータを受け取る

さて、次に、返戻されたデータの受け取りです。これはJSON形式で戻ってきますので、これをPythonのlist/dictの形式に変換できると非常に便利です。これは以下のようにすればOKです。

array_body = json.loads(result.read().decode('utf-8'))

そうすると、array_body の中に検索結果のJSONデータがすべて入りますので、あとはpython3でのlist/dictからデータを取り出す方法で必要なデータを取り出すことができます。 とりあえず print (array_body) してみて、どういう風にして欲しいデータを取り出すか考えてみてください。

なお、一つだけヒントを出しておくと、以下のようにすることで、docs以下のリストに入っている各dictを取り出して変数に入れることができます。

   for edoc in array_body['response']['docs']:
      obj_id = edoc['id']
      ent = edoc['entry']
      imgf = edoc['imageID']
      imgt = edoc['imageThumb']
      ent_id = edoc['entryID']

あとは、取り出したものをHTMLタグにうまく貼り込めればいいのですが、つまり、テンプレートにうまく渡せるようにすればいい、ということですね。

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形式で返ってくるので、見やすいという人も一部にはいると思いますが、普通の人にはあんまり見やすいものではないと思います。これを見やすい形に整形するということもそろそろやってみたいところですが、それは次回以降にしましょうか。

Apache Solr で異体字同時検索

さて、今回は異体字同時検索です。クラウドソーシング翻刻に限らず、字体・字形の選択ポリシーが一定していなかったり徹底されていなかったりということは、テキスト文字起こし全般においてしばしばみられます。普通にデータを作成していても、國學院大學を国学院大学と書いてしまったり、慶應義塾大学と慶応義塾大学が混在するという例は割と見られます。しかし、いちいち両方の単語を検索するのは大変ですし、Google検索や各地の検索システムでは、両方を同時に検索してくれることが結構多いように思われます。ではそれは特殊な技術かと言えばそんなことはなくて、たとえばApache Solrでは設定するだけでそれができるようになります。そこで、とりあえずその設定の仕方も含めて、一緒にやってみましょう。

ではまず、前回の手順をおさらいしつつ、今回の手順を確認してみましょう。

  1. Apache Solrをインストール&起動
  2. コアの作成
  3. 検索項目の設定(省略可)⇒今回はここを!
  4. 検索対象ドキュメントの登録

前回の手順では省略した、「3. 検索項目の設定」をきちんと設定するのが今回のポイントです。今回は、前回作ったminhonコアはそのままにしておきつつ、 新たに minhon2 というコアを作ることにして、以下、前回と重なるものは解説はざっくりするか飛ばしてコマンドのみ再掲します。

1. Apache Solrをインストール&起動

Apache Solrの最新版をダウンロードして圧縮ファイルを伸張し、そのディレクトリに入ってからPower ShellのターミナルかWSLターミナル、Macのターミナルなどを起動して、以下のようにしてSolrを起動。 Power Shellの場合は、

> bin/solr start

WSLターミナルの場合は(多分MacOSやLinuxでも同様に)

$ ./bin/solr start

※ startできないと言われたら、エラーメッセージにあわせて対応します。よくあるのはJAVAがない、あるいは、JAVA_HOMEがない、というエラーメッセージですが、JAVAをインストールしてない場合は、「無料Javaソフトウェアをダウンロード」からJAVA (runtime Environment)をダウンロード・インストールします。JAVA_HOMEがない、というエラーメッセージの場合は、JAVA_HOMEをウインドウズで設定します。JAVA_HOMEの設定は、ググると色々やりかたがでてきますのでそちらを参考にしてください。

2. コアの作成

Power Shellの場合は、

> bin/solr create -c minhon3

WSLターミナルの場合は(多分MacOSやLinuxでも同様に)

$ ./bin/solr create -c minhon3

3. 検索項目の設定(省略可)

ここまでは前回と一緒ですが、次が少々異なります。「3. 検索項目の設定(省略可)」を敢えて行います。

ここでの手順は、

3-1. フィールドタイプの設定 3-2. フィールドの設定

という風になります。つまり、「異体字検索用辞書を使って検索用インデックスを作るフィールドタイプ」というのを設定した上で、 「異体字検索用インデックスを検索したいフィールドに割り当てる」という手順になります。では以下でやってみましょう。

3-1. フィールドタイプの設定

「異体字検索用辞書を使って検索用インデックスを作るフィールドタイプ」を作成するのですが、このためには、まず、「異体字検索用辞書ファイル」を用意して、そのファイルパスを設定に組み込む必要があります。 そこで、まずは、空の「異体字検索用辞書ファイル」を作成しておきましょう。

Power Shellの場合

> mkdir server/solr/minhon3/dict > New-Item -Type File server/solr/minhon3/dict/itaiji.txt

WSL Ubuntu18やMac OS、Linux等の場合

$ mkdir server/solr/minhon3/dict $ touch server/solr/minhon3/dict/itaiji.txt

そうすると、以下のフォルダに、itaiji.txtというファイルができているはずです。(以下の画像は空のフォルダです)

f:id:digitalnagasaki:20200126180629p:plain
itaiji.txt

そこで、itaiji.txtを開いて、試しに以下の異体字ペアの内容を入力して、UTF-8で保存しておきましょう。記号も含めてこのまま入力してください。

"圖"=>"図"

"學"=>"学"

これで準備は終了です。

次に、この異体字検索用辞書ファイルを使って異体字同時検索のできるフィールドタイプを作成します。以下のようにコマンドをうってみましょう。

Power Shellの場合:

> curl.exe -X POST -H 'Content-type: application/json' --data-binary '{
    "add-field-type": {
        "name": "text_cjkuni",
        "class": "solr.TextField",
        "positionIncrementGap": 100,
        "analyzer": {
            "charFilters": [{
                    "class": "solr.MappingCharFilterFactory",
                    "mapping": "dict/itaiji.txt"
                }],
            "tokenizer": {"class": "solr.StandardTokenizerFactory"},
            "filters": [
                {"class": "solr.CJKWidthFilterFactory"},
                {"class": "solr.LowerCaseFilterFactory"},
                {
                    "class": "solr.CJKBigramFilterFactory",
                    "outputUnigrams": "true"
                }
            ]
        }
    }
}' http://localhost:8983/solr/minhon3/schema

WSL Ubuntu , Linux, MacOSなどの場合:

$ curl.exe -X POST -H 'Content-type: application/json' --data-binary '{
    "add-field-type": {
        "name": "text_cjkuni",
        "class": "solr.TextField",
        "positionIncrementGap": 100,
        "analyzer": {
            "charFilters": [{
                    "class": "solr.MappingCharFilterFactory",
                    "mapping": "dict/itaiji.txt"
                }],
            "tokenizer": {"class": "solr.StandardTokenizerFactory"},
            "filters": [
                {"class": "solr.CJKWidthFilterFactory"},
                {"class": "solr.LowerCaseFilterFactory"},
                {
                    "class": "solr.CJKBigramFilterFactory",
                    "outputUnigrams": "true"
                }
            ]
        }
    }
}' http://localhost:8983/solr/minhon3/schema

これで、フィールドタイプ text_cjkuni が作成されました。

3-2 フィールドの設定

次に、このフィールドタイプが適用されるフィールドを設定します。これまで作ってきたドキュメント登録用ファイルの中の textConn というフィールドに、これを割り当ててみます。以下のコマンドを打ってみましょう。

Power Shellの場合:

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

WSL Ubuntu , Linux, MacOSなどの場合:

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

これで、textConnフィールドに対しては、異体字同時検索がかかるようになります。

4. 検索対象ドキュメントの登録

今回は、minhon3.jsonという登録用ドキュメントをまとめたファイルを新たに作ってみました。

https://vayu.dhii.jp/minhon_search/minhon3.json

これは、テキストの内容は同じですが、id というフィールドを新たに追加しています。このidフィールドを登録ドキュメントに設定しておくと、 検索インデックスの上書き更新が可能になりますので後々便利です。

それでは、minhon3.jsonをダウンロードして、それが置いてあるフォルダでターミナルを開いて(あるいは、現在ターミナルで開いているフォルダにminhon3.jsonを持ってきて(お勧め!))、 以下のようにして、検索対象ドキュメントの入ったminhon3.jsonを、新しく作ったコア minhon3に登録してみましょう。

Power Shellの場合

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

WSL Ubuntu18やLinux、MacOS等の場合

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

これで、フィールド「textConn」は、上記の二つの異体字の対応づけが行われた状態の検索インデックスが作成されました。

さて、どうなったか確認してみましょう。

異体字同時検索の設定をしたtextConnで「"東京大學圖書"」を検索すると、以下のように"numFound":31、つまり、31件のドキュメントがヒットしています。「"東京大学図書"」でも同様です。(二重引用符で囲むのを忘れないようにしてください!)

f:id:digitalnagasaki:20200126174308p:plain
marge_result

一方、同じデータだが異体字同時検索の設定をしていないフィールド、textNoKTで検索すると(「df」項目に「textNoKT」と入力して検索)、以下のように、"東京大学図書"では23件、"東京大學圖書"では8件となります。あわせて31件ですから、ちょうどぴったりですね。

f:id:digitalnagasaki:20200126174849p:plain
noitaiji2

f:id:digitalnagasaki:20200126174654p:plain
noitaiji1

これが異体字同時検索の基本的なやり方です。

異体字のペアを後から追加する場合:

異体字のペアを増やしたければ、上記のitaiji.txtにペアを追加していけばいいのですが、こちらは複数文字でも対応してくれるようで、たとえば以下のものなどもできるようです。

"圕"=>"図書館"

ただし、ペアを増やしたら、Apache Solrを再起動してあげなければなりません。再起動は、以下のコマンドで行います。

> bin/solr restart -p 8983

(WSLとかMacでも一緒です)

そして、再起動しただけだと、矢印方向の変換しか効きません。これだと逆方向が検索できなくなってしまうので、「検索インデックスへの再登録」も行う必要があるようです。 ここで、先ほどの、「idを付与」した minhon3.json が活きてきます。再登録は、登録時と同じコマンドで大丈夫です。(もっと効率の良い方法があるかもしれませんが)

Power shellの場合は:

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

WSL Ubuntu やLinux、MacOSなどの場合は: $ curl "http://localhost:8983/solr/minhon3/update?commit=true" --data-binary "@minhon3.json" -H 'Content-type:text/json;charset=utf-8'

これで大丈夫かと思います。

複数文字対応ができるということは、単語・フレーズ単位での表記揺れのようなことにも対応できますし、さらに言えば、たとえば、"安震"=>"安政大地震"と登録すれば、「安震」で「安政大地震」を検索するようなこともできるようになります。

ということで、Apache Solrの異体字同時検索、ぜひお試ししてみてください。

Apache Solr でとりあえず全文検索

前回記事では、Apache Solrを起動するところまでいきました。起動しただけではなんともなりませんので、まずは全文検索をできるようにしてみましょう。

Apache Solrで全文検索をできるようにするためには、大体以下のようなプロセスがあります。

  1. Apache Solrをインストール&起動
  2. コアの作成
  3. 検索項目の設定(省略可)
  4. 検索対象ドキュメントの登録

このうち「3. 検索項目の設定」を行うことによって、より便利な検索ができるようになりますが、省略してもそこそこ便利な検索ができます。そこで、 まずは、ここをスキップして、「2.コアの作成」をした上で検索対象ドキュメントの登録をしてみましょう。

2. コアの作成

コアの作成は、Webインターフェイスからでもできるらしいのですが、後々のことを考えて、CUI(コマンドラインインターフェイス)でやってみましょう。前回記事のように、SolrのディレクトリでPower ShellやWSLターミナルなどを開いた上で、

Power Shellの場合は、

> bin/solr create -c minhon

WSLターミナルの場合は(多分MacOSやLinuxでも同様に)

$ ./bin/solr create -c minhon

としてみてください。そうすると、

「Created new core 'minhon'」などと表示され、「minhon」というコアができます。

コアができたどうかを確認するべく、再び http://localhost:8983/ にアクセスしてみましょう。

ここで、左側のメニューの「Core admin」をクリックすると、先ほど作った「minhon」が以下のように表示されるはずです。

f:id:digitalnagasaki:20200125133730p:plain
core admin

そして、左側のメニューの「Core selector」をクリックすると

f:id:digitalnagasaki:20200125133915p:plain
core selector

以下のように、先ほど作った「minhon」が選べるようになっているはずです。

f:id:digitalnagasaki:20200125134009p:plain
core selector minhon

これで、コアができました。そうしましたら、次は、このコアに対してドキュメントを登録していくことになります。

3. 検索項目の設定(省略可)

今回は省略します。省略すると、自動的に無難な項目設定が行われます。検索の利便性を高めたい場合には、これを自分で詳細に行うことになります。

4. 検索対象ドキュメントの登録

検索対象ドキュメントの登録は、Webインターフェイスからでもできるようですが、大量ドキュメントの登録にはあまり向いていないので、ここではCUIで行うことにします。

準備(Power Shellのための):

最近は、LinuxやMacOSでこういうことを行う場合、curlというコマンドを使うことが多く、割と便利なのですが、PowerShellでもインストールすれば使えますので、Power Shellで行う場合にはcurlをこちらからインストールしましょう。

(なお、Power Shellでは標準でもcurlというコマンドが使えますが、標準のコマンドはcurlではない別のコマンドのエイリアスになっているというおそろしい状態ですので、気をつけてください。Power Shellでcurlを他のOSと同様に使うためには、PATHの設定をすればいいのですが、エイリアスを外す操作も必要かもしれず、ちょっと面倒です。上記のcurlをインストールした後に curl.exeという風にコマンドを打てば、インストールした方を使ってくれるようですので、以下ではPower Shellの場合は curl.exe、それ以外は curlとして、それぞれコマンドをコピペできるように用意していきます。)

ドキュメントの用意:

最近では、この種のデータの登録はJSONでデータを作るのが一般的です。特にPythonを使っている場合、リスト+辞書を作ればほぼそのまま使えますので、色々楽です。今回は、「みんなで翻刻」の「石本コレクション」の翻刻データをベースにしてご用意したのがこちらです。「みんなで翻刻」の利用条件はCC BY-SAですので、形式を改変したものも CC BY-SAとしてご利用ください。このファイルを minhon.json というファイル名で保存したという前提で続けます。

Power Shellの場合:

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

WSLのUbuntu18やMac OS等の場合:

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

という風にすると、ドキュメントが minhonコアに登録されて検索できるようになります。検索は、まずは Webインターフェイスからやってみましょう。再び http://127.0.0.1:8983/ にアクセスしてみてください。

そして、 Core selector で「minhon」を選ぶと、以下のように登録ドキュメント数が表示されているはずですが、いかがでしょうか。(!この数字は、以下のものと皆さんのお手元のものは異なる場合がありますが、1以上でしたら大丈夫ですのであまり気にしないでください!)

f:id:digitalnagasaki:20200125141937p:plain
doc_number

これがうまくいっていたら、左側のメニューの「query」をクリックして、「Execute Query」ボタンをクリックしてください。そうすると以下のように、全てを対象にして検索した結果が表示されるはずです。検索結果は標準ではこのようにJSON形式で戻ってきます。"response"⇒"numFound"でヒットした件数が表示されているはずです。

f:id:digitalnagasaki:20200125142338p:plain
numfound

上記の画面でまずもう少し注目していただきたいのは、「q」というテキストエリアと「rows」というテキスト入力フィールドです。それぞれ「:」と「10」となっています。「rows」は、検索結果の表示数ですので、適宜増やしたり減らしたりしてください。(増やしすぎるとブラウザがフリーズするので注意してください)。「q」は、検索項目と検索語を入力できるようになっていて、「検索項目:検索語」という風に書くことになっているのですが、「:」で、すべての項目のすべての言葉、という風に検索できます。ここで例えば「project:"石本"」という風に入力して「Execute Query」ボタンをクリックすると、「project」項目に「石本」が含まれるドキュメントがヒットします。

f:id:digitalnagasaki:20200125143028p:plain
query_example

では次に、本文を検索してみましょう。本文のフィールドは、このデータではいくつか用意されていますが、textConnというのフィールドが一番検索しやすいものですので、「textConn:富山」として検索してみましょう。そうすると、以下のように68件ヒットします。富山が68件も!・・・と思ってヒットしたデータをよくみてみると、どうやら「富士山」も一緒にヒットしているみたいです。

f:id:digitalnagasaki:20200125145608p:plain
toyama1

つまり、文字を分割してそれらをAND検索してしまっているようです。これをきちんと「富山」として検索したい時は「textConn:"富山"」のように、検索語を二重引用符で囲めば、その文字列として検索してくれるようになります。

f:id:digitalnagasaki:20200125150123p:plain
toyama2

そうすると、「"numFound":3」となりますね。この検索結果には皆、文字列「富山」が含まれているはずです。

ここでもう一つ注目していただきたいのは、以下の黄色いマーカーで囲んだ箇所です。このURLが、この検索を行うためのURLということになります。

f:id:digitalnagasaki:20200125150555p:plain
searchurl

Power Shellなら

> curl.exe 'http://127.0.0.1:8983/solr/minhon/select?q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22'

WSL UbuntuやMacOS、Linux等では

$ curl 'http://127.0.0.1:8983/solr/minhon/select?q=textConn%3A%22%E5%AF%8C%E5%B1%B1%22'

という風にすると、JSONで検索結果が戻ってくるところを確認できると思います。

なお、JSON形式で得られるよりも、もっと簡単にエクセルなどに取り込んでしまいたい、という人には、検索メニューの中の「wt」というセレクタから「csv」を選択することもできますので、こちらも試してみてください。

f:id:digitalnagasaki:20200125151134p:plain
csv

ここでも、URLの箇所を確認してみてください。URLに「&wt=csv」というのが追加されていると思います。URLで検索結果を得る場合には、このようにしてパラメータを追加することで色々な形式のデータを得ることができます。

ということで、とりあえずドキュメントを登録して検索するところまではできるようになりました。今回はここまでとしましょう。

Apache Solr - 全文検索、異体字同時検索、ファセット…

ここ5年ほど、Webでの全文検索は、どこも大体似たような感じの機能に落ち着いてきているように思います。全文検索ができて、異体字も同時に検索ができて、ファセットから絞り込みができて…。

この背景には、Apache Luceneという全文検索ソフトウェアの存在があります。プログラミング言語Javaで書かれておりMac でもWindowsでもLinuxでも動くフリーソフトウェアです。とにかく速くて、我々が思いつく機能は大体用意されています。多言語対応も充実していて、日本語を分かち書きして単語ごとに検索する、といったこともできます。つまり、日本語分かち書きソフトも組み込まれているということです。これがあちこちのサイトで使われるようになっているのです。たとえば、最近話題のジャパンサーチでも検索にはこれを使っているそうです。

ところで、Javaで書かれているということは、手元のパソコンでも動かせるのではないか…?と誰もが思うところです。その通り、パソコン上でも動きます。色々な動かし方があるようですが、比較的簡単なのは、Apache Solrを使ってブラウザ経由で動かすという方法です。Apache SolrにはこのApache Luceneが組み込まれているので、SolrをインストールすればLuceneが使えるようになるという具合です。ちなみに、ジャパンサーチではElasticsearchを採用することでApache Luceneを使っているようです。基本的に、超大規模なものになるとElasticsearchを使った方がよさそうな感じです。

さて、このApache Solrですが、個人的経験としては、SAT大蔵経テキストデータベース 2018年版を作った時に採用しておりまして、ここで採用するまでには色々な試行錯誤をしておりました。もちろん、Elasticsearchも試してみた、というか、別の試験的なシステムに採用してみたことがありますが、Apache Solrの方が枯れていてわかりやすいような感じがしたので、こちらにしました。そこら辺の経緯は『デジタル学術空間の作り方』(オープンアクセス本)のChapter 1にも書いていますので、そういうことに興味がおありでしたらぜひご一読ください。

その後、最近になって、前回記事にてご紹介した「みんなで翻刻」の検索システムの開発に際して採用しました。と言ってもこちらは非常に簡素な使い方で、ファセットも使ってないようなものですが、せっかくですので少しApache Solrの使い方などをご紹介しておきたいと思います。これは、ググってみて、新しくてまとまった解説があんまりないことがちょっと気になったということもあります。

Apache Solrですが、使うためにはJava環境をインストールする必要があります。Mac OSとかWindows10のWSLだとJava環境も含めて比較的簡単にインストールできると思いますが、Windows10の通常のWindows環境ですと、環境変数JAVA_HOMEの設定をしなければならないようで、微妙に大変でした。とにかく、「java環境 インストール」などとしてググると色々やり方が出てきますので、そちらをご参照ください。WSLでUbuntu18.04だと多分こういう感じです。 $ sudo apt install -y openjdk-11-jdk

次に、Apache Solr のダウンロードです。こちらの頁から、「most recent Apache Solr release.」などと書かれたバージョンのBinary releasesのtar.gzかzipファイルをダウンロードします。tar.gzと言われてなんだかよくわからない場合はzipの方をダウンロードするのがおすすめです。現在の最新安定版は8.4.1ですので、たとえばこちら

なお、ここからはWindows 10とWindows 10 WSLのUbuntu18.04環境の話をしていきますが、MacOSは多分WSLと大体同じやり方でできるのではないかと思いますので適宜読み替えてください。

さて、ファイルをダウンロードしたら圧縮ファイルを伸張してそのフォルダに入ります。このとき、おそらく、ターミナルでCUI環境でないとうまく動かないのではないか…と思いますので、このフォルダでターミナルを開いてみましょう。 Windows10の場合はパワーシェルのターミナルを開くことになると思います。パワーシェルの場合は、エクスプローラ画面の何もないところをシフトキー+右クリックすると以下のようなダイアログが現れますので、「PowerShellウインドウをここで開く」を選択するとパワーシェルが開きます。 f:id:digitalnagasaki:20200123034751p:plain

パワーシェルを開くと以下のように、文字ベースでコンピュータに命令を出せるようになります。

それから、Windows10の場合は、このあたりでこちらからcurlをインストールしておいて、パワーシェルから使えることを確認しておいてください。

WSLであれば、現在のところ、大体以下のようになっていると思います。(なお、今回用意した事例で使っているApache Solrのバージョンは8.4.0で、一つだけ古いです。)

f:id:digitalnagasaki:20200123033521p:plain

そこで、以下のように、コマンドを入力してEnterキーをおしてみます。

PowerShell:

\solr-8.4.0> bin/solr start

WSL+Ubuntu18.04:

nagasaki@LV8:solr-8.4.0$ ./bin/solr start

そうすると、色々なメッセージがでてきます。メッセージが止まったなと思ったら、以下のURLにWebブラウザでアクセスしてみましょう。

http://localhost:8983/

ここで、このサイトのApach Solrの管理画面にアクセスできるようになります。

ところで、眠くなってきてしまったので、続きはまた次回としたいと思います。引き続き、よろしくお願いいたします。

みんなで翻刻のWeb API!

Web APIという言葉はあちこちで耳にされることがあると思います。API=アプリケーション・プログラミング・インターフェイス、ということで、つまり何かを作ったり動かしたりする時にやりとりをする機能、をWebで使えるようにしたもの、と考えておけばいいでしょうかね。Webブラウザの裏側でサーバ同士でデータをやりとりできるというのが肝で、各所のデータを統合した便利な機能を提供してくれたりするのはまさにWeb APIの恩恵によるものです。

世はWeb API花盛りです。花盛り過ぎて、それについて話題にすることもあまりなくなってきてはや10年、といったところです。あってもなくても、それはそれぞれのポリシーによる選択の結果をあらわしているに過ぎない、というところまで時代は進んでいるのかなと思っております。

しかしながら、一方で、開発途中のシステムだと、「Web APIも用意したけどあとまわし」ということも散見されます。また、Web APIを用意しても、「どういう構造になっているのか」「どういう風に使えるのか」を知らしめる仕様書のようなものが公開されていないと、せっかく用意したのにあまり使ってもらえないということもあります。逆に、あまり広く使われすぎると困るので仕様書は広く公開しないようにしていることもあるようです。

前置きが長くなりましたが、前回記事で紹介した「みんなで翻刻⇔みんなで検索」が実現できたのも、このWeb APIのおかげです。開発者である橋本雄太氏が、ご多忙な中で1時間でささっと作ってくださいました。ここでのWeb APIはそれほど複雑なものではなく、しかし、本文検索サイトを構築しつつ本家に更新があったらそれを検知して検索インデックスを更新する、という仕組みを作成するにあたって必要となる機能を大体網羅しているものでした。

というわけで、今回は、みんなで翻刻Web APIの紹介を少しだけ試みます。

まず、「現在登録されているプロジェクトの一覧」を取得するWeb API (のURL)は下記の通りです。

https://us-central1-honkoku2.cloudfunctions.net/api/projects

こちらにアクセスすると、プロジェクトの一覧とそれぞれの説明等に加えて、プロジェクトのIDが返戻されます。このフォーマットはWebでよく用いられるJSONというフォーマットになっています。これは、このまま見てもごちゃっとしてよくわかりませんが、全部コピペしてJSON整形ツールにかけると、構造を反映した形で見やすく表示できます。たとえばこのサイトだと以下のような感じになります。

f:id:digitalnagasaki:20200106210839p:plain
JSON画像

つまり、「みんなで翻刻(以下、「みん翻」と略します)」で現在展開されているプロジェクトの情報を得たければ、このWeb APIで取得できる、ということになります。

さて、筆者が今回作ったのは「本文検索」でした。そうすると、ここで取得できる情報だけでは検索ができません。「みん翻」のデータは、

プロジェクト>コレクション>エントリ>各頁(画像)

という風になっているようですので、次に、個々のプロジェクトが持っている「コレクション」の情報を取得します。この際には、上記のWeb APIで得られる各プロジェクトの"id"が必要になります。 これを何らかの方法で取得して、今度は以下のURLにてアクセスします。たとえば、「日本の仏典を翻刻!」のプロジェクトの場合、idは「21dzk」ですので、URLの最後に以下のようにそのプロジェクトidを付与してアクセスします。

https://us-central1-honkoku2.cloudfunctions.net/api/projects/21dzk

そうすると、このプロジェクトがどういうコレクションを持っているかという情報が得られます。これもやはり、先ほどと同じJSON形式で返戻されます。コレクションにはその説明もありますが、今回必要なのは本文ですので、さらに下の階層へと向かいます。このコレクションに含まれる(=下位にある)エントリを取得すべく、とりあえずここで入手できたコレクションのidを用いて以下のようにWeb APIにアクセスしてみます。ちなみに、以下のURL中の「2AC632E5683C37F7C69425EA497007FA」という箇所が、上記のURLから得られたコレクションのidです。

https://us-central1-honkoku2.cloudfunctions.net/api/collections/2AC632E5683C37F7C69425EA497007FA

このURLにアクセスして返戻されたJSONデータを整形すると以下のようになります。

f:id:digitalnagasaki:20200106223750p:plain
jsonデータの画像

ここで、"entries": となっているキーの値が複数で列挙されています。これらの値がそれぞれのエントリのidということになります。この中の一つのエントリidを使って以下のURLを作ってアクセスしてみます。

https://us-central1-honkoku2.cloudfunctions.net/api/entries/6AC3A275993D8AC73941D97376426A61

ここでは、「6AC3A275993D8AC73941D97376426A61」がエントリidの部分になります。

このURLにアクセスすると、このエントリに含まれる画像とそれに対応する翻刻テキストのデータが入手できます。あとは、このテキストデータを検索できるようにして、画像ともリンクできるようにすればOKです。ちなみに、翻刻テキストデータは"transcriptions"以下にリストとして入っていて、各リストは辞書になっていて、その中の「text」というキーが翻刻テキストデータのようです。また、更新日付のキーは「timestamp」、といった案配です。画像に関しては、IIIF対応画像の翻刻をシステムの基盤の一つとしていることもあるのかもしれませんが、IIIF Manifestを継承した形になっているようで、これをきちんと理解しようと思うなら、IIIF Presentation API 2.1をみておくとよいかもしれません。

というわけで、今回は、Web APIで本文を取得する方法を最短距離でまとめてみました。

エントリのJSONデータからどのデータを取り出してどのように使ったか、という話は、また次の機会としましょう。