🔍

Isarで日本語の全文検索

2022/09/06に公開

はじめに

Flutter用のデータベースIsarで自作のアプリに日本語の全文検索を組み込んだときの四苦八苦をメモにしたものです。
結論としては、単語分割ができなかったので、インデックスの使えないLIKE検索的なものになりました。

Isarとは

IsarはFlutterのための、クロスプラットフォームでとても速いデータベースです、とisar.devの目立つところに書いてあります。
自分は、Androidでローカルに閉じ、RailsのActiveRecord的に扱えて、割とカスタマイズしやすいので使っています。

サンプル

↓のようなMemoに対して、検索ワードがnamekeywordsStringbodyに含まれていたときに抽出したいと思っています。
検索時に複数のワードを指定したときには、両方含んだものを取り出そうと思っています。

memo.dart
()
class Memo {
  ()
  int? id;

  String? name;
  ()
  File? labelImage;
  ()
  File? specImage;
  ()
  File? otherImage;
  ()
  DateTime? tappedOn;
  String? keywordsString;
  int? score;
  String? purchaceStore;
  String? body;
  late DateTime createdAt;
  late DateTime updatedAt;
}

含む

公式ドキュメントのとおり、contains()を使うだけです。
実際に利用するときにはnameContains()などのように検索対象にするカラム名を前につけます。

// isarは別のところでオープンしたものです。
isar.memos
        .filter()
        .nameContains('検索ワード')
	.findAllSync();

複数の項目のいずれかに含む

公式ドキュメントのとおり、or()でつなぐだけです。

String word = '検索ワード';
isar.memos
        .filter()
        .nameContains(word)
	.or()
        .keywordsStringContains(word)
	.or()
	.bodyContains(word)
	.findAllSync();

複数の検索ワード

公式ドキュメントのLogical operatorsにあるgroup()で↑のクエリをまとめたものを公式ドキュメントのQuery modifiersにあるrepeat()で繰り返します。

一見不要な.nameContains('')ですが、これがないと型が合わずに.findAllSync()が使えません。SQLの1=1みたいなのをかければいいんですが、思いつかず…。

String searchWordsString = '検索ワード1 検索ワード2';
List<String> words = searchWordsString.split(' ');
isar.memos
        .filter()
	.nameContains('')
	.repeat(
	   words,
	  (wordQ, String word) => wordQ.and().group((q) => q
            .nameContains(word)
	    .or()
            .keywordsStringContains(word)
	    .or()
	    .bodyContains(word)
	.findAllSync();

他に検討したこと

公式ドキュメントに全文検索について載っていました。
試してみたところ、↓のようにIsar.splitWords()だとひらがなを全部1単語としてみてしまうようで、使えませんでした…。

mecab

全文検索といえば、少し色気を出して、完全一致ではなく、単語の変化形や周辺の語も拾ってほしくなります。
そうなったらmecabが適任です。
mecab_dartというパッケージがあったのでちょっとやったみましたが、↓のように分析してくれて、よさそうに感じました。

…ですが、このパッケージはFlutterに依存していて、自分のコードで依存しないようにしてた部分まで侵食しちゃいそうなのと、テストがなかったりしたので、今回は諦めました。

おわりに

結局全文検索をインデックスが使えないLIKE的なもので実装することになってしまいましたが、自分の入力したデータからしか検索しないので、大した量ではなく、スピードも問題ないと思います。

Isar、うまく単語で分割さえできれば、インデックスを作れるのですが、ちょっと残念です…。

自分で使っているアプリなので、不満が出てきたらまた考えようと思います。

参照

Discussion