vuetify.jsのautocompleteを利用して、よみがな検索した結果を表示する方法

個人の時代と呼ばれるようになったので、WEBアプリくらいも個人で開発運用する時代だと思って、一人でWEBアプリ開発中の @nabeemichi です。

一人で開発、運用するにはできるだけコーディングする量を減らしてWEBアプリを作らないと、いつまでたっても完成しないので、 nuxt.jsを使い、CSSフレームワークとしてはvuetify.jsを利用して開発しているのですが、 ちょっとハマったので、トライアンドエラーした記録をしておきます。

nuxt.jsやvuetify.jsとはなんぞや?と思う方もいると思いますが、今回は完全にプログラミングの内容になっておりますので、 エンジニア界ではお決まりのフレーズである、ググレカスを前提に攻めていきます。 ググっても出てこないことを主に書いていこうと思います。

課題となる事象

まずは何が課題となったかについてです。 グーグルの検索などでは、ユーザーが入力した内容で、検索したそうなワードをオートコンプリートで候補を出してくれます。

f:id:hrmch-ioii:20181113222032p:plain

例えば、「て」と入力したら、オートコンプリートとして「天気、天気東京、、、、、」などと検索したそうな単語を表示してくれるやつです。

これと同じようなことを実現しようとした場合、サーバサイドではelasticsearchを利用して検索した結果をフロントエンドでオートコンプリートとして表示させるのが一般的だと思われます。

今回はvuetify.jsのAutocompleteコンポーネントを利用して、これを実現しようとしましたが、オートコンプリートが表示されないケースがありハマりました。 具体的には、

Autocompleteコンポーネントで「て」と入力したら、「天気、天気東京、手」などが出てきてほしいケースを実現するのにハマったのです。 でも「天」と入力すると「天気、天気東京」は出現するという現象。

オートコンプリートを表示するまでの一連の流れ

  1. vuetify.jsのAutocompleteにユーザーが入力(例:「て」と入力)

  2. ユーザーがインプットするたびに入力内容をelasticsearchで検索(例:検索結果として["天気", "天気東京", "手"]がヒットする)

  3. elasticsearchの検索結果をvuetify.jsのAutocomplete側のオートコンプリート用のitemsと連動させる

課題となる事象の原因

vuetify.jsのAutocompleteコンポーネントにデフォルトで「:filter」が指定してあるため、インプットした文字でフィルタリングがされる仕様になっているため。

今回のケースでは、オートコンプリートのitemとしてelasticsarchの検索結果をAutocompleteコンポーネントitemsで管理できているのだが、さらに「て」でfilterをしているので、漢字の「天気」などは表示されない

ということであります。

解決策

Autocompleteの no-filterプロパティがあるので、これを有効にしてあげましょう。

<template>
  <v-autocomplete
    :loading="loading"
    :items="items"
    :search-input.sync="search"
    v-model="select"
    no-filter // ☆これをつけないと、インプットした文字でフィルタされて、読み仮名検索とかできない☆
  ></v-autocomplete>
</template>

<script>
  import elasticsearchClient from '~/plugins/elasticsearchClient'; // ここはelasticsearchのドキュメントを読んでelasticsearchのJS用ライブラリを頑張って入れる

  export default {
    data() {
      return {
        loading: false,
        items: [],
        search: null,
        select: null,
        
      }
    },
    watch: {
      search(val) {
        val && val !== this.select && this.querySelections(val)
      }
    },
    methods: {
      
      querySelections(v) {
        const me = this;
        this.loading = true

        elasticsearchClient.search(
          {
            index: 'hoge',
            type: 'doc',
            body: {
   // 検索はいい感じにオートコンプリートされるリストがとれるようにelasticsearchの設定とqueryを頑張る。
              "query": {
                "match_phrase_prefix":
                  {"fuga": v}
              }
            }
          }
        ).then(
          function (resp) {
            const hits = resp.hits.hits;
    // 検索結果をitemsに打ち込むことでオートコンプリートとして表示される。
            me.items = hits.map((hit) => {
              return hit._source;
            });

          }, function (err) {
            console.trace(err.message);
          }
        ).finally(
          () => {
            this.loading = false
          }
        )
      }
    }
  }
</script>


解決策に対する根拠

f:id:hrmch-ioii:20181113231054p:plain

vuetifyjsのAutocompleteコンポーネントのソースコードを見てみると、赤枠で書いてあるところにfilterのデフォルト関数が定義されており、 ユーザーが入力した文字(queryText)がオートコンプリートとして表示する文字(itemText)に含まれているかを判定しているので、ほぼ間違いなし。 no-filterをつけると、このfilter関数が実行されなくなるので解決