まえがき
FirebaseのFirestoreは便利だが、クエリーの書き方に制限が多くて使い物にならない。
制限を解消するため他のサービスと連携する必要がある。
Algolia(https://www.algolia.com/)
というサービスもあるが、
他にもAddSearch(https://www.addsearch.com/)
というAlgoliaよりも微妙に安いサービスが有る。
今回はAddSearchの記事です。
僕が人柱になって記事書けば日本で発展してくれて、賢い方たちが情報発信してくれたら嬉しいなーっていう魂胆で書きましたよ。
プラン(お金関連)はココにあります。
https://www.addsearch.com/pricing/
Linux開発者のリーナス同じフィンランドのヘルシンキにある会社のサービス。
使い放題らしい。
サンプルや世間の情報があまりないのが残念。
情報が英語ですらあんまりない。
「AddSearch」でググっても「Add」と「Search」での結果が出る始末。
Firestoreの中身の値をAddSearchへ中身をコピーするSync(同期)する仕組みつくって、
コピーされたAddSearchからクエリーを書く方法で対処したいです。
FirebaseのデータベースサービスのFirestore上でドキュメントが生成・削除・更新があれば、
それを検知して、AddSearchに更新をかけるということ。
SyncするためにFirebaseのCloud Functionsで書きます。
「Cloud Functions for Firebase」内のデータベースをsyncさせるイメージです。
users/
配下にある前提で話を勧めていきますね。
Cloud Functiondsの準備
キーの設定
firebase functions:config:set addsearch.sitekey="{サイトキー}" addsearch.secretkey="{シークレットキー}"
{サイトキー}
これはあなたの管理画面の「Your Site Key」にしてください。
{シークレットキー}
これはあなたの「Your Secret API Key」にしてください。
{
と}
はいらないですよ。
・スクショ貼っておきます。
コード
TypeScriptで書きます。
一応、JSとAddSearchで使う公式っぽいnpm
はあります(https://www.npmjs.com/package/addsearch-js-client)が、Issueできいたら検索専用なので今回使いません。
ちなみにそのnpmはTypeScriptは対応してない。
TypeScriptの型定義ファイルがなかったです。
自分でHTTTPリクエストする必要がある。
無難にaxios
を使います。使わないやり方は自分で読み替えて下さい。
Syncするための仕組み作成
準備
以下で色々npm入れて下さい。
npm install -g npm
npm i -g firebase-tools
firebase init functions
して下さい。
中に生成されるので最初にフォルダ切ったほうがいいです。
? What language would you like to use to write Cloud Functions?
ではTypeScript
を忘れずに!
> firebase init functions Mon Jul 13 10:53:23 2020
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
/Users/shinriyo/development/functions_apps
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: hoge-project (hoge)
i Using project hoge-project (hoge)
=== Functions Setup
A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.
? What language would you like to use to write Cloud Functions? TypeScript
? Do you want to use TSLint to catch probable bugs and enforce style? Yes
✔ Wrote functions/package.json
✔ Wrote functions/tslint.json
✔ Wrote functions/tsconfig.json
✔ Wrote functions/src/index.ts
✔ Wrote functions/.gitignore
? Do you want to install dependencies with npm now? Yes
> protobufjs@6.9.0 postinstall /Users/shinriyo/development/functions_apps/functions/node_modules/protobufjs
> node scripts/postinstall
npm notice created a lockfile as package-lock.json. You should commit this file.
added 287 packages from 221 contributors and audited 287 packages in 12.772s
気をつけてほしいのが「"node": "10"」にしておかないとnodeが古いとCloud Functionsがサポートしなくなるのでおそらく8となってるのを10に変えて下さい。
しかし、11はサポートしてないみたいですw
{
"name": "functions",
"scripts": {
"lint": "tslint --project tsconfig.json",
"build": "tsc",
"serve": "npm run build && firebase emulators:start --only functions",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"main": "lib/index.js",
"dependencies": {
"algoliasearch": "^4.3.0",
"axios": "^0.19.2",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.6.1"
},
"devDependencies": {
"tslint": "^5.12.0",
"typescript": "^3.8.0",
"firebase-functions-test": "^0.2.0"
},
"private": true
}
設定ファイル
こんな感じに作ってきました。
export
してる箇所ありますが後で作るのでこの瞬間はエラーになると思います。
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const env = functions.config();
// AddSearch
export const baseURL = 'https://api.addsearch.com';
// 設定のYour Site Key (Index public keyのこと)
export const siteKey = env.addsearch.sitekey;
// 設定のYour Secret API (Secret key is used in the authentication header)
export const secretKey = env.addsearch.secretkey;
// 各Functionのため
export * from './user.onDelete';
export * from './user.onUpdate';
export * from './user.onCreate';
生成
今回生成ですが、UpdateやDeleteなども作ります。(後述)
本当はCREATEするときにPOST使いたいと思うが、
https://www.addsearch.com/docs/api/indexing-create/
id
はこちらで定義されたやつは使えない!!
仕方ないので
POSTはAddSearchの自動生成のテキトーなIDを使いたいときではいいが、
今回はFiresoreのdocumentIdを使いたいので、
なので以下これはだめですね。
axios.post(`${baseURL}/v2/indices/${siteKey}/documents/`, args, {
https://www.addsearch.com/docs/api/indexing-update/
新規作成での同期はUPDATE
でも使えるうPUTで書きます。新規も可能。
コード全体。
ファイル名にわざと.
つけてますが動きます。
嫌なら名前を好きに付けて下さい。
import { index, baseURL, siteKey, secretKey } from './index';
import * as functions from 'firebase-functions';
// AddSearchのDocument生成
export const userOnCreateForAddSearch = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
const axios = require('axios');
const data = snap.data();
const objectID = snap.id;
const args = {
withCredentials: true,
custom_fields: {
...data
},
headers: {
"Content-Type": "application/json",
}
}
axios.put(`${baseURL}/v2/indices/${siteKey}/documents/${objectID}`, args, {
// HTTP Basic Auth
auth: {
username: siteKey,
password: secretKey,
}
})
.then((response: any) => {
console.log(response.data)
})
.catch((error: any) => {
console.log(error)
})
.then(function () {
console.log("*** finish ***")
})
});
注意点
axios.postの第三引数に「HTTP Basic Auth」にいるものを書く。
// HTTP Basic Auth
auth: {
username: siteKey,
password: secretKey,
}
の箇所。ハマった。
args
に含めてもだめだった。
内容は、custom_fields
を使って書くこと。data
と書いてハマった。
{
"id": objectID,
"custom_fields": {
...data
}
}
みたいにやること。
custom_fields
の中に欲しいデータ書くらしい。
idはAlgoliaみたくobjectIDと書くんじゃなく、単にid
withCredentials
はおそらく必須。
削除処理
PUTSの処理をそもままやればいけると思ったらできない。
args
は2番目だったと思ったら違う。
このサイト、よくみたら
PUTとDELETEで引数の順番が違う。
axios.delete(url[, config])
axios.put(url[, data[, config]])
import { index, baseURL, siteKey, secretKey } from './index';
import * as functions from 'firebase-functions';
// AddSearchのDocument生成
export const userOnCreateForAddSearch = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
const axios = require('axios');
const data = snap.data();
const objectID = snap.id;
const args = {
withCredentials: true,
custom_fields: {
...data
},
headers: {
"Content-Type": "application/json",
}
}
axios.put(`${baseURL}/v2/indices/${siteKey}/documents/${objectID}`, args, {
// HTTP Basic Auth
auth: {
username: siteKey,
password: secretKey,
}
})
.then((response: any) => {
console.log(response.data)
})
.catch((error: any) => {
console.log(error)
})
.then(function () {
console.log("*** finish ***")
})
});
更新
import { index, baseURL, siteKey, secretKey } from './index';
import * as functions from 'firebase-functions'
export const userOnUpdateForAddSearch = functions.firestore
.document('users/{userId}')
.onUpdate(async (event, context) => {
const axios = require('axios');
// const oldData = event.before.data();
const newData = event.after.data();
const documentId = event.after.id;
const args = {
withCredentials: true,
custom_fields: { ...newData },
headers: {
"Content-Type": "application/json",
}
}
// PUT /v2/indices/{index public key}/documents/{document id}
axios.put(`${baseURL}/v2/indices/${siteKey}/documents/${documentId}`, args, {
// HTTP Basic Auth
auth: {
username: siteKey,
password: secretKey,
}
})
.then((response: any) => {
console.log(response.data)
})
.catch((error: any) => {
console.log(error)
})
.then(function () {
console.log("*** finish ***")
})
});
Cloud Functionsにデプロイします
firebase deploy --only functions
あとがき
全部削除はわからない
消し方わかりません。中の人に消してもらいましたw
非対応の型?
タイムスタンプや、マップやマップの配列はうまく同期されてませんでした。
対応してないのか?教えてほしい。
ログ
これ叩いてちまちま見ている。
もっといいやり方あったら教えてほしい。
firebase functions:log
AddSearchに今はいってるのか確かめるのはPostmanとかで見るしか無いです。
Webで見る仕組みを来月2020/08くらいに出すっぽいです。
おまけ
- 現状確認しているバグ
自動生成したやつではなく、マニュアルで(自分で)作ったIDでは検索できない。
サーバーエラーになる。(ほうこくすると今対処してるらしい)