DoqueDB：もうひとつの全文検索データベース

### 正式公開しました (2024年2月16日追記)

当記事をご覧いただきありがとうございます。長らくお待たせいたしましたが、DoqueDBはようやく正式公開を果たすことができました。以下の記事にてWebサイトやGitHubプロジェクトをご案内しております。

[**DoqueDB：MySQL, PostgreSQLと構文を比較してみる**](https://qiita.com/junnohta999/items/0afe45f8050bbc4af2ed)

これ以降の内容は正式公開前のβ版に関するものです。内容は正式版にも当てはまりますが、リンク先は順次公式サイトへと切り替わりますので、ご留意ください。

<hr>

**概要**：全文検索にしのぎを削るDBMS業界にもう1人のプレイヤーが登場しましたので、ここでご紹介しますね、という話です。御用とお急ぎのない方は、ぜひお立ち寄りを。

### 全文検索いろいろありますよね

近年では、小規模な業務でも、あるいは個人の趣味の範囲でも、大量のテキストデータを扱えることの重要性が高まってきています。DBMSを使うことで、大量のデータを安全かつ高速に保管・更新でき、また全文検索を使うことで、データ管理コストと検索の利便性をうまくバランスさせることができるわけです。
![DoqueDBの位置付け２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3510207/7b94e950-ce43-e64c-4c4a-cd28d3e033f2.png)
全文検索が使えるDBMSとしては、現在は **MySQL(InnoDB)** や **Groonga** の名前がまず候補として挙がってきますね。Groongaはことに、日本語との相性の良さ、検索の高速さが評価されているようです。

### 株式会社リコーも参加します

さて、株式会社リコーでは、社内で開発され、長らく使われてきたDBMSを「**[DoqueDB(ドックディービー)](https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/")**」という名称で公開することにしました。DoqueDBは日本語の全文検索に強みをもつ、SQLベースのRDBMSです。SQL拡張構文により、通常のデータ操作と全文検索をシームレスに組み合わせることが可能です。今回は、全文検索機能のうち **ランキング検索** と **自然文検索** をご紹介しましょう。

DoqueDBは、Linux上で動作します。動作確認はRedHat Enterprise Linux 7, CentOS 7で行っています。現時点ではβ版バイナリパッケージのみの配布ですが、オープンソースでの公開を目指して連日作業が進められています。入手方法や関連ドキュメントを記事末尾にまとめましたので、ぜひご参照ください。

### DoqueDBをインストールします

ここでは関連ドキュメント「**[使ってみよう](https://dqdemo.concept.ricoh/doc/howtouse.html?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/doc/howtouse.html")**」に従ってインストールを進めます。各ステップの詳細についてはドキュメントをご覧ください。作業はrootユーザーで行います。DoqueDBのインストールは簡単で、時間もかかりません。

```shell
$ sudo su -
# tar xvf doquedb_beta.tar.gz
...
# cd doquedb_beta
# ./install.sh
Install Objects
# ./setup.sh
Install Mod Parameter
Install Default Parameter
Install System Parameter
Initialize Database
# cd /var/lib/DoqueDB/bin
# ./doquedb start
SydServer starting (root) ... done.
# exit
$
```

### データベースを作成してデータを登録します

β版のパッケージには、主に著作権の切れた文学作品を電子化して公開している「**[青空文庫](https://www.aozora.gr.jp/ "https://www.aozora.gr.jp/")**」の一部のデータ(作品数150件ほど)が収録されています。このデータを登録し、全文検索を試してみましょう。

最初にパッケージからサンプルデータを取り出します。これ以降の作業は一般ユーザーで行います。

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

データベースの作成は以下の手順で実施します。
① データベースの作成 (create database)
② テーブルの作成 (create table)
③ データの登録 (insert)
④ 全文索引の作成 (create fulltext index)

まずはデータベースとテーブルを作成します。テーブルAozoraBunkoには作品IDや作品名、作者名とともに本文データが格納されます。

```shell
$ 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"
```

次に作品データを登録します。登録には「**[バッチインサート](https://dqdemo.concept.ricoh/doc/k_batchinsert.html?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/doc/k_batchinsert.html")**」という機能を使います。データを格納しているinsert.csvの各行は次のような形式で、末尾に本文データを格納したファイルの相対パスがあります。

```csvs
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/4
0_15151.html","2004-03-14","ShiftJIS","JIS X 0208","0","000879_000040.txt",FIL
E OUTPUT/000879_000040.txt
```

このinsert.csvのフルパスを指定して、insert文を実行します。

```shell
$ 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"
```

これで作品データが登録できました。次に、本文データのカラムcontentに全文索引を作ります。

```shell
$ 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以降は全文検索の挙動を決めるためのヒント情報です。kwicは検索結果の検索語周辺テキストを取得するために指定します。indexing=dualは、英字列については単語単位で、日本語列についてはN-gramで索引語を生成することを意味します。詳細については「**[使ってみよう](https://dqdemo.concept.ricoh/doc/howtouse.html?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/doc/howtouse.html")**」をご覧ください。

### ランキング検索を試してみよう

全文索引が設定されたカラムに対してcontains述語を指定することで、指定したキーワードを含むレコードを取得できます。score(content)で検索条件との適合度が得られるので、このスコアを用いて適合度の高い順に結果をソートできます。

「**難破船**」「**無人島**」「**太平洋**」「**漂流**」をOR検索してみましょう。

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

サイズの制約があるため、パッケージには青空文庫の全データは付属していません。実際には、全データから上記の検索条件で見つかる作品のうち、ランクが上位のデータのみが含まれています。したがって、検索条件を変更したときの結果は、青空文庫の全データに対する検索結果とは異なります。さまざまな検索条件での結果を確認したい方は、関連情報にある「**[デモサイト](https://dqdemo.concept.ricoh/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/")**」をご利用ください。

なお、ここでAND検索しなかったのは、すべての条件にマッチするデータが見つからなくても、条件との一致度がそれなりに高いデータが見つかるからです。すべての条件にマッチするデータが多数あれば、結果はAND検索と近いものになります。実用上は、OR検索のほうが便利に使える場面が多いでしょう。

### 自然文検索を試してみよう

検索キーワードを明示しなくても、適当な文章を入力することで、その文章に近い特徴をもつレコードを検索することができます。そのためには、contains述語にfreetext()でテキストを指定します。形態素解析により、テキストから検索キーワードが自動的に抽出され、重み付けが施されたあと、OR検索でランキング検索が実行されます。

「**小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話**」を検索してみましょう。

```shell
$ query="select docId, score(content), title, lastName, firstName,
        kwic(content for 150)
    from AozoraBunko
    where content contains freetext(
        '小人と暮らすお姫さまが悪いおばあさんに毒リンゴを食べさせられる話')
    order by score(content) desc limit 5"
$ $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 獄中への手紙 宮本 百合子 ました。あなたはよく、あの懐 し
い懐しい物語［自注２］をおぼえていらしたこと。小さな泉とそこの活溌な住人雄々しいきれい
な小人のはなしは、いつになっても、どのような話しかたで話されても、本来の愛らしさ、献身
、よろこばしさの失われることのない物語です。私は沢山のヴァリエーション、かえ話を知って
居ます。覚え

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

freetext()に指定できるテキストの長さに上限はありません。したがって、検索結果の中でユーザーの検索意図に近かったものを選んで、その本文データをcontains freetext()に指定して繰り返し検索することにより、より精度の高い検索結果を得る、といった使い方も可能です。(前述の「**[デモサイト](https://dqdemo.concept.ricoh/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/")**」では、諸般の事情により入力の上限を50,000字に制限しています。)

また、freetext()に指定したテキストから実際にどんな検索キーワードが生成され、検索に使われるのかは、word(content)で確認することができます。

```shell
$ 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}
```

DoqueDBによる全文検索のサンプルを駆け足でご覧いただきましたが、ご興味をお持ちいただけたでしょうか？

### 入手方法と関連情報

DoqueDB β版をご利用いただくには、早期ご紹介ページよりメールアドレスを登録してダウンロードページURLを受け取り、使用許諾に同意していただく必要があります。下記ページよりお手続きをお願いします。

**[DoqueDB早期ご紹介ページ](https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/")**
[https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/](https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://doquedb.concept.ricoh/all-functions-at-one-stop-service/doquedb/")

ご登録いただいた方には、オープンソース化で進展があった際に随時ご案内をお送りさせていただきます。また、本記事でご紹介した「**[使ってみよう](https://dqdemo.concept.ricoh/doc/howtouse.html?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/doc/howtouse.html")**」やユーザーズマニュアルについては、どなたでも以下のURLからご参照いただけます。

**[DoqueDB β版 各種情報](https://dqdemo.concept.ricoh/doc/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/doc/")**
[https://dqdemo.concept.ricoh/doc/](https://dqdemo.concept.ricoh/doc/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/doc/")

以下のサイトでは、「**[青空文庫](https://www.aozora.gr.jp/ "https://www.aozora.gr.jp/")**」の全データを利用したランキング検索や自然文検索を、ご自身でお試しいただくことができます。

**[DoqueDBデモサイト](https://dqdemo.concept.ricoh/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/")**
[https://dqdemo.concept.ricoh/](https://dqdemo.concept.ricoh/?utm_source=qiita&utm_medium=referral&utm_campaign=beta "https://dqdemo.concept.ricoh/")

### おわりに

今回ご紹介させていただいたDoqueDBは、社内開発の特許検索システムや、リコー製のさまざまな文書管理システムの全文検索インタフェース、またストレージエンジンとして長年活用されてきた実績あるシステムです。リコー自身、DBMS開発には40年におよぶ経験を有しています。

全文検索それ自体は古くからある成熟した技術ですが、検索時にユーザーが入力したテキストからどのようにキーワードを切り出し、どのように重み付けして検索するかなどは、実装により異なります。つまり、どんな検索結果が上位に並ぶかについては、全文検索システムの個性がそれなりに現れるということですね。

DoqueDBは後発製品として登場することになりましたが、開発チームとしては、並び立つ全文検索データベースの中でそれなりの評価が得られることを願ってやみません。ご紹介したい特長もまだまだあります。今後の記事でまたお会いしましょう！