MENU

【SpringBoot】検索機能をつけてみた。(複数ワード・半角全角対応)

こんにちは、くまごろーです。
今回は、作成中のアプリに検索機能を付けてみました。
一応、複数ワード対応&半角/全角どちらの空白にも対応するようにしてます。
 
 
 
1 Serviceクラス

今回は、viewファイルで入力された文字列を、Controllerクラス→Serviceクラスへ渡して、Serviceクラスで検索可能な形に整えることにしました。

<Serviceクラス>

public List<PostByNickname> SearchPosts() {
  String wordsStr = "全角 と 半角 が 混ざった 文字列"
  //検索ワードの入れ物としてlistを用意
  List<String> keyWordsList = new ArrayList<String>();
		
  //検索ワードが1つの場合
  if (wordsStr.indexOf(" ") == -1 && wordsStr.indexOf(" ") == -1) {
    //listにその単語をaddしておしまい
    keyWordsList.add(wordsStr);
    //検索ワードが複数の場合
  } else {
    //空白が全角、半角の場合も対応
    while (wordsStr.indexOf(" ") != -1 || wordsStr.indexOf(" ") != -1) {
      //空白の場所を割り出すための変数
      int number = 0;
      int numberHalf = wordsStr.indexOf(" ");
      int numberFull = wordsStr.indexOf(" ");
      //一番手前の空白が半角ならnumberHalf、全角ならnumberFullをnumberに代入
      if (numberHalf != -1 && numberHalf < numberFull) {
        number = numberHalf;
      } else {
        if (numberFull != -1) {
          number = numberFull;
        } else {
	  number = numberHalf;
	}
      }
    //空白の場所で文字列をトリミング
    String word = wordsStr.substring(0, number);
    //抜き出した単語をlistにadd
    keyWordsList.add(word);
    //抜き出した分の文字列を削除
    wordsStr = wordsStr.substring(number + 1);
    }
  //最後の検索ワードをlistにaddする
  keyWordsList.add(wordsStr);
  }
  //検索ワードListを配列化
  String keyWords[] = keyWordsList.toArray(new String[keyWordsList.size()]);

  //DBで検索
  List<PostByNickname> list = pDao.findPostByWords(keyWords);
		
  return list;

}

  
改めて見てみると、本当ナンセンスなコードの組み方してる・・・(-_-;)
if節がマトリョーシカみたいになってるのは、後でなんとかしなきゃなあ。
 
 
 
2 DAOクラス
 
今回はDAOクラスを使って、JPQLでやりました。理由は、ネイティブクエリではSQL文を動的に変化させられなさそうだったからです。(自分の調べ方が悪かっただけかもしれないが)

<DAOクラス>

//投稿検索(複数ワード対応)
@SuppressWarnings("unchecked")
@Override
public List<PostByNickname> findPostByWords(String words[]) {
  String qstr1 = "select P.postId, P.postCategory, U.username, U.userNickname, P.postTitle, P.postBody, P.createdAt, P.updatedAt from Post P ";
  String qstr2 = "left join SiteUser U on P.username = U.username where P.postBody like concat('%', ?1, '%') ";
  //検索ワードの数に応じて、qstr3の結合回数を変化させる
  String qstr3 = "and P.postBody like concat('%', ?number, '%')";
  
  //qstr1とsqtr2はあらかじめ結合しておく
  String qstr = qstr1 + qstr2;
  
  //検索ワード数が2以上の場合
  if (words.length > 1) {
    for (int i = 2; i < words.length + 1; i++) {
      String currentNumber = String.valueOf(i);
      //SQL文の引数に数字を振る
      qstr3 = qstr3.replace("number", currentNumber);
      //SQL文に結合
      qstr += qstr3;
      //SQL文の引数を所定の変数名に戻す
      qstr3 = qstr3.replace(currentNumber, "number");
    }
  }

  Query query = em.createQuery(qstr);

  //検索ワード数に応じて、パラメータをセット
  for (int i = 0; i < words.length; i++) {
      query.setParameter(i + 1, words[i]);
  }

  List<Object[]> listObj = query.getResultList();
  List<PostByNickname> list = listObj.stream().map(PostByNickname::new).collect(Collectors.toList());

  return list;
}

 
検索ワード数を「配列.length」メソッドで確認して、その数に応じてSQL文を変化させるようにしました。また、SQL文にセットするパラメータも、for文で番号を振る方式にしました。
なんとなく思い付きでやってみた方法だけど、これでうまくいってしまいました・・・