kintoneの検索をマスターしよう！

この記事は[ソニックガーデン 若手プログラマ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/sonicgarden-junior)の15日目の記事です。

# はじめに
「ソニックガーデン」と聞くとRuby on Railsのイメージが強いかとは思いますが、お客さまの課題やお悩みによってはFirebaseであったりkintoneを採用することがあります。
私はソニックガーデンに入社してかれこれ7年ほど（7年！）kintone開発をメインにしてきたこともあり、定期的に問題が出たり出なかったりするkintoneのちょっとややこしい検索仕様についてまとめておこうと思います。

# kintoneの検索仕様
kintoneの検索における注意事項は以下の通りです。

https://jp.cybozu.help/k/ja/id/040689.html#search_search_details_60

また、こんなヘルプページもあります。

https://jp.cybozu.help/k/ja/id/040315.html

悩める子羊が多いのでしょう。具体例も書いてあるので、読んでいただければ基本的には、なるほどね、と思っていただけるのではないでしょうか。
よく躓きがちなのはやはり、
・日本語一文字では検索できない。ただし、検索対象が日本語2文字以上の場合のみ
・英数字は単語単位でしか検索できず、部分一致がきかない
といったあたりです。


## 日本語一文字では検索できない。ただし、検索対象が日本語2文字以上の場合のみ
「田中」を「田」で検索することはできないけれど、「田」を「田」で検索することはできる、ということです。
私は最初に「日本語一文字では検索できない」とインプットしてしまったために、いまだに「森」で検索すると「森」さんはヒットするけど「森川」さんがヒットしないのはなぜ？というようなことを尋ねられると一瞬なんでだっけ？と考えてしまいます。

## 英数字は単語単位でしか検索できず、部分一致がきかない
「SUPEREIGHT」は「SUPER」でも「EIGHT」でもなく「SUPEREIGHT」でしか検索ができません。
「SUPER EIGHT」であれば「SUPER」や「EIGHT」で検索ができます。ただし「SU」や「GHT」ではやはりヒットしません。
スペースによって分割された単語単位での検索、ということになります。

## スペース以外にも、単語の区切りとみなされる記号や文字はあるの？
[検索キーワード入力時の注意事項](https://jp.cybozu.help/k/ja/id/040689.html#search_search_details_60)の「英数字で検索する場合」「記号で検索する場合」に書いているのですが、
- アンダースコア（_）」、番号記号（#）、およびプラス（+）」は、全角および半角共に単語の一部とみなされます。
- 次の記号は検索キーワードから除かれます。
```
日本語、および中国語の句読点
全角、および半角の次の記号
- & | / ! ( ) { } [ ] ^ " ~ * ? : ; \ $ % @ ' ` = < > , .
```
とあるとおり、「_」「#」「+」は区切り文字として機能しませんし、検索キーワードから除かれる記号の例に挙げられているものは区切り文字の役割を果たしてくれそうです。（後者に関してはすべて確認したわけではないのでおおよそ）
アンダースコアなどで商品の型番など繋いで表記する場合には完全一致で運用する覚悟を決める必要があります。

## 英数字と2文字以上の日本語とを組み合わせた文字列を検索する場合
これはkintoneのヘルプページ[検索したいデータがヒットしません](https://jp.cybozu.help/k/ja/id/040315.html)で一番最後に挙がっている例です。
ちょっと掴みづらい部分もあるのですが、要は、前段で解説した「日本語一文字では検索できない。ただし、検索対象が日本語2文字以上の場合のみ」と「英数字は単語単位でしか検索できず、部分一致がきかない」という条件は、検索対象の文字列が日本語＋英数字の組み合わせであっても適用される、というだけの話です。
ですので、「SUPER EIGHTとなにわ男子」というデータの場合だと「SUPER」「EIGHT」「となにわ男子」のような区切りの単語とみなされるため、こんな感じの検索結果になります。

<table>
  <caption>「SUPER EIGHTとなにわ男子」に対する部分一致検索結果</caption>
  <thead>
    <tr>
      <th>検索語</th> <th>結果</th> <th></th>
    </tr>
  </thead>
  <tr>
    <td> SUPER </td> <td>◯</td> <td></td>
  </tr>
  <tr>
    <td> EIGHT </td> <td>◯</td> <td></td>
  </tr>
  <tr>
    <td> EIGHTと </td> <td>×</td> <td>「となにわ男子」に対して「と」で検索をかけているのでNG</td>
  </tr>
  <tr>
    <td> なにわ </td> <td>◯</td> <td></td>
  </tr>
</table>

ちなみに、「Snow ManとSixTONES」というデータの場合、「Manと」でちゃんと検索できます。「と」でも検索可能です。
なぜなら「Snow」「Man」「と」「SixTONES」という区切りになるからですね。
こうやって整理してみると傾向がわかりやすくなりました。

## 解決方法はないの？
検索仕様はどうしようもないため、検索対象とするデータ側を工夫することで部分一致が可能です。
たとえば「SUPER EIGHT」であればこんな形で輪切りにしたものを用意してあげればどこを切り取っても「含む」で検索できるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3940123/f445b186-a98e-447b-9cb4-ea12bcb72186.png)

力技ですが、どうしようもないときには保存時や一括で処理できるボタンを設置して、検索用のフィールドで検索する運用を取ってもらったりしています。
ちなみに: この手法はN-gramと呼ばれるそうです。

輪切りにするサンプルコードはこちら
``` splitSearchKey.js
kintone.events.on(['app.record.index.edit.submit', 'app.record.create.submit', 'app.record.edit.submit'], (event) => {
  const record = event.record
  const searchKey = record.searchKey.value
  const splitSearchKeys = []

  for (let i = 0; i < searchKey.length; i++) {
    for (let j = i + 1; j <= searchKey.length; j++) {
      splitSearchKeys.push(searchKey.slice(i, j))
    }
  }

  record.splitSearchKey.value = splitSearchKeys.join(' ')
  return event
})

```

# おわりに
以上、kintoneのちょっとややこしい検索仕様の話でした。
開発というよりも運用の際に問題になるところかな〜とは思いますが、念頭に置いておくとアプリの使いやすさの向上に繋がるかもしれません！

明日の[ソニックガーデン 若手プログラマ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/sonicgarden-junior) 16日目は [@yuckeyee](https://qiita.com/yuckeyee) です。お楽しみに！