【Dart/Flutter開発】検索エンジンでありがちな、単語/言葉の検索機能を実装する、話

 

こんにちは、たちつてとです。

↓過去記事では(年月やカテゴリIDなど)

「完全一致」に関する検索機能を

実装しました。

【Dart/Flutter開発】表示制御と検索機能の実装、実際の(自作)アプリと考え方を共に...【スマホアプリ】

 

が、Googleやyahooで見かけるような

単語を入力→一致する項目を表示する

「部分一致」の検索機能は

実装したことがありませんでした。

 

「あい」で検索すると

  • あかさたな
  • あいうえお←Hit!
  • ゆがんだあい←Hit!
  • あいのない話←Hit!
  • あばばばば

とこんな感じの検索機能の話です。

 

メモアプリを作っているんですが、

それで上記のような単語検索機能を

組みたいなと思い、記事にする次第です。

 

完成イメージ

実装イメージ、シミュレーターのキーボードの都合で

aiu」で検索...

f:id:tachitutetoNosuke:20211027200927p:plain



作戦

基本的な作戦は、

【Dart/Flutter開発】表示制御と検索機能の実装、実際の(自作)アプリと考え方を共に...【スマホアプリ】

こちらの記事と同じものでいきます。

(※前提:一覧画面があってその項目を絞り込みたい。)

 

言葉にすると、

  1. テキスト入力がされた時に
  2. その言葉を含む項目「だけ」
  3. 一覧画面にセットしたい

です。

 

ですんで、より具体的には

 

  1. TextFieldのChangeイベントで
  2. TextFieldに入力された内容を含む項目を、if分岐しつつList変数にぶちこみ
  3. ListView.builderのitemBuilderに渡す

 

となります。

ここで厄介なのが、キーワードを含む

なんですが、割と余裕?というか

プログラムにして一文でクリアできたりします。

 

正規表現

これタイトルに入れると、アクセス数少なくなりそうで

入れませんでした。

ブラウザバックはしないでください...。

正直、僕も100%は使いこなせません。

業務の中で必要に応じて、必要な記述を勉強してくって感じで。

はい、結論から。

 

for (Memo memo in mesBox.values) {
if(memo.categoryCd == _categoryCd) {
if(new RegExp(r'.*' + _searchWord +'.*').hasMatch(memo.title)) {//ここが大事
_memos.add(memo);
}
}
}

RegExpが最も大事な記述です。

RegexExpression(のはず...)が正規表現と呼ばれるものです。

 

は?って方もいっぺんコピペして

動かしてみてください。

 

何かしらのパターンで文字検索したい時に

よく利用されます。

 

ほんの少し解説すると、

「.」:任意の一文字

「*」:0回以上の繰り返し

という意味になります。0回以上ってなんやねんて方。

うーん、あってもなくてもいいって感じ?

この解説に長くなるのは本意ではないので、

ここまでにします。

 

要は

「.*」任意の文字の繰り返しが0回以上ある

 

任意の文字列(あってもなくてもいい)

+

検索ワード

+

任意の文字列(あってもなくてもいい)

 

という形式でパターン一致をみます。

先の例を繰り返し書くと

 

「あい」で検索すると

  • あかさたな
  • あいうえお←Hit!
  • ゆがんだあい←Hit!
  • あいのない話←Hit!
  • あばばばば

 

2番目のは、「あい」 + 任意の文字列(以下、任)

3番目のは、任 + 「あい」

4番目のは、任 + 「あい」 + 任

でヒット!となります。

2,3番目は、任意の文字列があってもなくてもいい

ということに注意です。

 

最後に、

メソッドhasMatchはこのパターンに一致したら

trueという感覚的にわかるものです。

 

このように、正規表現によって

検索機能が実現できます。

 

補足

一応補足ですが、

 

「アイ」で検索すると

  • あかさたな
  • あいうえお
  • ゆがんだあい
  • たあいのない話
  • あばばばば

はヒットしませんし、

 

「愛」で検索しても

  • あかさたな
  • あいうえお
  • ゆがんだあい
  • たあいのない話
  • あばばばば

ヒットしません。

ヒットしません...

ヒットしないのです...

 

検索ワードとパターン内の文字のひらがな・カタカナ・漢字は

完全に一致していないといけません。

アルファベットも同様です。

 

ここを解消しようとすると、

カタカナの例では、

「あい」ときたら

「アイ」、「アい」、「あイ」

も検索ワードに含めるという処理を書くことが必要です。

 

余談の余談ですが、

これを実装するために、if分岐で||(or)を書かなくても

(=if( 正規表現 or 正規表現 )としなくても)

正規表現の中で「または」と書くことができます。

 

めんどくさいので、僕はやりませんよ。

あんまり面白そうでもないし...

 

コード全貌

僕のコード独自のものが入っているので、

コピペしただけでは動きませんが...

 

黄色のコメントのところがポイントですので

真似するのも難しくはないんじゃないかなと思います。

 

プロパティ宣言

TextEditingController _cont= new TextEditingController();//補足に詳細
final List<Memo> _memos = [];
static int _categoryCd = -1;
String _searchWord = '';

 

絞り込み用ロジック

void loadData() async {//テキスト変更イベントで呼び出される、項目絞り込みロジック
Hive.openBox<Memo>(_memo).then((value) {
Box<Memo> mesBox = value;
_memos.clear();
for (Memo memo in mesBox.values) {
if(memo.categoryCd == _categoryCd) {
    //項目の絞り込み
if(new RegExp(r'.*' + _searchWord +'.*').hasMatch(memo.title)) {
_memos.add(memo);
}
}
}
    setState(() {});//絞り込みを反映するために、再描画。非同期処理の最後に記述。
  });
}

検索用テキストボックス

TextField(
controller: _cont,
style: TextStyle(fontSize: 16),
textAlign: TextAlign.left,
onChanged: (word) {//テキスト変更イベント
_searchWord = word;
loadData();// 絞り込み開始
},
decoration: const InputDecoration(
labelText: '',
filled: true,
fillColor:
Color.fromRGBO(255, 255, 255, 1.0),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black12, width: 1.0),
),
),
)

 

一覧表示用のロジック

ListView.builder(
// リストを生成
itemBuilder: (context, index) {
final daymemo = _memos[index];// 絞り込まれた項目が表示

return Dismissible(...)

 

後から気づいたんですが、

変数「_serachWord」は実は不要でした。

TextEditingController(=_cont).textというので

テキスト入力に入っているワードを取得できるので

わざわざプロパティとして準備しなくてよかったです。

 

補足

コードにあるコメントですが、

TextEditingControllerをあえて、プロパティにしているのは

訳があります。

TextFiled

    controller:new TextEditingController

とかくと、(Onchangeイベント等で)

画面が描画されるたびにカーソル位置が

リセットされ、一番左端になってしまいます。

 

つまり、素朴にaiuと入力しても

(カーソルを|とすると)

 

  1. |a
  2. |ia
  3. |uia

 

となるのです。ですので、

いちいちnewしないで、プロパティ化しています。

 

という具合でキーワード検索を実装しました。

どうでしょうか。正規表現という言葉さえ知っておけば

割と誰でもすぐ実装できます。

 

正規表現は、

もっといろんな表現で様々なパターンの

文字検索ができます。

正規表現だけで無限に記事が書けそうなくらい。

 

ま、「含む」くらいの実装なら

今回書いたような内容で十分です。

それ以上のことは、僕は知りません!

 

 

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Android/iOSクロス開発フレームワークFlutter入門 [ 掌田津耶乃 ]
価格:3300円(税込、送料無料) (2021/10/25時点)

楽天で購入
[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Flutter モバイルアプリ開発バイブル [ 南里勇気 ]
価格:3509円(税込、送料無料) (2021/9/20時点)

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Javaのオブジェクト指向がゼッタイにわかる本[第2版] [ 立山秀利 ]
価格:2530円(税込、送料無料) (2021/9/26時点)

エンジニア発オンラインスクール【RUNTEQ】