Help us understand the problem. What is going on with this article?

FirebaseのCloud Functionsで、Cloud Firestoreによるトリガーじゃない関数でFirestoreにデータを保存する

More than 1 year has passed since last update.

Firebaseとかいうスーパー便利サービスの使い方の一部を紹介します。

Cloud Functions

従来、自分で借りたり用意したサーバー上で動かしていたもろもろの処理を、Firebase上に書いてしまうことができます。

サーバー用意しなくていいし、少量なら無料だし、関数をトリガーも使いやすいものがたくさん用意されています。

今日は、Cloud Functionsを使って、Analytics for Firebaseへのイベントの投稿をトリガーにして、Cloud Firestoreにデータを保存するようなfunctionを書いてみようと思います。

そんなの公式ページにサンプルあるでしょ?

ある…かな?

こちらのCloud Functions で可能な処理には、「Realtime Database(Cloud Firestoreの前身)へのデータの追加をトリガーにして、その追加されたデータに何かする」という例はありますが、新しいデータをCloud Firestoreに追加する例はパッと見つからなかったです。

コード

いきなりですがコードです。中身については下で順次説明していきます。

functions/src/index.ts
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';//※1 Admin SDK について
admin.initializeApp();//※1 Admin SDK について

exports.recordPostedScore = functions.analytics.event('post_score').onLog((event)=>{
  const firestore = admin.firestore();//※2 timestampについて
  const settings = { timestampsInSnapshots: true };//※2 timestampについて
  firestore.settings(settings);//※2 timestampについて
  const colref = firestore.collection('scores');
  colref.add({
    score: Math.round(Number(event.params.score)),
    date: new Date(),
    device_category: event.user.deviceInfo.deviceCategory,
    device_model: event.user.deviceInfo.deviceModel
  }).then(docref=>{
    console.log("Added ID is: " + docref.id);
  }).catch(e=>console.log(e));
});

AdminSDKについて

import * as admin from 'firebase-admin';//※1 Admin SDK について
admin.initializeApp();//※1 Admin SDK について

この部分です。

普段クライアント側でコーディングしてる場合、Firestoreにデータを追加するには、CollectionReferenceとかDocumentReferenceを作成しないといけないのですが、

その辺のことができるのがこのAdmin SDKです。

こちらの公式スタートガイドに書いてあるように、admin.initializeApp();を実行しておく必要があります。importの直後にやっておけば間違いないですね。

また、この公式スタートガイドはimport文のかわりにrequire文が書かれており、若干書き方が古いです(2018-12-20現在)。

ターミナルからfirebase init functionsとした場合に作成されるfunctions/src/index.tsにはこの記事にあるような書き方でimport文が書かれておりましたので、そちらの方が新しくて間違いないと思います。

const firestore = admin.firestore();

const firestore = admin.firestore();

これで、こちらのGoogle Cloudのドキュメントに書いてあるのと同じFirestore clientが得られますので、あとは

colref = firestore.collection('scores');

などとすれば、CollectionReferenceが得られるので、クライアント側でプログラミングしてる時と同じように書けます。

admin.firestore()について詳しくはこちら。→本家リファレンス

timestampについて

Cloud Firestoreに保存するデータのうち、timestamp型のものは、new Date()をそのままつっこんではいけないようです。

そのために、こちらの

  const firestore = admin.firestore();//※2 timestampについて
  const settings = { timestampsInSnapshots: true };//※2 timestampについて
  firestore.settings(settings);//※2 timestampについて

下二行のような設定作業が必要になります。

詳しくはこちらのQiita記事がわかりやすかったです。

FirestoreのTimestampの仕様変更による警告と、その対処

paramsとかdeviceCategoryとかの調べ方

あともう一つ困ったことがあったので、それについての話だけして終わります。

リファレンスを見るときは、一つずつ、何型のインスタンスが返るのか確認し、その型の使い方を改めて調べることを繰り返す

ということです。

実際にやってみましょう。

今回は、Analytics for Firebaseへのイベント投稿をトリガーとして、「そのイベントについてきたscore値」「投稿したユーザーのデバイス情報」をCloud Firestoreに保存したいです。

でも、それらの情報が一体どういう形で取得できているのかがわからない!!

ちなみにいきなりですが最終的なコードは以下のようになります。

exports.recordPostedScore = functions.analytics.event('post_score').onLog((event)=>{
//...中略...
colref.add({
    score: Math.round(Number(event.params.score)),
    date: new Date(),
    device_category: event.user.deviceInfo.deviceCategory,
    device_model: event.user.deviceInfo.deviceModel
  }//...後略...
);

この部分、大枠としてはfunctions.analytics.event(...).onLog(...)の引数に、ハンドラ関数を渡す、という形をしています。

Analytics for Firebaseへのイベント投稿をトリガーとして呼び出される関数は、こちらの本家の例によると、functions.analytics.event('event_type').onLog((event)=>{});という書き方をするようなのて、これでいい。

で、そのハンドラ関数の引数に、いろいろな情報が詰まったインスタンスを渡してくれるわけですね。

ところで、そのハンドラ関数の引数はeventという名前にしてあるわけですが、このeventには一体何型のインスタンスが渡っているんでしょうか?どう書けばそこから情報を取り出せるのでしょうか?

ということでfunctions.analyticsのリファレンスを見ると、functions.analytics.event(...)というメソッドは
functions.analytics.AnalyticsEventBuilderを返すことがわかります。

では、functions.analytics.AnalyticsEventBuilderのリファレンスを見ると、

そこにやっとonLogメソッドがあって、その返り値はfunctions.CloudFunctionだそうです。なるほど、これでCloud Functionsで実行可能な形になるわけですね。

で、そのonLogの引数として渡すハンドラ関数の引数は、何型なのかというと、
functions.analytics.AnalyticsEvent
だそうです。

やっとこれでeventの型がわかりました。

で、
functions.analytics.AnalyticsEventのリファレンスを見るとparamsとかuserとかのプロパティがあって、その中に今回記録したい情報があるらしいことが、ここまで調べてようやくわかりました

さらに、userの中にどんな情報があるかは
functions.analytics.UserDimensionsを見る必要があるし、その中のデバイス情報についてはさらに
functions.analytics.DeviceInfo
を見なきゃいけません。そこまで見てやっと返り値がstring or undefinedになります。

サンプルコード一つでポンとわからない場合、この程度のリファレンスの渡り歩きは日常茶飯事ですから、臆せずガンガン調べましょうね〜〜。

agajo
あんなに勉強して、親に高い予備校代も出してもらって東大に入り、卒業したのに、今では家と食事を親に頼りながら、年金と住民税を払うためにトイレ掃除をしている者です。
https://portal.oka-ryunoske.work/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした