検索した語の前/後のn文字をグループ化&ヒット数の多い順にリスト@みんなで翻刻サーチ

 本日は、顧問としてお手伝いしている大学院生たちのデジタルヒストリー研究グループ、ToDHによるシンポジウムがあり、その関係で朝からお仕事でした。シンポジウムは大阪大学と九州大学からの招待講演の先生方お二人の深いお話と、主に大学院生達による新鮮な発表とで、とても充実したものであったように思われました。彼らの成長ぶりを確認できたという意味でもうれしいことであり、これからもさらに自ら高めつつ、相互に高め合っていただければと思うところです。コロナウイルスへの対応として、Youtube配信をメインとした開催になりましたが、これは大向一輝氏による安定の配信でした。会の模様はtwitterのハッシュタグ等でもある程度確認できるようです。

twitter.com

ところで、シンポジウムの最中、地震史料のテキスト分析に関する発表を聞いていて、近世以前のテキストの分析ってやっぱり形態素解析のところですでに難しさがあるよね…と思って、みんなで翻刻サーチへの機能追加ですが、「検索した語の前か後のn文字をグルーピングしてヒット数の多い順にリストする」というものを作ってみました。

たとえば、以下のように「通り」で検索した後に、

https://honkoku.dhii.jp/search/?url_key=%E9%80%9A%E3%82%8A&url_var=1&url_proj=%E3%81%99%E3%81%B9%E3%81%A6&url_col=%E3%81%99%E3%81%B9%E3%81%A6

以下のように、「前の2文字をグループ化」してみると、うら通り: 9件、本橋通り: 8件、本郷通り: 7件…という風になります。

f:id:digitalnagasaki:20200223235115p:plain

「本橋通り」はちょっと変なのでもう1文字増やしてみると…「日本橋通り: 8件」という風になりました。

f:id:digitalnagasaki:20200223235306p:plain

 N-gram分析にしたらいいじゃないかという声が聞こえてきそうではありますが、とりあえず探索的な使い方ができるといいのではと思って作ってみたのでした。

他にもたとえば、「阿弥陀」の次の1文字は何が多いかな…という風なことを確認してみたり、

f:id:digitalnagasaki:20200223235508p:plain

どんな菩薩が多いかな…と確認してみたり、と色々できます。

f:id:digitalnagasaki:20200223235620p:plain

形態素解析の精度が高いのであれば、いちいちこんなことをしなくてもOKですので、むしろ形態素解析ソフトを組み込んでみようかとも思うのですが、まあとりあえずこれはこれで、ということで、よかったらお試ししてみてください。

今更 jQueryですが、簡単な要素の表示/非表示程度なら…

Web頁をインタラクティブに見やすくするための工夫としてJavascriptを使うのはもうごく一般的な手法になっています。Javascriptは、そのまま書くのはちょっと大変なので書きやすくするための工夫が様々に凝らされています。ライブラリやフレームワークが各地で開発公開されており、百花繚乱といった状況です。中でも、大規模開発が広まるなかで、React、Angular、Vueの3つがよく取り沙汰されるようになってきています。筆者もVueを勉強中で、少しその良さがわかってきたところですが、一方で、ちょこっと何かをしたい、くらいのことであれば、こういう大がかりなものを使わなくてもよいのではという気持ちになることも少なくありません。  少し前まで、Javascriptをちょこっと使ってギミックを用意する時によく使われていたJavascriptのライブラリに、jQueryというライブラリがあります。多分、まだ現役です。Japanese Woodblock Print Search - Ukiyo-e Search というサイトを作ってしまうほど浮世絵好きなプログラマのJohn Resigさんが作り始めたもので、現在でも、読売新聞や首相官邸のサイトなど、我が国の主要Webサイトではまだまだ現役です。CSSを知っていると、あと一歩知識を深めるだけで使えてしまうという手軽さがありますので、筆者も10年ほど前から非常に重宝していました。  さて、どういう風に手軽かと言えば、たとえば みん翻検索 by SAT で使っている「資料群名をクリックすると資料の一覧が表示される」ものですが、これは、

1.「資料群名のタグにclassを与える」 2.「資料一覧を最初は隠しておく」 3.「資料群名のclassを持つテキストをクリックすると資料一覧を表示する」 4.「資料一覧がすでに開いている時は閉じる」

というような感じに書くことができます。

具体的な書き方ですが、まず、jQueryのファイルを読み込んでおく必要があります。jQueryのサイト⇒ jQuery から https://code.jquery.com/jquery-3.4.1.min.js をダウンロードして、適当なディレクトリ(フォルダ)に置いておきます。flaskを使っている場合はstaticディレクトリに 入れておくのがよいでしょう。その後、templates/index.htmlからこのファイルを読み込めるように、以下のタグを<head>~</head>の中に入れておきます。以下のような 感じです。flaskでない場合は、HTMLファイルからこのjquery-4.3.1.min.jsを読み込めるように適切なパスを書いてください。

<script src="/static/jquery-3.4.1.min.js"></script>

その後、<head>~</head>の中に、Javascriptを書いておくのですが、その前に、HTMLの方をどういう風に記述するか考えておく必要があります。

上に書いたようなことを実現するためには、以下のようにして、資料群名、資料一覧にclassをつけて、両者を操作できるようにしておきます。classにしているのは、 これが複数登場した時にも同じプログラムで処理できるようにするためです。1文書の中で1つしか操作しないことが明らかな場合にはclassの代わりにidをつけてもよいです。 さらに、資料群名と資料一覧をまとめて操作できるようにするために、両者を包むdivを作り、それにもclassをつけておきます。さらについでに、資料一覧(の要素)は、 デフォルトでは非表示にしておきます。

<html>
(略)
<body>
(略)
<div class="group">
  <div class="group_name">資料群の名前</div>
  <div class="witnesses" style="display:none">
    <ul>
      <li>資料一つ目
      <li>資料二つ目
    </ul>
  </div>
</div>
</body>
</html>

このHTMLタグに対して操作を行うのであれば、「class:group_nameがクリックされたら、兄弟要素であるclass:witnessesを表示する」という風に書くことになります。 そこで、今度は、<head>~</head>の方に戻ります。先ほど書いたjqueryを読み込むタグの次に、以下のように書いてみましょう。

<script>
<script>
$(function(){
  $(".group_name").click(function(){
    if($(this).siblings(".witnesses").css('display') == 'none'){
      $(this).siblings(".witnesses").show();
    }
    else{
      $(this).siblings(".witnesses").hide();
    }
  });
});

</script>

そうすると、今目指していた機能は実現できるはずです。操作対象となる要素の指定はCSSのセレクタとほぼ同様で(これがjQueryのよさです)、 classを指定したければ .group_nameや .witnessesのように . をつけます。idや要素名の指定の仕方は自分で調べてみましょう。 その他諸々、上記のスクリプトにはjQueryの大事な要素がたくさん詰まってますので、jQueryの教科書などがあれば見ながら 確認してみてください。

次に、もう少し工夫するべく、CSSをいじってみましょう。資料群名のテキストには、カーソルをあてたら文字の色が変わってくれると うれしいですね。

CSSを書く時は、<head>~</head>の中の、>scrpt<の前に書くのが比較的一般的です。ついでに、上の方で書いた style="display:none" もこちらに書いてしまいましょう。

<html>
<head>
(略)
<sytle>
.group_name:hover{color:blue}
.witnesses{display:none}
</style>
<script>
(略)

大体、こういう感じで必要最低限のことはできるかと思います。そこから先は、私のgithubを探してみてください…。

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

今回は、以下の企画の続編です。

digitalnagasaki.hatenablog.com

が… facet.pivotという機能を使った方が断然楽であるようですので、それを使って みんなで翻刻サーチに資料単位での絞り込み機能をつけてみたところです。 たとえば、以下のリンクのような感じです。

みん翻検索 by SAT

これはとっても簡単で、たとえば以下のようなURLでローカルのApache Solrにアクセスしてみると、

http://127.0.0.1:8983/solr/minhon3/select?facet.pivot=project_st,collection_st,entry_st&facet=on&fl=entry&q=textConn%3A%E9%98%BF%E5%BC%A5%E9%99%80&row=-1&facet.pivot.mincount=1

以下のような結果を得られます。

{
  "ResponseHeader": {
    "Status": 0,
    "QTime": 4
    "Params": {
      "Q": "textConn: 阿弥陀"
      "Facet.pivot": "project_st, collection_st, entry_st"
      "Fl", "entry",
      "Row": "- 1"
      "Facet", "we"
      "Facet.pivot.mincount": "1"}}
  "Response": { "numFound" 4, "start" 0, "Doc": [
      {
        "Entry": "[富士山 図]"},
      {
        "Entry": "じ し ん 百万 遍"},
      {
        "Entry": "じ し ん 百万 遍"},
      {
        "Entry": "兩 界 圖 位 (京都 東 寺觀 智 院 藏 本)"}]
  }
  "Facet_counts": {
    "Facet_queries": {}
    "Facet_fields": {}
    "Facet_ranges": {}
    "Facet_intervals": {}
    "Facet_heatmaps": {}
    "Facet_pivot": {
      "Project_st, collection_st, entry_st": [{
          "Field": "project_st"
          "Value": "翻 刻 石 本 コ レ ク シ ョ ン!"
          "Count": 3
          "pivot":[{
              "Field": "collection_st"
              "Value": "ス テ ー ジ 1"
              "Count": 1,
              "pivot":[{
                  "Field": "entry_st"
                  "Value": "じ し ん 百万 遍"
                  "Count": 1}]},
            {
              "Field": "collection_st"
              "Value": "ス テ ー ジ 2"
              "Count": 1,
              "pivot":[{
                  "Field": "entry_st"
                  "Value": "じ し ん 百万 遍"
                  "Count": 1}]},
            {
              "Field": "collection_st"
              "Value": "ス テ ー ジ 5"
              "Count": 1,
              "pivot":[{
                  "Field": "entry_st"
                  "Value": "[富士山 図]"
                  "Count": 1}]}]},
        {
          "Field": "project_st"
          "Value": "日本 の 仏 典 を 翻 刻"
          "Count": 1,
          "pivot":[{
              "Field": "collection_st"
              "Value": "大 正 新 脩 大 藏經 図 像 編"
              "Count": 1,
              "pivot":[{
                  "Field": "entry_st"
                  "Value": "兩 界 圖 位 (京都 東 寺觀 智 院 藏 本)"
                  "Count": 1}]}]}]}}}

この場合、ピボットとしてデータを取り出せるだけでなく、単なるファセット機能での出力結果と比べて、値と登場数をもう少し便利な形で取り出すことができますね。 こういうものもうまく使えると、検索がより便利になりそうです。

なお、みんなで翻刻サーチでは、検索結果を全部HTMLに変換した上で、jQueryを使って一部非表示にしたりクリックして表示できるようにしたりしています。flaskとの組み合わせだと こういう組み込みはとても楽にできますね。

Evasion of breathing tax: editing TEI with VSCode

This article introduces how to set up VSCode so that any user can edit TEI/XML relatively easy by free, based on a brief report by Yifan Wang who is graduate student of the University of Tokyo and junior fellow in the International Institute for Digital Humanities.

I strongly recommend to use Oxygen XML Editor for TEI, if you have enough budget due to its tremendous usability.

Breathing with VSCode would not be easier than with Ox, but we can swim.

The setting process is blow:

  1. Download and install VSCode.
  2. Download and install Java.
  3. Configure JAVA_HOME.
  4. Download and install XML extension for VSCode.
  5. Edit settings.json in VSCode.
  6. Restart VSCode
  7. install XML Tools
  8. Restart VSCode
  9. Try to open and edit a TEI/XML file.
  10. Install vscode-htmltagwrap
  11. Restart VSCode

Here we go!

1. Download and install VSCode.

Do it from the URL below:

https://azure.microsoft.com/en-us/products/visual-studio-code/

If you want to use non-English version, you may find a language pack as an extension of VSCode.

2. Download and install Java.

Do it from the URL below:

You can download and install Java according to the instruction in the URL below:

How do I install Java ?

3. Configure JAVA_HOME.

This setting is a little difficult for beginners and different between Windows and MacOS.

For Windows, see below:

javatutorial.net

For MacOS, see below:

www.appsdeveloperblog.com

During this process, note the value of your JAVA_HOME (that is, the path of Java installed in your PC).

4. Download and install XML extension for VSCode.

Install XML extension on VSCode.

  1. Click "Extension" icon on the left-side bar.

  2. input "XML" in the input from

  3. Click "install" button surrounded by green in the "XML" extension provided by Red Hat.

f:id:digitalnagasaki:20200215041719p:plain

5. Edit settings.json in VSCode.

This step is most difficult in this process. Please do it carefully.

  1. After installing "XML" extension, click "setting" icon, then, select "Extension settings"

f:id:digitalnagasaki:20200215042802p:plain
open extension settings

  1. Find "Edit in settings.json" link on the center window and click it.

f:id:digitalnagasaki:20200215043151p:plain
click settings.json

  1. add "xml.java.home": with JAVA_HOME value in your PC. (You've already had it above.)

For example, "xml.java.home": "C:\Program Files\Java\jdk-13.0.1", is my case.

f:id:digitalnagasaki:20200215043345p:plain
settings.json

6. Restart VSCode

Restart your VSCode. After that, XML file can be validated on VSCode.

7. install XML Tools

Install "XML Tools" extension like below:

f:id:digitalnagasaki:20200215033026p:plain
xmltools

8. Restart VSCode

Restart your VSCode. After that, some convenient functions for XML are available like below:

f:id:digitalnagasaki:20200215044111p:plain
outline of XML

9. Try to open and edit a TEI/XML file.

Open a TEI file by this VSCode. Probably it would not work well because it can not treat Relaxng file as the schema. Then, replace your tag with the following:

<TEI xmlns="http://www.tei-c.org/ns/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.tei-c.org/ns/1.0 https://tei-c.org/Vault/P5/current/xml/tei/custom/schema/xsd/tei_all.xsd">

Then, you will be able to find what we want like below:

f:id:digitalnagasaki:20200215045004p:plain
tag candidates

10. Install vscode-htmltagwrap

So far, we don't have the "Ctrl-e" (or Command-e). We can not find the same function, but find a similar one as vscode-htmltagwrap.

f:id:digitalnagasaki:20200215030359p:plain
install htmltabwrap

See the usage carefully.

11. Restart VSCode

You must restart VSCode again to activate the extension.

12. Install Auto rename tag

"Auto rename tag" extension is also necessary for us. Install it and restart VScode:

f:id:digitalnagasaki:20200215035944p:plain
auto rename tag

As we might need some more functions, we will survey furthermore.

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