GeekFactory

int128.hatenablog.com

プロパティの一部をkindに含める設計について

データストアで時系列データをカレンダー表示したい場合、どのような設計が最適でしょうか。下記のモデル(Event)を考えます。

プロパティ 説明
key com.google.appengine.api.datastore.Key キー(userId/time)
userId java.lang.String ユーザID
time java.util.Date 日時
value int 数値

下記のアクセスパターンを考えます。

  • カレンダーに1ヶ月間のデータ(日毎の平均値など)を表示する。
  • カレンダーの日付をクリックすると、1日のデータを表示する。
  • 最新10件のデータを表示する。
  • ログインユーザのデータを対象とする。

私の考える設計

まず、下記のモデル(EventCalendar)を作成します。

プロパティ 説明
key com.google.appengine.api.datastore.Key キー(userId/yyyymmdd)
userId java.lang.String ユーザID
date java.util.Date 年月日
yearmonth int 年月(例:201009)
average double 平均値

そして、Eventの親キーにはEventCalendarを指定します。これで特定の年月日に含まれるEvent(正確にはEventのキー)を取得できるようになります。更新パターンを考慮すると、1日分のデータをエンティティグループにするのは適当と思います。

年月を指定してEventCalendarを取得するのはインメモリフィルタで良さそうですね。1年間使っても365件から検索です。

最新10件のEventを取得するにはコンポジットインデックスが必要になります。

ふと考えたこと

フィルタが増えるにつれて、インデックスが増えてしまいます。すべてのフィルタにuserIdが含まれるので、kindにuserIdを含めてしまえばシングルプロパティのフィルタを使える場面が増えます。例えばユーザIDが1234であれば、EventU1234といった感じです。

管理コンソールが見にくくなるデメリットはあります。ただ、コンポジットインデックスが必要になる場面は減るので、設計の自由度(変更容易性)は格段に上がると思います。

Slim3のMetaクラスはkindの変更や派生を許していません。Datastore#query(String)やentityToModel()を使うことでkindを指定したクエリは実現できそうですが、簡単な方法を提供していないのは理由があるのでしょうか?

長くなりましたが、最適な設計も含めて教えていただけると幸いです。