使ってみよう

[トップページに戻る]
1. はじめに
2. インストール
2.1 パッケージを展開する
2.2 インストールする
2.3 インストールファイルを確認する
2.4 アンインストールするには
3. サンプルコードとデータ
4. SQLでサンプルを実行する
4.1 データベースとテーブルを作る
4.2 データを登録する
4.3 全文索引を作る
4.4 likeで部分一致検索する
4.5 ランキング検索を試してみる
4.6 自然文検索を試してみる
5. Javaでサンプルを実行する
5.1 URL
5.2 データベースを準備する
5.3 検索を実行する
6. Pythonでサンプルを実行する
6.1 データベースを準備する
6.2 検索を実行する
7. データベースを削除する
8. うまく動かないとき
9. 青空文庫の全データで検索する
9.1 青空文庫の作品データを取得する
9.2 青空文庫の書誌情報を取得する
9.3 データを加工する
9.4 データを登録する

1. はじめに

DoqueDBをお試しいただき、まことにありがとうございます。
この文書では、DoqueDBのインストールからさまざまな検索の実行方法までを、 青空文庫のデータを使いながら、順を追って駆け足で解説していきます。 DoqueDBの検索機能について、おおまかな感触をつかんでいただければ幸いです。
途中で処理に失敗するときは 8. うまく動かないとき をご覧ください。

DoqueDBに収録されている青空文庫のデータはごく一部ですが、 青空文庫からご自身でデータを取得することにより、 そのデータを対象に検索をお試しいただくことが可能です。 詳細については 9. 青空文庫の全データで検索する をご覧ください。

2. インストール

インストール作業はroot権限のあるユーザーで行う必要があります。 rootでログインするか、sudo su -でrootユーザーになってください。 これ以降、rootユーザーで実行することをプロンプト「#」で示します。

$ sudo su -
#

2.1 パッケージを展開する

DoqueDBのインストールパッケージはdoquedb-<バージョン>.tar.gzです。 任意の作業ディレクトリで、以下のように展開してください。

# tar xvf doquedb-<バージョン>.tar.gz

作業ディレクトリにサブディレクトリdoquedb-<バージョン>/が作られ、 その中にいくつかのディレクトリとファイルが作られます。

conf.shにはDoqueDBのプログラムや設定ファイルが置かれるインストールディレクトリのパス、 データベース本体が置かれるパス、 クライアントがDoqueDBと通信するポート番号などが書かれています。 必要に応じて編集してください。 インストールディレクトリやポート番号を変更した場合は、 以降の章をそれに応じて読み替えてください。

# cd doquedb-<バージョン>
# vi conf.sh
...
installpath=/var/lib/DoqueDB
databasepath=/var/lib/DoqueDB/db
portnumber=54321

ユーザーズマニュアルを含む各種HTML文書は以下のURLにあります。
https://doquedb.github.io/doquedb/index.html

2.2 インストールする

展開したパッケージをインストールし、サービスを開始します。

  1. install.shを実行します。
    必要なファイルがインストールディレクトリにインストールされます。
    # ./install.sh
    Install Objects
    
  2. setup.shを実行します。
    データベースが初期化されます。
    # ./setup.sh
    Install Mod Parameter
    Install Default Parameter
    Install System Parameter
    Initialize Database
    
  3. サービスを開始します。
    <インストールディレクトリ>/binに移動し、 doquedbスクリプトに引数startを指定して実行します。
    # cd /var/lib/DoqueDB/bin
    # ./doquedb start
    SydServer starting (root) ... done.
    

2.3 インストールファイルを確認する

インストールディレクトリの下には以下のようなディレクトリがあります。

2.4 アンインストールするには

DoqueDBをアンインストールするには、以下の手順を実行します。

  1. サービスを停止します。
    # cd /var/lib/DoqueDB/bin
    # ./doquedb stop
    
  2. インストールされたファイルをすべて削除します。
    パッケージを展開したディレクトリがある場合は、 その中でunsetup.shとuninstall.shをその順に実行します。
    # cd <パッケージを展開したディレクトリ>
    # ./unsetup.sh
    Uninstall Database
    # ./uninstall.sh
    Uninstall Objects
    
    アンインストール後にディレクトリやいくつかファイルが残ることがあります。 必要であれば、以下のように手動で削除してください。
    # rm -fr /var/lib/DoqueDB
    

3. サンプルコードとデータ

DoqueDBの検索機能をお試しいただくため、青空文庫の一部のデータをパッケージに収録しています。 青空文庫は、主に著作権の切れた文学作品を電子化して公開しているサイトです。 詳細については以下のページをご覧ください。

サンプルコードとデータはインストールディレクトリ下のdoc/sample/に置かれています。 内容は以下のとおりです。

doc/sample/
  README.txt            サンプルディレクトリの説明文書
  data/
    insert.csv          青空文庫サンプルデータの書誌事項
    OUTPUT/*.txt        青空文庫サンプルデータの本文
    DATA/*.zip          OUTPUT/*.txtの加工前データ
  sqli/
    setup.sh            SQLでデータベースを準備するスクリプト
    likeSearch.sh       SQLでlike検索を実行するスクリプト
    rankSearch.sh       SQLでランキング検索を実行するスクリプト
    sentenceSearch.sh   SQLで自然文検索を実行するスクリプト
  Java/
    Setup.Java          JDBCでデータベースを準備するプログラム
    SentenceSearch.Java JDBCで自然文検索を実行するプログラム
  Python/
    setup.py            Pythonでデータベースを準備するプログラム
    sentence_search.py  Pythonで自然文検索を実行するプログラム

以降の章ではSQLインタプリター、Javaプログラム、Pythonプログラムで データベースと全文索引を作成し、さまざまな検索を実行します。 説明を簡単にするため、データベース操作はすべて管理者ユーザーの rootで実行していることをご承知ください。

4. SQLでサンプルを実行する

SQLインタプリターを使ってサンプルデータをデータベースに登録し、 いくつかの検索方法で検索を実行します。
以降ではLinuxの一般ユーザー権限で実行するため、 doc/sample/以下を適当なディレクトリにまるごとコピーします。 (一般ユーザーで実行することをプロンプト「$」で示します。)

$ mkdir ~/doquedb
$ cp -rp /var/lib/DoqueDB/doc/sample ~/doquedb
$ cd ~/doquedb/sample/sqli

4.1 データベースとテーブルを作る

以下の手順でデータベースを準備します。

最初にデータベースsampleSqliを作り、その中にテーブルAozoraBunkoを作ります。 カラムは作品ID(docId), 作品名(title), 作者の姓(lastName)および名(firstName), 青空文庫のXHTML/HTMLファイルURL(url), 本文データ(content)で、 作品IDをプライマリキーとします。

$ sqli_cmd="/var/lib/DoqueDB/bin/sqli -remote localhost 54321
    -user root -password doqadmin -code utf-8"
$ $sqli_cmd -sql "create database sampleSqli"
$ query="create table
    AozoraBunko (
        docId int,
        title nvarchar(256),
        lastName nvarchar(128),
        firstName nvarchar(128),
        url varchar(128),
        content ntext,
        primary key(docId)
    )"
$ $sqli_cmd -database sampleSqli -sql "$query"

4.2 データを登録する

次に、バッチインサートでデータを登録します。 データの書誌事項はinsert.csvから読み込まれます。 このファイルは青空文庫の 「公開中 作家別作品一覧拡充版:全て(CSV形式、zip圧縮)」 を加工したもので、各行は次のような形をしています。

000040,"歯車","はぐるま","はくるま","","","","「文藝春秋」1927(昭和2)年10月","NDC 913","新字旧仮名","なし",1998-04-27,2014-09-17,"https://www.aozora.gr.jp/cards/000879/card40.html","000879","芥川","竜之介","あくたがわ","りゅうのすけ","あくたかわ","りゆうのすけ","Akutagawa","Ryunosuke","著者","1892-03-01","1927-07-24","なし","現代日本文学大系 43 芥川龍之介集","筑摩書房","1968(昭和43)年8月25日","1968(昭和43)年8月25日初版第1刷","","","","","","","","","","","","","j.utiyama","かとうかおり","https://www.aozora.gr.jp/cards/000879/files/40_ruby_310.zip","2004-03-14","ShiftJIS","JIS X 0208","2","https://www.aozora.gr.jp/cards/000879/files/40_15151.html","2004-03-14","ShiftJIS","JIS X 0208","0","000879_000040.txt",FILE OUTPUT/000879_000040.txt

バッチインサートを行うinsert文を以下に示します。 insert.csvはフルパスで指定する必要があります。 カラム57は本文データで、"FILE "で始まっているため、 1件ごとにOUTPUTフォルダ内のファイルから個別に読み込まれます。

$ query="insert into AozoraBunko
    input from path '<insert.csvのフルパス>'
    hint 'code=\"utf-8\" InputField=(1,2,16,17,51,57)'"
$ $sqli_cmd -database sampleSqli -sql "$query"

4.3 全文索引を作る

その後、本文データのカラムcontentに全文索引を作ります。

$ query="create fulltext index INDEX1 on AozoraBunko(content)
    hint 'kwic,
    delayed,
    inverted=(normalized=(stemming=false, deletespace=false),
    indexing=dual,
    tokenizer=DUAL:JAP:ALL:1 @NORMRSCID:1 @UNARSCID:1)'"
$ $sqli_cmd -database sampleSqli -sql "$query"

hint以降はヒント句で、全文索引の挙動を決めるために指定しています。 ここで指定している値は以下のものですが、詳細についてはユーザーズマニュアルの 5.5.5 ヒント句H.1 全文索引のスキーマ定義をご覧ください。

ヒントオプション 説明
kwic 検索結果からkwic関数で検索語を含む部分文字列を取得できるように、
必要な情報を作成して索引に格納します。kwic関数を使うと、
長いテキストのどこに索引語が出現したかを見やすく示すことができます。
kwic関数で参照するカラムはcontains述語の対象でなければなりません。
delayed 全文索引の作成には時間がかかるので、遅延更新でまとめて更新します。
inverted= 全文索引は転置索引という仕組みを使って実装されています。
invertedは転置索引に関するヒントを指定します。
normalized=
(stemming=false,
deletespace=false)
normalizedは索引語を正規化するかどうかを決めるヒントです。
ここではステミング(※)とスペース削除を行わない設定としています。
indexing=dual indexingでは索引タイプを指定します。
dualは英字列についてはWORD(単語単位)で、日本語文字列については
NGRAM(文字列単位)で索引語を生成します。
tokenizer=
DUAL:JAP:ALL:1
@NORMRSCID:1
@UNARSCID:1
tokenizerではトークナイズパラメーターを指定します。
JAP:ALL:1は日本語を対象とし、全文字種を1文字ごとに切り出します。
テキスト量が比較的少ない(1GB以下)ときに向いています。
@NORMRSCIDと@UNARSCIDは正規化および形態素解析に用いる
辞書を指定します。現在はID:1の標準リソースのみが使用可能です。

サンプルのsqliディレクトリにあるsetup.shを実行すると、 ここまでの処理を行うことができます。

4.4 likeで部分一致検索する

本文データ(content)からlike述語で部分一致検索を実行してみます。

$ query="select docId, title, lastName, firstName, substring(content from 0 for 150)
    from AozoraBunko
    where content like '%難破船%' escape '!'
       or content like '%無人島%' escape '!'
       or content like '%太平洋%' escape '!'
       or content like '%漂流%'   escape '!'
    limit 5"
$ $sqli_cmd -database sampleSqli -sql "$query"
docId title lastName firstName substring(content from 0 for 150)

863 海に生くる人々 葉山 嘉樹 海に生くる人々 葉山嘉樹      一  室蘭港が奥深く入り込んだ、その太平洋への湾口に、大黒島が栓をしている。雪は、北海道の全土をおおうて地面から、雲までの厚さで横に降りまくった。  汽船万寿丸は、その腹の中へ三 千トンの石炭を詰め込んで、風雪の中を横浜へと進んだ。船は今大黒島をかわろうとして

1323 海島冒険奇譚 海底軍艦 押川 春浪 海島冐檢奇譚 海底軍艦 押川春浪         はしがき 一。太平洋の波に浮べる、この船にも似たる我日本の國人は、今や徒ら に、富士山の明麗なる風光にのみ恍惚たるべき時にはあらざるべし。 光譽ある桂の冠と 、富と權力との優勝旗は、すでに陸を離れて、世界の海上に移されたり。 この冠を戴き 、この優勝旗

1743 光と風と夢 中島 敦 光と風と夢 中島敦 一  一八八四年五月の或夜遅く、三十五 歳のロバァト・ルゥイス・スティヴンスンは、南仏イエールの客舎で、突然、ひどい喀血に襲われた。駈付けた妻に向って、彼は紙切に鉛筆で斯う書いて見せた。「恐れることはない。之が死なら、楽なものだ。」血が口中を塞いで、口が利けなかったのである。

1813 新版 放浪記 林 芙美子 新版 放浪記 林芙美子      第一部      放 浪記以前  私は北九州の或る小学校で、こんな歌を習った事があった。 更けゆく秋の夜 旅の空の 侘しき思いに 一人なやむ 恋いしや古里 なつかし父母  私は宿命的に放 浪者である。私は古里を持たない。父は四国の伊予の人間で、太物の行商人で

2012 道標 宮本 百合子 道標 宮本百合子 道標 第一部 第一章 一  からだの下で、列 車がゴットンと鈍く大きくゆりかえしながら止った。その拍子に眼がさめた。伸子は、そんな気がして眼をあけた。だが、伸子の眼の前のすぐそばには緑と白のゴバン縞のテーブルかけをかけた四角いテーブルが立っている。そのテーブルの上に伸子のハ

本文に「難破船」「無人島」「太平洋」「漂流」のいずれかが含まれる作品の書誌事項と、 本文の先頭から150文字の内容が出力されました。 サンプルデータにはこの条件に該当する作品がいくつか含まれています。

like述語は標準SQLの機能ですが、全文索引が付与されたカラムに対しては、 標準SQLのカラム値に対する文字列検索でなく、全文索引を用いた検索が行われます。 たとえば索引タイプがdualのとき、like '%text%'はfulltextにヒットしません。 また、後述するランキング検索と同様、score関数で検索条件との適合度を取得できます。

サンプルのsqliディレクトリにあるlikeSearch.shを実行すると、 上記の例を含む、いくつかのlike検索を行うことができます。

4.5 ランキング検索を試してみる

contains述語を用いて、指定したキーワードを含む本文データをランキング検索してみます。 ランキング検索はDoqueDBで拡張された機能です。 score(content)は検索条件との適合度で、ここではスコアの降順に結果を出力します。 詳細についてはユーザーズマニュアルの H.4 全文索引のランキング検索をご覧ください。

$ query="select docId, score(content), title, lastName, firstName, kwic(content for 150)
    from AozoraBunko
    where content contains('難破船'|'無人島'|'太平洋'|'漂流')
    order by score(content) desc limit 5"
$ $sqli_cmd -database sampleSqli -sql "$query"
docId score(content) title lastName firstName kwic(content for 150)

1323 7.75436602113091E-1 海島冒険奇譚 海底軍艦 押川 春浪 海島冐檢奇譚 海底軍艦 押川春浪         はしがき 一。太平洋の波に浮べる、この船にも似たる我日 本の國人は、今や徒らに、富士山の明麗なる風光にのみ恍惚たるべき時にはあらざるべし。 光譽ある桂の冠と、富と權力との優勝旗は、すでに陸を離れて、世界の海上に移され たり。 この冠を戴き、この優勝旗を

42294 7.72043825689451E-1 怪奇人造島 寺島 柾史 なおもピストルを、僕の胸に擬した ままだ。 「ね、君! この船は、機関の故障で航海が続けられないのだぜ。つまり、漂 流船だ。この先、何十日、何百日、海洋を流されるかしれないじゃないか。僕等まで射殺して、たった一人で、太平洋を漂流するなンか、心細いだろう」  豹のような水夫は、 肯いて、僕等の麻縄を解きはじめた。   

42767 7.37552322585715E-1 無人島に生きる十六人 須川 邦彦 てから、中川船長は、練 習船琴ノ緒丸の、一等運転士となり、私たち海の青年に、猛訓練をあたえていられたのである。  私は、中川教官に、龍睡丸が遭難して、太平洋のまんなかの無人島に漂着した ときの話をしていただきたいと、たびたびお願いをしていたが、それが、今やっとかなったのであった。  日はもう海にしずんで、館山湾

54331 6.35019183719995E-1 海上の道 柳田 国男 考えのうちに入れて見て行かねばなら ない。今度集めた論文の中には空想を遥かに遠く、青森県の北端まで持っていったものもある。今までの日本人論をみると、太平洋の交通を考慮に入れることが少し不十分であった。つまり伊勢とか、もう少し東に寄って駿河とか遠江とかいうくらいまでのところが、区切りになっているような気

2718 6.03568144316093E-1 恐竜島 海野 十三 電球であった。 「こんなところに電球が ある」  彼はそれを拾いあげた。べつにかわったところもないふつうの電球だ。しかし およそこの無人島には、にあわぬものだった。 「漂流して、この島へ流れついたんだよ 。やっぱりモンパパ号の遺物なんだろう」  電球なんかこの島に用がないと思ったけれ ど彼は、それを拾って手

like検索と同様、本文に「難破船」「無人島」「太平洋」「漂流」の いずれかが含まれる作品の書誌事項と本文の一部が出力されました。 ランキング検索のメリットは、検索条件との適合度が高い結果が上位に出力されることです。 たとえば4つのキーワードがすべて含まれる本文は、適合度がより高いと判断されます。 また、kwic関数により、本文のうち検索キーワードを含む150文字が取り出されています。

この例ではcontainsオペランドを'|'(OR演算子)でつないでいますが、 そのほかに'&'(AND演算子)、'-'(ANDNOT演算子)が指定できます。 また、ここでは指定していませんが、contains述語にスコア計算機(calculator)を指定すると、 スコア計算の重み付けを細かく調整することができます。

サンプルのsqliディレクトリにあるrankSearch.shを実行すると、 上記の例を含む、いくつかのランキング検索を行うことができます。

4.6 自然文検索を試してみる

contains述語にfreetextを指定すると、自然文検索が行われます。 自然文検索では、まず検索条件に指定した自然文が形態素解析され、検索語が自動的に抽出されます。 その後検索語に対して重み付けが施され、OR演算子でランキング検索が実行されます。 詳細についてはユーザーズマニュアルの H.5 全文索引の自然文検索をご覧ください。

$ query="select docId, score(content), title, lastName, firstName, kwic(content for 150)
    from AozoraBunko
    where content contains freetext(
        '小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話')
    order by score(content) desc limit 15"
$ $sqli_cmd -database sampleSqli -sql "$query"
docId score(content) title lastName firstName kwic(content for 150)

42308 5.3557889623954E-1 白雪姫 菊池 寛 は、どうして、わたしたちの家にはいってきたのかね。」と、小人たちはききました。そこで、お姫さまは、まま母が、じぶんをころそうとしたのを、かりうどが、そっと助けてくれたので、一日じゅう、かけずりまわって、やっと、この家を見つけたことを、小人たちに話しました。その話をきいて、小人たちは、 「もしも、おまえ

58052 5.12426102699047E-1 ニールスのふしぎな旅 矢崎 源九郎 も台所からのあかりに 照らしだされていました。おばあさんがしばらく見ていますと、やがて、ちっぽけな小僧が足音をそっとしのばせて、この門からはいってきました。せの高さはほんの十センチぐらいのものでしょう。革ズボンに木靴といった、労働者のようなかっこうです。おばあさんは、すぐに小人だなと気がつきましたので

59411 4.97246063966408E-1 旅の仲間 アンデルセン ハンス・クリスチャン と思ったの です。宿屋の主人は、ふたりにむかって、こんな話をしました。 「この国の王さまは、 たいへんおやさしい、よい方で、どんな人をも苦しめるようなことはなさいません。それなのに、お嬢さまといったら、ほんとになさけない話ですが、それはひどいお姫さまなんですよ。おきれいなことは、たしかに、おきれいです。

33188 4.46023442077327E-1 獄中への手紙 宮本 百合子 ました。あなたはよく、あの懐 しい懐しい物語[自注2]をおぼえていらしたこと。小さな泉とそこの活溌な住人雄々しいきれいな小人のはなしは、いつになっても、どのような話しかたで話されても、本来の愛らしさ、献身、よろこばしさの失われることのない物語です。私は沢山のヴァリエーション、かえ話を知って居ます。覚え

59838 4.3203101123098E-1 三本の金の髪の毛をもっている鬼 グリム ヤーコプ・ルート ヴィッヒ・カール この女があるときひとりの男の子を生みましたが、その子は頭に〈( 1)福の皮〉をかぶって生まれてきました。それで、この子は十四になったら、王さまのお姫さまをおよめさんにもらうだろう、という予言をしたものがありました。  それか らまもなくのこと、王さまがこの村にやってきました。けれども、それが王さまだとは

33192 4.30263702785913E-1 獄中への手紙 宮本 百合子 静かな処を探し出さなければな りません。そして野菜の食べられる処をね。スエ子はろくに青い葉っぱを食べられなかったのよ。  弘文堂から原著者は判りませんが除村吉太郎訳で『ロシア年代記』と云う中 世の歴史が出るらしく、あなたも興味がおありになったら買いましょうか。世界の中世史として高く評価されるものだと云う広告です。  今年

2282 4.17825501266632E-1 津軽 太宰 治 、その手紙にも、「なんにも、おかまひ下さるな。あなたは、知らん振りをしてゐて下さい。お出迎へなどは、決して、しないで下さい。でも、リンゴ酒と、それから蟹だけは。」といふやうな事を書いてやつた筈で、食べものには淡泊なれ、といふ私の自戒も、蟹だけには除外例を認めてゐたわけである。私は蟹が好きなのである。どうしてだか

57856 4.10832243328572E-1 二十四の瞳 壺井 栄 うち、ごつげな音がして、おばあさん にかじりついて寝たん。朝おきたら、はねつるべの棹が折れとったんで。水がめがわれてしもたん」  今朝きいたことをマスノはくりかえして母に語っていた。ふんふんといち いちうなずいていたマスノの母親は、半分は先生にむかって、 「岬じゃあ船がながされ たり、屋根がつぶれたり、ごっそり

2012 3.97834343708277E-1 道標 宮本 百合子 、たべる――伸子は支那料理が非常にすきだったから、店の人々の間にある、一種の空気を押しきって、それを食べるということを、よけい動物的に感じるのだった。  しかしフランシーヌはもうその計画を話されてい るらしくて、 「ジャックがそろそろ帰る時間ですわ、ちょっと失礼いたします」  着がえするらしく、二階の

54870 3.92057127555272E-1 ロザリオの鎖 永井 隆 あなた方青年はいま神から特別に祝 福されているのです。それを感謝しなければなりません。そうして神の思し召しにそうように霊魂も肉身も潔く保ってくださいね。 丸ぼうろ  多比良町の婦人会に私は話をしに行った。ひと月まえに長崎市で開かれた県主催の講習会で顔見知りの幹部のおばあさん方が駅に迎えに出ていて、先日の

655 3.84825815447557E-1 小熊秀雄全集-14 小熊 秀雄 ひました。  そこで王様はまん まと城外に追ひ出され、馬鹿な詩人のトムさんが、王様と早変りをしてしまひました、城の兵士たちも、王様のわがままを憎んでをりましたので、誰もみな喜んだくらゐです。   不思議なお嫁さんは、いつかトムさんが空を仰いでながめた白鳥のお姫さまでした。(大14・4愛国婦人) たばこの好き

24370 3.6020608012537E-1 お父さん 林 芙美子 といっていました。金井君のおとうさんはマニラで戦死をされたのです。金井君は、とてもいいひとです。人のいやがることを何でもします。お家がまずしいので上の学校には行かないのだそうですけれど、とても頭がよくて、先生も、大変ほめていらっしゃいました。英語の会話なんかとてもうまくなっていて、もうれつに勉強します

59744 3.55953772485405E-1 白ヘビ グリム ヤーコプ・ルートヴィッヒ・カール はてま でとんでいき、そのリンゴをとってきたのです。」  若者は、よろこびいさんでかえり ました。美しいお姫さまのところへ金のリンゴをもっていきますと、さすがのお姫さまも、こんどばかりはいいのがれることができなくなってしまいました。  ふたりはその命 のリンゴをふたつにわけて、いっしょに食べました。すると、お

49258 3.52891975346525E-1 其中日記 種田 山頭火 十数尾を持参してくれたのだつた、 さつそく料理して、うまい夕飯を食べた。 暮れて樹明君来庵、ほろ酔機嫌でニコ/\し てゐる、今日の私の行動をもうチヤンと知つてゐる、明後日の緑平老歓迎のことを話しあつて、めでたくさよなら。 Y夫人の急死を聞かされたとき、私の身心はドキンとした、 手当は十分行き届いたのだらう

59635 3.42362872986366E-1 十二人兄弟 グリム ヤーコプ・ルートヴィッヒ・カール く れておいで。にいさんたちがかえってきたら、ぼくがうまく話をするからね。」  お姫 さまはいわれたとおりにしました。やがて、夜になりますと、ほかのにいさんたちが狩りからかえってきました。食事のしたくは、ちゃんとできていました。みんながテーブルについて、食べているとき、にいさんたちがベンジャミンにたずねまし

自然文検索を利用すれば、検索語の取捨選択や重み付けをシステムに任せることができます。 selectで取得される値式にword(content)を指定すれば、 自然文検索でどんな検索語が使われたかを知ることもできます。 (word()の取得は単独で行う必要があります。 検索結果の取得と同時にword()を指定することはできません。)

$ query="select word(content) from AozoraBunko
    where content contains freetext(
        '小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話')"
$ $sqli_cmd -database sampleSqli -sql "$query"
{word(content)}
{'小人' language '' category 'Helpful' scale 0.67 df 25}
{'お 姫' language '' category 'Helpful' scale 0.20 df 18}
{'姫' language '' category 'Helpful' scale 0.67 df 44}
{'姫 さま' language '' category 'Helpful' scale 0.20 df 15}
{'おばあさん' language '' category 'Helpful' scale 0.67 df 32}
{'毒' language '' category 'Helpful' scale 0.67 df 92}
{'毒 リンゴ' language '' category 'Helpful' scale 0.20 df 0}
{'リンゴ' language '' category 'Helpful' scale 0.67 df 30}
{'食べ' language '' category 'Helpful' scale 0.67 df 77}
{'話' language '' category 'Helpful' scale 0.67 df 152}

kwic関数にenclose withを指定すると、検索結果に出現する検索語がタグで囲まれます。

$ query="select docId, score(content), title, lastName, firstName,
    kwic(content for 150 enclose with '<BEGIN>' and '<END>')
    from AozoraBunko
    where content contains freetext(
        '小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話')
    order by score(content) desc limit 15"
$ $sqli_cmd -database sampleSqli -sql "$query"
docId score(content) title lastName firstName kwic(content for 150 enclose with '<BEGIN>' and '<END>')

42308 6.89818731482802E-1 白雪姫 菊池 寛 は、どうして、わたしたちの家にはいってきたのかね。」と、<BEGIN>小人<END>たちはききました。そこで、<BEGIN>お姫さま<END>は、まま母が、じぶんをころそうとしたのを、かりうどが、そっと助けてくれたので、一日じゅう、かけずりまわって、やっと、この家を見つけたことを、<BEGIN>小人<END>たちに<BEGIN>話<END>しました。その<BEGIN>話<END>をきいて、<BEGIN>小人<END>たちは、 「もしも、おまえ
...

ここでは指定していませんが、contains述語にextractorを指定すると、 検索語の抽出に使われる辞書リソースを明示的に指定することもできます。

サンプルのsqliディレクトリにあるsentenceSearch.shを実行すると、 上記の例を含む、いくつかの自然文検索を行うことができます。

5. Javaでサンプルを実行する

JDBCドライバーを用いて、Javaアプリケーションからデータベースを操作できます。 JDBCドライバーの詳細については DoqueDB JDBC Driverを参照してください。

5.1 URL

JDBCのURLは以下のとおりです。

jdbc:ricoh:doquedb://<ホスト名>:<ポート番号>/<データベース名>

5.2 データベースを準備する

以下の手順でデータベースを準備します。 データベース名がsampleSqliからsampleJavaに変わったことを除いて、 前章の4.14.3と同じ内容を実施しています。

この処理を行うプログラムがJavaディレクトリのSetup.javaです。 以下のように実行してください。

$ mkdir ~/doquedb
$ cp -rp /var/lib/DoqueDB/doc/sample ~/doquedb
$ cd ~/doquedb/sample/Java
$ javac Setup.java
$ java -classpath .:/var/lib/DoqueDB/bin/java/doquedb.jar Setup
データベースの作成を開始
テーブルの作成を開始
バッチインサートを開始
全文索引の作成を開始

Setup.javaの内容は以下のようになっています。

import java.sql.*;

public class Setup {
    public static void main (String[] args) {
        // Driverクラスのインポート
        try {
            Class.forName("jp.co.ricoh.doquedb.jdbc.Driver");
        } catch (ClassNotFoundException cne) {
            System.err.println("error" + ": " + cne);
            return;
        };

        // データベースの設定
        String url = "jdbc:ricoh:doquedb://localhost:54321/";
        String user = "root";
        String password = "doqadmin";
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;

        // カレントディレクトリの取得
        String dir = System.getProperty("user.dir");
        
        // データベースの作成
        try {
            System.out.println("データベースの作成を開始");
            conn = DriverManager.getConnection(url, user, password);
            statement = conn.createStatement();
            String query = "create database sampleJava";
            resultSet = statement.executeQuery(query);
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        } finally {
            try {
                if (resultSet!=null) resultSet.close();
                if (statement!=null) statement.close();
                if (conn!=null) conn.close();
            } catch (Exception e) {};
        };

        // 立ち上げた DB に再接続
        url = "jdbc:ricoh:doquedb://localhost:54321/sampleJava";
        try {
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        };

        // テーブルの作成
        try {
            System.out.println("テーブルの作成を開始");
            statement = conn.createStatement();
            String query = "create table "
                + "AozoraBunko ("
                    + "docId int, "
                    + "title nvarchar(256), "
                    + "lastName nvarchar(128), "
                    + "firstName nvarchar(128), "
                    + "url varchar(128), "
                    + "content ntext, "
                    + "primary key(docId))";
            resultSet = statement.executeQuery(query);
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        } finally {
            try {
                if (resultSet!=null) resultSet.close();
                if (statement!=null) statement.close();
            } catch (Exception e) {};
        };

        // バッチインサートの実施
        try {
            System.out.println("バッチインサートを開始");
            statement = conn.createStatement();
            String query = String.format(
                "insert into AozoraBunko "
                + "input from path '%s/../data/insert.csv' "
                + "hint 'code=\"utf-8\" InputField=(1,2,16,17,51,57)'"
                , dir);
            resultSet = statement.executeQuery(query);
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        } finally {
            try {
                if (resultSet!=null) resultSet.close();
                if (statement!=null) statement.close();
            } catch (Exception e) {};
        };

        // 全文索引の作成
        try {
            System.out.println("全文索引の作成を開始");
            statement = conn.createStatement();
            String query =  "create fulltext index INDEX1 on AozoraBunko(content) "
                + "hint 'kwic, "
                    + "delayed, "
                    + "inverted=(normalized=(stemming=false, deletespace=false), "
                    + "indexing=dual, "
                    + "tokenizer=DUAL:JAP:ALL:1 @NORMRSCID:1 @UNARSCID:1)'";
            resultSet = statement.executeQuery(query);
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        } finally {
            try {
                if (resultSet!=null) resultSet.close();
                if (statement!=null) statement.close();
            } catch(Exception e) {};
        };

        try {
            if (conn!=null) conn.close();
        } catch (Exception e) {};
    };
}

5.3 検索を実行する

検索を実行し、結果を表示するJavaプログラム、SentenceSearch.javaを示します。
ここでは自然文検索を行っています。

import java.sql.*;

public class SentenceSearch {
    public static void main (String[] args) {
        // Driverクラスのインポート
        try {
            Class.forName("jp.co.ricoh.doquedb.jdbc.Driver");
        } catch (ClassNotFoundException cnf) {
            System.err.println("error" + ": " + cnf);
            return;
        };

        // データベースの設定
        String url = "jdbc:ricoh:doquedb://localhost:54321/sampleJava";
        String user = "root";
        String password = "doqadmin";
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;
        ResultSetMetaData metaData = null;

        // DB に接続
        try {
            conn = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        };

        String searchWord = "";
        String query = "";

        // 白雪姫を検索
        searchWord = "小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話";
        query = "select docId, score(content), title, lastName, firstName, kwic(content for 150) "
            + "from AozoraBunko "
            + "where content contains freetext('" + searchWord + "') "
            + "order by score(content) "
            + "desc limit 5";
        try {
            System.out.println("自然文検索の実施");
            System.out.println("検索文:" + searchWord + "\n");
            statement = conn.createStatement();

            resultSet = statement.executeQuery(query);
            // メタデータを取得し、カラム名を表示する
            metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                System.out.print(metaData.getColumnName(i) + ",");
            };
            System.out.println("\n");

            // クエリの実行結果を取得し、表示する
            while (resultSet.next()) {
                int docId = resultSet.getInt(1);
                double score = resultSet.getDouble(2);
                String title = resultSet.getString(3);
                String lastName = resultSet.getString(4);
                String firstName = resultSet.getString(5);
                String content = resultSet.getString(6);
                String output = String.join(
                    ",",
                    String.valueOf(docId),
                    String.valueOf(score),
                    title,
                    lastName,
                    firstName);
                System.out.println(output);
                System.out.println(content);
                System.out.print("\n");
            };
        } catch (Exception e) {
            System.err.println("error" + ": " + e);
            return;
        } finally {
            try {
                if (resultSet!=null) resultSet.close();
                if (statement!=null) statement.close();
            } catch (Exception e) {};
        };

        try {
            if (conn!=null) conn.close();
        } catch (Exception e) {};
    };
}

SentenceSearch.javaを実行した結果を以下に示します。

$ javac SentenceSearch.java
$ java -classpath .:/var/lib/DoqueDB/bin/java/doquedb.jar SentenceSearch
自然文検索の実施
検索文:小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話

docId,score(content),title,lastName,firstName,kwic(content for 150),

42308,0.5355788957430164,白雪姫,菊池,寛
は、どうして、わたしたちの家にはいってきたのかね。」と、小人たちはききました。そこで、お姫さまは、まま母が、じぶんをころそうとしたのを、かりうどが、そっと助けてくれたので、一日じゅう、かけずりまわって、やっと、この家を見つけたことを、小人たちに話しました。その話をきいて、小人たちは、
「もしも、おまえ

58052,0.5124260938479301,ニールスのふしぎな旅,矢崎,源九郎
も台所からのあかりに照らしだされていました。おばあさんがしばらく見ていますと、やがて、ちっぽけな小僧が足音をそっとしのばせて、この門からはいってきました。せの高さはほんの十センチぐらいのものでしょう。革ズボンに木靴といった、労働者のようなかっこうです。おばあさんは、すぐに小人だなと気がつきましたので

59411,0.4972460628457043,旅の仲間,アンデルセン,ハンス・クリスチャン
と思ったのです。宿屋の主人は、ふたりにむかって、こんな話をしました。
「この国の王さまは、たいへんおやさしい、よい方で、どんな人をも苦しめるようなことはなさいません。それなのに、お嬢さまといったら、ほんとになさけない話ですが、それはひどいお姫さまなんですよ。おきれいなことは、たしかに、おきれいです。

33188,0.4460234280553054,獄中への手紙,宮本,百合子
ました。あなたはよく、あの懐しい懐しい物語[自注2]をおぼえていらしたこと。小さな泉とそこの活溌な住人雄々しいきれいな小人のはなしは、いつになっても、どのような話しかたで話されても、本来の愛らしさ、献身、よろこばしさの失われることのない物語です。私は沢山のヴァリエーション、かえ話を知って居ます。覚え

59838,0.4320310104986243,三本の金の髪の毛をもっている鬼,グリム,ヤーコプ・ルートヴィッヒ・カール
この女があるときひとりの男の子を生みましたが、その子は頭に〈(1)福の皮〉をかぶって生まれてきました。それで、この子は十四になったら、王さまのお姫さまをおよめさんにもらうだろう、という予言をしたものがありました。
 それからまもなくのこと、王さまがこの村にやってきました。けれども、それが王さまだとは

6. Pythonでサンプルを実行する

Pythonクライアントライブラリを用いることで、Pythonプログラムからデータベースを操作できます。 ライブラリの詳細についてはPython Driverを参照してください。

本章のサンプルを実行するためには、以下の条件を満たす必要があります。

6.1 データベースを準備する

以下の手順でデータベースを準備します。 create databaseを実行するときは、connect()の引数dbnameに 組み込みデータベースDefaultDBを指定してください。 データベース名がsampleSqliからsamplePythonに変わったことを除いて、 第4章の4.14.3と同じ内容を実施しています。

この処理を行うプログラムがPythonディレクトリのsetup.pyです。 以下のように実行してください。

$ mkdir ~/doquedb
$ cp -rp /var/lib/DoqueDB/doc/sample ~/doquedb
$ cd ~/doquedb/sample/Python
$ python3 setup.py
データベースの作成を開始
None
テーブルの作成を開始
None
バッチインサートを開始
[(None, None, 0), (None, None, 1), ..., (None, None, 155)]
全文索引の作成を開始
None

setup.pyの内容は以下のようになっています。

import os
import doquedb as dq

# カレントディレクトリの取得
path = os.getcwd()

# 1. サンプルコード実行用のデータベースを作成する
# 接続情報を与え、データベースに接続する
try:
    conn = dq.connect(host='localhost',
                      port=54321,
                      dbname='DefaultDB',
                      user='root',
                      password='doqadmin',
                      autocommit=True)  # スキーマ操作のため明示的なトランザクションは利用できない
    # データベース操作のためのCursorオブジェクトを作り、データベースを作成する
    print("データベースの作成を開始")
    cur = conn.cursor()
    cur.execute("create database samplePython")
    rows = cur.fetchall()
    print(rows)
except dq.Error as e:
    print("error: " + e)
finally:
    cur.close()
    conn.close()

# 2. 作成したデータベースに再度アクセスしテーブルを作成
try:
    conn = dq.connect(host='localhost',
                      port=54321,
                      dbname='samplePython',
                      user='root',
                      password='doqadmin',
                      autocommit=True)  # スキーマ操作のため明示的なトランザクションは利用できない
    cur = conn.cursor()
    # テーブルを作成
    print("テーブルの作成を開始")
    rs = cur.execute("""
                CREATE TABLE AozoraBunko (
                docId int,
                title nvarchar(256),
                lastName nvarchar(128),
                firstName nvarchar(128),
                url varchar(128),
                content ntext,
                primary key(docId))
                """)
    rows = cur.fetchall()
    print(rows)
except dq.Error as e:
    print("error:" + e)
finally:
    cur.close()
    conn.close()

# 3. トランザクション処理のため再度データベースに接続しデータを登録する
try:
    conn = dq.connect(host='localhost',
                      port=54321,
                      dbname='samplePython',
                      user='root',
                      password='doqadmin',
                      autocommit=False)  # オートコミットを切る
    # トランザクションを開始
    conn.begin_transaction()
    cur = conn.cursor()
    # バッチインサートを実行
    print("バッチインサートを開始")
    statement = f"""
    insert into AozoraBunko
    input from path '{path}/../data/insert.csv'
    hint 'code="utf-8" InputField=(1,2,16,17,51,57)'
    """
    cur.execute(statement)
    rows = cur.fetchall()
    print(rows)
    conn.commit()
except dq.Error as e:
    print("error: " + e)
    conn.rollback()
    cur.close()
    conn.close()

# 4. 全文索引の作成のため再度データベースに接続し、索引を登録する
try:
    conn = dq.connect(host='localhost',
                      port=54321,
                      dbname='samplePython',
                      user='root',
                      password='doqadmin',
                      autocommit=True)  # スキーマ操作のため明示的なトランザクションは利用できない
    cur = conn.cursor()
    # 全文索引の作成
    print("全文索引の作成を開始")
    statement = """
    create fulltext index INDEX1 on AozoraBunko(content)
    hint 'kwic,
          delayed,
          inverted=(normalized=(stemming=false, deletespace=false),
          indexing=dual,
          tokenizer=DUAL:JAP:ALL:1 @NORMRSCID:1 @UNARSCID:1)'
    """
    cur.execute(statement)
    rows = cur.fetchall()
    print(rows)
except dq.Error as e:
    print("error: " + e)
    cur.close()
    conn.close()

# 終了処理
dq.close()

6.2 検索を実行する

検索を実行し、結果を表示するPythonプログラム、sentence_search.pyを示します。
ここでは自然文検索を行っています。

import doquedb as dq

# 白雪姫を検索する
try:
    conn = dq.connect(host='localhost',
                      port=54321,
                      dbname='samplePython',
                      user='root',
                      password='doqadmin',
                      autocommit=True)
    cur = conn.cursor()
    searchword = "小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話"
    cur.execute(f"""
                select docId,
                       score(content),
                       title,
                       lastName,
                       firstName,
                       kwic(content for 150)
                from AozoraBunko where content contains
                    freetext('{searchword}')
                order by score(content)
                desc limit 5
                """)
    # 検索結果を取得してprintする
    rows = cur.fetchall()
    assert rows
    print(*rows, sep='\n')
except dq.Error as e:
    print("error:" + e)
finally:
    cur.close()
    conn.close()

dq.close()

sentence_search.pyを実行した結果を以下に示します。

$ python3 sentence_search.py
(42308, 0.6898187314828024, '白雪姫', '菊池', '寛', 'は、どうして、わたしたちの家にはいってきたのかね。」と、小人たちはききました。そこで、お姫さまは、まま母が、じぶんをころそうとしたのを、かりうどが、そっと助けてくれたので、一日じゅう、かけずりまわって、やっと、この家を見つけたことを、小人たちに話しました。その話をきいて、小人たちは、\n「もしも、おまえ')
(59411, 0.6124133539876164, '旅の仲間', 'アンデルセン', 'ハンス・クリスチャン', 'と思ったのです。宿屋の主人は、ふたりにむかって、こんな話をしました。\n「この国の王さまは、たいへんおやさしい、よい方で、どんな人をも苦しめるようなことはなさいません。それなのに、お嬢さまといったら、ほんとになさけない話ですが、それはひどいお姫さまなんですよ。おきれいなことは、たしかに、おきれいです。')
(58052, 0.535589643758629, 'ニールスのふしぎな旅', '矢崎', '源九郎', 'あかりに照らしだされていました。おばあさんがしばらく見ていますと、やがて、ちっぽけな小僧が足音をそっとしのばせて、この門からはいってきました。せの高さはほんの十センチぐらいのものでしょう。革ズボンに木靴といった、労働者のようなかっこうです。おばあさんは、すぐに小人だなと気がつきましたので、すこしもこわく')
(33188, 0.5113688616088571, '獄中への手紙', '宮本', '百合子', 'お餅をたべたいのに食べられないと残念がったりしていたのをよく御承知ですから、手術したことをびっくりなさりながら、やっぱり、後がさっぱりして却って安心と云って下さいました。後がさっぱりのうれしさは、今にもうすこしして平気に歩くようになったとき俄然真価を発揮すると思います。\n\u3000私はリンゴぜめよ。誰彼が見舞')
(2282, 0.5063893225844849, '津軽', '太宰', '治', '、その手紙にも、「なんにも、おかまひ下さるな。あなたは、知らん振りをしてゐて下さい。お出迎へなどは、決して、しないで下さい。でも、リンゴ酒と、それから蟹だけは。」といふやうな事を書いてやつた筈で、食べものには淡泊なれ、といふ私の自戒も、蟹だけには除外例を認めてゐたわけである。私は蟹が好きなのである。どうしてだか')

7. データベースを削除する

一度実行したサンプルコードをもう一度実行するには、 作成したデータベースが削除された状態に戻す必要があります。 データベースを削除するには、以下のコマンドを実行してください。

$ sqli_cmd="/var/lib/DoqueDB/bin/sqli -remote localhost 54321
    -user root -password doqadmin -code utf-8"
$ $sqli_cmd -sql "drop database sampleSqli"
$ $sqli_cmd -sql "drop database sampleJava"
$ $sqli_cmd -sql "drop database samplePython"

以上、sqliおよびJavaによるいくつかの検索サンプルをお試しいただきました。
DoqueDBにはほかにもさまざまな機能があります。 それらの機能をご確認いただくためにも、 上記サンプルを加工するなどして、さらにお役立ていただければ幸いです。

8. うまく動かないとき

サンプルコードの実行手順については十分確認しておりますが、 処理に失敗するなどうまく動かないときは以下の点をご確認ください。

バッチインサートで権限がないと表示される
参照されるファイルにrootのアクセス権があるか確認してください。
ファイルがないと表示される
DoqueDBサービスのプロセスから見える場所にファイルがあるか確認してください。

9. 青空文庫の全データで検索する

ここまで青空文庫のサンプルデータで検索をお試しいただきましたが、 ご自身でデータを取得して登録することにより、 青空文庫の全データを対象として検索を行うことができます。 ここではデータの取得から加工、登録までの手順について説明します。

9.1 青空文庫の作品データを取得する

青空文庫の作品データはGitHubのリポジトリaozorahackにあり、 ZIP書庫としてダウンロードできます。 ブラウザで以下のURLにアクセスしてください。

ダウンロードされたデータは以下の名前で保存されます。

サイズはZIP書庫で250MB、展開して550MB、件数は17000件ほどです。 ディレクトリ構造は以下のとおりです。

作品識別名は以下の構造です。

作品IDは全作品で一意、rubyはルビ情報あり、txtはルビ情報なし、 ファイル通し番号は全ファイルで一意ですが、随時振り直されます。 著作権なしのテキストデータについては文字コードはすべてShiftJISです。

9.2 青空文庫の書誌情報を取得する

青空文庫の書誌情報は「公開中 作家別作品一覧拡充版」として以下に置かれています。 これを取得してください。

内容はShiftJISのCSVファイルで、以下の名前です。

参照するカラムは以下のとおりです。

テキストファイルURLの形式は以下のとおりです。

9.3 データを加工する

作品データと書誌情報を加工し、データベースに登録します。 Perlの実行環境とunzipが必要です。

  1. 当サイトから、以下の加工ツールを取得します。ブラウザで右クリックし、「名前を付けてリンク先を保存」してください。
  2. aozorabunko_text-master.zipとlist_person_all_extended.zipを展開します。
    $ unzip aozorabunko_text-master.zip
    $ unzip list_person_all_extended.zip
    
  3. そのディレクトリでprocess-aozora-data.plを実行します。
    $ perl process-aozora-data.pl
    

以上により、OUTPUTフォルダが作られ、その下に加工済みテキストデータが書き出されます。 またinsert.csvに加工済みテキストデータに対する書誌情報が出力されます。 insert.csvには加工済みテキストデータのファイル名列が追加されています。

登録用データには、著作権の切れた作品のみが出力されます。 データがどのように加工されるかについては、加工ツールのコメントをご覧ください。

9.4 データを登録する

insert.csvとOUTPUT/*.txtが用意できたら、4. SQLでサンプルを実行する に従ってデータベースを作成します。 データの格納には800MB程度の領域が必要です。 また、作業には数分の時間がかかります。

すでにサンプルデータを登録済みのデータベースが存在する場合は、あらかじめ 7. データベースを削除する の手順でデータベースを削除しておいてください。

以上でデータの登録は終わりです。 サンプルデータと同様に、さまざまな検索をお試しください。

Copyright (c) 2023, 2024 Ricoh Company, Ltd. All rights reserved.