algolia と firebase と Vue を使ってハッシュタグ検索を実装する


algolia という強力な検索サービスがあり、firebase と組み合わせることで簡単に全文検索やタグ検索などを実装することができます。今回は Instagram や Twitter にあるようなハッシュタグ検索を実装してみます。すぐできます。

aloglia のインデックス作成

aloglia にログイン後、インデックスを作成します。今回は適当に books にします。インデックス作成自体はこれだけです。データを登録すると勝手にフィールドなどを作ってくれます。

作成後、API Key を取得します。フロントページで利用する検索用 API Key とインデックス登録などをする管理用の Admin API Key があります。検索用は Vue.js 側で、管理用は Firebase functions で使用します。

algolia のインデックス登録

firestore にデータが登録更新されたら functions を起動して algolia へインデックス登録するようにします。

init.ts

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as algoliasearch from 'algoliasearch'
admin.initializeApp()
export const db = admin.firestore()

const ALGOLIA_ID = functions.config().algolia.app_id
const ALGOLIA_ADMIN_KEY = functions.config().algolia.api_key

export const ALGOLIA_BOOK_INDEX_NAME = 'books'
export const algolia = algoliasearch(ALGOLIA_ID, ALGOLIA_ADMIN_KEY)

bookCreated.ts

import * as functions from "firebase-functions"
import DocumentSnapshot = FirebaseFirestore.DocumentSnapshot
import DocumentData = FirebaseFirestore.DocumentData
import {algolia, ALGOLIA_BOOK_INDEX_NAME, db} from './init'

export const onBookCreated = functions.firestore.document('book/{bookId}').onWrite((change: functions.Change<DocumentSnapshot>) => {
  const book:DocumentData = change.after.data()
  book.objectID = change.after.id
  const index = algolia.initIndex(ALGOLIA_BOOK_INDEX_NAME)
  index.saveObject(Object.assign({}, book, { canonical: canonical }))
  })
})

firestore のトリガー onWrite を使うと特定ドキュメントが作成・更新されたときに関数を実行できます。

book のスキーマはこのようになっています。 hashtags というフィールドに配列でハッシュタグを格納しています。

{
  id: 'example_id',
  title: 'book title',
  hashtags: ['foo', 'bar', 'baz']
}

algolia のハッシュタグ検索

無事にインデックスが登録されると algolia のコンソールにデータが表示されます。ここで2箇所設定を変更します。

Dispay Settings

Dispay -> Faceting の項目に hashtags を追加します。

Searchable Attributes

次に Ranking -> Searchable Attributes の項目にも hashtags を追加します。

こうすると Browse のページにハッシュタグの一覧が出てきます。

これで準備は完了です。

Vue.js

次は Vue.js でハッシュタグの候補サジェストを実装します。

必要なパッケージをインストールします。contenteditable の場合は textcomplete.contenteditable を追加でインストールしてください。

npm install --save algoliasearch textcomplete

algolia.js

import Algoliasearch from 'algoliasearch'

const config = {
  appId: process.env.ALGOLIA.APP_ID,
  searchKey: process.env.ALGOLIA.SEARCH_KEY
}
const algoliaClient = new Algoliasearch(config.appId, config.searchKey)

export const AlgoliaBooks = algoliaClient.initIndex(process.env.ALGOLIA.INDEXES.BOOKS)

export const AlgoliaBooksRegister = {
  id: 'markdown-hashtags',
  match: /(^|\s)#([a-z0-9+\-_\u30a0-\u30ff\u3040-\u309f\u3005-\u3006\u30e0-\u9fcf]*)$/,
  search: (query, callback) => {
    AlgoliaBooks.searchForFacetValues({
      facetName: 'hashtags',
      facetQuery: query
    }).then(content => {
      callback(content.facetHits)
    })
  },
  template: (hit) => {
    return '#' + hit.value
  },
  replace: (hit) => {
    return '$1#' + hit.value + ' '
  },
  header: '',
  footer: ''
}

export default ({ Vue }) => {
  Vue.prototype.$algolia = algoliaClient
}

Vue component

import { Textcomplete, Textarea } from 'textcomplete'
import { AlgoliaBooksRegister } from '../plugins/algolia'

export default {
  data () {
    return {
      book: {
        title: ''
      }
    }
  },
  mounted () {
    const editor = new Textarea(document.getElementById('title-example'))
    const textcomplete = new Textcomplete(editor)
    textcomplete.register([AlgoliaBooksRegister])
  }
}

これで # を入力すると algolia からリストを取得してサジェスト表示をしてくれます。

非常に簡単ですね。