Nuxtに全文検索サービスAlgoliaを導入する

投稿日
Nuxtに全文検索サービスAlgoliaを導入する

ブログやサービスを作っていると、往々にして検索機能が欲しくなる事があります。


いざ実装しようとするとどの部分に検索をかけるか、部分一致検索にするか完全文字一致にするか…などなど意外とめんどくさい作業ですよね 😅




そんな中、Algoliaは検索に特化したサービスです!



検索機能を自身のサービスに簡単に実装する事が出来ますし、

しかも色んな言語やフレームワークをサポートしています。

Algoliaがサポートしている言語フレームワークについて

Algolia公式ページより引用

フロントであれば今時のReactやVue、バックエンドはRubyにPythonと幅広く対応しています!




実際にどのようなサービスかについては公式の動画があるんですが、


それ以上に公式サイトのWidgetページを見ると、出来る事のイメージがしやすいです。

Widgets Showcase | Vue InstantSearch | Building Search UI | Guide | Algolia Documentation




ここではNuxt.jsにAlgoliaを導入します。

Nuxt ContentというNuxtでブログなどSSGを作るのに特化したライブラリ上での解説ですが、Nuxt.jsで作った場合と差異はないはず。

検証した環境

1 nuxt 2.14.12
2 @nuxt/content 1.11.1

前準備

料金について

料金は0円からスタート可能です

Algoliaの料金について

↑の公式のPRICINGページに書かれているように1ヶ月に10,000検索(10UNIT = 10 x 1,000検索)までは無料でいけます。

とりあえず試してみて、検索ボリュームと利便性から費用対効果を考えて判断する、というのでも良さそうです。

アカウント登録

アカウント登録はこのようなサービスなのでGitHubも選択出来るのは嬉しいところ!

Algoliaのユーザ登録はGitHubも選択出来る

名前や会社の情報を入力して作成、特に迷うところはなし。



またアカウント作成時にクレジットカードの登録は不要です。


気付いたら支払わないといけない、、といった心配をしなくて済むので安心ですね 😊

Algoliaの設定

まずAlgoliaで大きく3つの設定をします。


  1. indexの作成(検索対象1つにつき、1つのindexを作成)
  2. 検索するための元となるデータを設定
  3. 検索対象のリストを作成(重み付けも合わせて)

1. indexの作成

検索する対象に対して1つのindexを作成します。

To handle indices across multiple environments, just prefix/suffix your index name like dev_NAME, test_NAME, or prod_NAME

複数の環境でインデックスを処理するには、dev_NAME、test_NAME、prod_NAMEなどのインデックス名のプレフィックス/サフィックスを付けるだけです(Google翻訳)

prefix/suffixで環境を自動判別してくれる、また環境毎に1つのindexが必要

と作成時に表示されるので、

  • dev_prod_のようにprefix/suffixをつければ自動的に開発用、本番用といった差異を自動判別してくれる
  • 開発用、本番用など環境毎に違うデータを試したい場合は違うindexを作る必要がある

という事が分かります。

2. 検索するための元となるデータを設定

検索するためのデータを追加します。


追加方法は

  1. API経由でAlgoliaにアップロード
  2. JSONファイルをローカルで作成してAlgoliaにアップロード
  3. Algolia上で入力

の3つ。



私の場合ブログのため、

masterマージされたのをフック → CIでAPIを叩いて検索用のJSONをAlgoliaに自動アップロード

というのが最終目標ですが、今は簡単に検索を試したいので検索用のファイルをアップロードします。

testData.json
[
  {
    "title": "reset.cssをNuxt.jsに導入する",
    "body": "reset.cssをNuxt.jsのプロジェクトに導入する方法を書きます",
    "tags": ["Nuxt.js", "reset.css"]
  },
  {
    "title": "Nuxt.jsで必要なFont Awesomeだけをimportする",
    "body": "Nuxt.jsでFont Awesomeを使用する際に使われる nuxt-fontawesome パッケージ。",
    "tags": ["Nuxt.js"]
  },
  {
    "title": "VueでのCSS Modulesの書き方",
    "body": ""
  },
  {
    "title": "Reset.cssをNext.jsに導入する",
    "body": "Reset.cssをNext.jsに導入します、ここでは2つのやり方をご紹介します。",
    "tags": ["Next.js", "reset.css"]
  }
]

自分のブログの記事データをいくつか適当に、そこに対してタグを設定した状態で上げてみました。



なおここでのアップロードはJSON以外にも、CSV, TSVが対応しています。

JSON以外にもCSV,TSVに対応している

3. 検索対象のリストを作成(重み付けも合わせて)

検索対象とする属性を設定します。

また、どの属性を優先的に検索対象とするかを設定します。

検索対象とする属性と優先度を設定する

例えば↑の画像の場合、

body を検索対象から外し、優先度はtagsの方がtitleよりも高い、という設定になります。

Nuxt.jsへの実装

公式にVueを実装するやり方があるためそちらを参考にします

https://www.algolia.com/doc/guides/building-search-ui/getting-started/vue/

必要な値を取得する

必要な値は3つ。

  • index名
  • Application ID
  • Search-Only API Key

index名は、Algolia作成時に付けたもの。

Application IDSearch-Only API KeyはAogoriaのAPI Keysページに載っています。

AlgoliaのAPI KEYページ

これらの値は実装時に使用します。

ライブラリのインストールとNuxtの設定

検索に必要なのはalgoliasearch

UIを簡単に実装するのにvue-instantsearchinstantsearch.cssの2つを使います。

$ yarn add vue-instantsearch algoliasearch instantsearch.css

pluginsの作成

Vue.use()を使う必要があるためpluginsを使います。

私のプロジェクトではTypeScriptを使っているので.tsで作成、またvue-instantsearchがTypeScript未対応のため、ひとまず@ts-ignoreで回避。

~/plugins/Algolia.ts
import Vue from 'vue'
// @ts-ignore
import InstantSearch from 'vue-instantsearch'
 
Vue.use(InstantSearch)


作成したプラグインをnuxt.config.jsで設定。

mode: 'client' を使用しないとエラーが起きてしまうので設定しています。

nuxt.config.js
export default {
  plugins: [{ src: '~/plugins/Algolia.ts', mode: 'client' }],
}

コンポーネントを作成

検索をするためのコンポーネントを作ります。

ここで先ほどの index名Application IDSearch-Only API Keyを使用します。

search/index.vue
<template>
  <ais-instant-search :search-client="searchClient" index-name="<index名>">
    <ais-search-box />
    <ais-hits>
      <div slot="item" slot-scope="{ item }">
        <div>{{ item }}</div>
      </div>
    </ais-hits>
  </ais-instant-search>
</template>
 
<script>
import algoliasearch from 'algoliasearch/lite'
import 'instantsearch.css/themes/algolia-min.css'
 
export default {
  data() {
    return {
      searchClient: algoliasearch('<Application ID>', '<Search-Only API Key>'),
    }
  },
}
</script>


そして上記コンポーネントを使用するページでimportして呼び出します

<template>
  <div>
    <search />
    ・・・
  </div>
</template>
 
<script>
import Search from '~/components/organisms/search/index.vue'
 
export default {
  components: { Search }
  ・・・
}
</script>

Algoliaの検索が出来るようになった

検索が出来るようになりました!



ハイライト表示する

検索結果は以下のコードの部分です

<ais-hits>
  <div slot="item" slot-scope="{ item }">
    <div>{{ item }}</div>
  </div>
</ais-hits>


item は検索結果の1つになります。

itemが該当する箇所


↑は「nux」というワードで調べた時の結果。


_highlightResult.title.valueなどに検索結果があるのが分かります。

そのため、先ほどのコードを以下のように書き換えてみると

search/index.vue
<template>
  <ais-instant-search :search-client="searchClient" index-name="TECH-BROCCOLI">
    <ais-search-box />
    <ais-hits>
      <div slot="item" slot-scope="{ item }">
        <div v-html="item._highlightResult.title.value" />
        <div v-html="item._highlightResult.body.value" />
      </div>
    </ais-hits>
  </ais-instant-search>
</template>


検索結果にハイライトしながら表示出来るようになりました!


検索結果にハイライトを効かせながら表示する

課題

Nuxt.jsで実装時の課題としてCSR/SSRのDOMの違いに関するwarningメッセージがコンソールに表示されます。

SSRとCSRでの違いがwarningとして表示される


プラグインでUI実装のためのvue-instantsearchを使ったのが良くなかったかな… 🤔


今回はAlgoliaの検索が出来る事を簡単に確認したかったので、 vue-instantsearchinstantsearch.cssを使いましたが、 次はこれらを使わずに実装してみたいと思います!

プロフィール画像
Yuki Takara
都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。