FirestoreのTimestamp型について
記事の内容
今回は業務中に詰まったFirestoreのtimestamp型の取得について書いていきます。
結論を言っておくと、取得の際はnanoSecondまで意識したほうがいいです。
※Firestoreに直接データを追加したからかもしれません、原因検証は不十分です。
環境
Laravel 9 | Mysql 8 | Ubuntu |
---|
Timestamp型の中身
FirestoreのdocumentからTimestamp型の中身を取り出すと以下のような形になっています。
Google\Cloud\Core\Timestamp ^{
-value: DateTimeImmutable @1657260000 {
date: 2022-07-08 06:00:00.651 +00:00
}
-nanoSeconds: 651000000
上記のデータは日本時間の「2022年7月8日 14:00:00」です。
取得の際は日本時間でひっかけて取得できますが、手元に来るときにUTCに変換されます。(設定でどうにかなるんですかね?)
ここでの注目ポイントはdateが「Y-m-d H : i : s .u」とFirestoreで設定していないnanoSecondが付いていることです。
Firestoreに直接テストデータを作成したからかは不明ですが、「2022-07-08 14:00:00」と設定したにも関わらず、nanoSecondが付いていました。
nanoSecondがあると何がまずいのか
これがあるとstartAfterではじくことができない場合があります。
MySqlを使用してditetimeに「Y-m-d H:i:s」形式で時間を保存していました。
ここでDB内の最新データ以降のものをFirestoreから取得しようとすると重複が発生する可能性があります。
とりあえずFirestore上にtimestamp型のcreated_atが保存されているとします。
DB内の最新データを$latest = 2022-07-08 14:00:00;とした場合、以下のような感じで取得すると思います。
$docRef = $this->firestore->database()->collection('test');
$datetime = new Datetime($latest , new DateTimeZone('Asia/Tokyo'));
$query = $docRef->orderBy('created_at')->startAfter([$latest])
$documents = $query->get()
この時、Firestoreに保存されている「2022年7月8日 14:00:00」のデータは取得したくないですが、謎のnanoSecondによって「$latest <= Firestore上のデータ」になるので取得できてしまいます。
解決策
- DiteTime型のカラムにnanoSecondを3桁分格納できるように変更。
- Firestoreのtimestamp型には__toString()という関数が使用できるので、Carbon::create($firestore_time->__toString())としてそのままDBに保存する
※__toString()はnanoSecondも含めて時間を返してくれます。
まとめ
Firestoreのtimestamp型はnanoSecondがあるので注意しましょう。
もしかしたらLaravelやアプリ側から登録する際はnanoSecondがないのかもしれませんが、詳しくはまだ未検証です。
documentSnapshotのcreateTime()やupdateTime()はnanosecond持ちですがwhereなどはかけられないので、fieldの値には警戒しておいたほうがいいと思います。