#なぜalgolia?
アプリなら大体ついてる機能、「キーワード検索」。アプリ側にデータを持たせてfilterやsortを使うのは、めちゃめちゃスペックが落ちるので絶対に避けたいです。
ネイティブアプリのエンジニアであれば、Firebaseを愛用している方も多いかと思いますが、Firebaseでサポートされていないのがクエリの「全文検索」です。指定したキーワード通りの単一・複数ドキュメントをとってきたり、ソートのクエリを追加したり、indexをふるなどはできますが、全文検索はサポートされていません。そこで、公式ドキュメントが推奨する「algolia」を使って、全文検索機能を補填しようと思います。
#作るものイメージ
アプリからFirestoreにデータを書き込むと、Cloud Functionsにイベントが発火し、書き込んだ内容をalgoliaに転記するイベントをNode.jsで実装します。algoliaにデータが保存されるので、検索する場合は直接ここにアクセスします。
#費用
Cloud Functions自体は無料のSprakプランで使えるのですが、algoliaのようなサードパーティサービスとの連携はBlazeプランが必要です。Blazeプランは無料枠があるので、とりあえずやってみるだけならほぼ費用はかかりません。algoliaも、無料枠があるので、とりあえず、って感じなら総じて無料でできます。
(Blazeプランは従量課金で、通信量を全く管理していなかった結果数100万円の請求がきたという都市伝説を聞くのでお気をつけください・・・)
#実装
###Firebase側の実装
FirestoreもしくはRealtimeDatabaseから書き込まれた際に、検索対象とするデータをalgoliaに転記するfunctionをNode.jsで実装します。具体的には、下記Firebase公式ドキュメントを参照してください。Nodeの管理にはNVM(NodeVersionManager)が推奨されています。Nodeの環境構築で、NVMをHomebrewでインストールしている記事をちょくちょく見かけますが、NVMの公式ドキュメントによればHomebrewによるインストールは非推奨です。Nodeは使うバージョンがプロジェクトによってコロコロ変わったり、管理が激しいので、ドキュメント通りに構築することを強くお勧めします。
また、Firebaseの公式ドキュメントによればNodeのバージョンは8、10が推奨されていますが、10はベータ版のため8をお勧めします。
CloudFunctionsをデプロイするための環境構築(Firebase公式)
ドキュメント通り進めていき、index.jsファイルを下記の通り実装します。今回は、新規ドキュメントが追加されたらalgoliaに転記するイベントを実装しています。削除時、更新時など、他にも様々な発火条件を設定できます。
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
const algoliasearch = require('algoliasearch')
const ALGOLIA_ID = functions.config().algolia.app_id
const ALGOLIA_ADMIN_KEY = functions.config().algolia.api_key
const ALGOLIA_SEARCH_KEY = functions.config().algolia.search_key
const ALGOLIA_INDEX_NAME = 'sampleDemo'
const client = algoliasearch(ALGOLIA_ID, ALGOLIA_ADMIN_KEY)
//発火タイミングを指定(今回はonCreate)、発火する場所(コレクション名等)を指定
exports.onProductUpdated = functions.firestore.document('コレクション名/{id}').onCreate((snap, context) => {
const data = snap.data()
data.objectID = context.params.id
// algoliaのindexへ追加
const index = client.initIndex(ALGOLIA_INDEX_NAME)
return index.saveObject(data)
})
###アプリ側の実装
CocoaPodか、Carthageでインストールできます。インストールしたら、ソースコードに「InstantSearchClient」をimportしてください。
本来はAPIクライアントを作成してエラーハンドリングをしますが、今回は、とりあえず動かすためのコードでJSONのデコードまでをやります。JSONのサンプルレスポンスも公式に上がっているので、お好みに合わせてどうぞ。
import UIKit
import InstantSearchClient
class ViewController: UIViewController {
let appId = "algoliaのアプリID"
let apiKey = "algoliaのapiKey"
override func viewDidLoad() {
super.viewDidLoad()
let client = Client(appID: appId, apiKey: apiKey)
let index = client.index(withName: "sampleDemo")
let query = Query(query: "semple")
index.search(query, completionHandler: { (content, error) -> Void in
if let error = error {
print(error)
} else {
guard let content = content else {
fatalError("no content")
}
let data = try! JSONSerialization.data(withJSONObject: content, options: .prettyPrinted)
let response = try! JSONDecoder().decode(Hits.self, from: data)
print(response)
}
})
}
}
import Foundation
struct Hits: Codable {
let hits: [SampleDemo]
}
struct SampleDemo: Codable {
let title: String
let content: String
let objectID: String
}
これで全文検索結果が帰ってきます!
あえて検索キーワード「sample」を「semple」にしてリクエストしていますが、ちゃんと帰ってきました。最高!
タイポは、"typoTolerance"がtrue|falseのパラメータを持っているので、状況に応じてタイポの許容可否は変更できます。
#使ってみて
CloudFunctionsの実装は若干慣れが必要ですが、サードパーティサービスとAPI連携できるのは頼もしいです。Firebaseで完結する機能もたくさんあるので、個人開発には向いているかと思います。アプリ側の実装も簡単なので、手ごろに全文検索機能を実装できました。
algoliaは2019年から東京にも拠点を設立しており、今後も楽しみです!!!(早くGoogleが買収してFirebaseに全文検索機能入れろとかいっちゃダメ)
以上