本ストーリーは当社運営の「GameWith Developer Blog」の転載になります。

ウマ娘フレンド募集掲示板の Firestore 設計と検索 #GameWith #TechWith - GameWith Developer Blog
はじめに
自分が所属しているチームではいくつか攻略ツールを実装しており、去年はあつ森交換掲示板をリリースしたりしました!
その際のブログはこちらになります!

ツール開発で firestore を初めて使って感じた利点と、ハマった落とし穴 #GameWith #TechWith - GameWith Developer Blog
今回はウマ娘フレンド募集掲示板を実装した際に得た Firestore の知見や工夫について書いていこうと思います!
ウマ娘フレンド募集掲示板
ウマ娘フレンド募集掲示板は、自分のプロフィールを投稿したり、いろいろな条件で他のプレイヤーを検索したりすることができます。
【ウマ娘】フレンド募集掲示板|星3因子&完凸絞り込み検索 - ゲームウィズ(GameWith)
このツールもあつ森交換掲示板と同じく GameWith Design System を利用し Vue + TypeScript で実装を行っています。

GameWithのリプレイスについて vol.2 ~Web Components を Vue で書いたら最高だった編~ #GameWith #TechWith - GameWith Developer Blog
Firestore の設計
登録する内容は自分のフレンドコードに加えて、代表ウマ娘 + 親(継承元)1 + 親(継承元)2 の情報となっています。
代表ウマ娘, 親(継承元)1, 親(継承元)2 に関しては登録するデータの構造はほぼ同じになっているため、当初はサブコレクションを利用した設計を考えていました。
サブコレクションは、親になっているドキュメントを取得した際に一緒に取得できるわけではなく、親のドキュメントとは別に取得する必要があります。
そのため1つの募集を表示するために、募集ドキュメント + 代表ウマ娘ドキュメント + 親(継承元)1ドキュメント + 親(継承元)2ドキュメント = 4回ドキュメントを取得することになります。
Firestore は取得するドキュメントの数で課金されるため、1つの募集を表示するために4回ドキュメントの課金が発生します。

Cloud Firestore の課金について | Firebase
検索について
サブコレクションの検索
一覧のコストを考えサブコレクションでデータを持たず、募集ドキュメントでデータを持っていますが、検索面の理由もあり募集ドキュメントでデータを持つようにしています。
例えば代表ウマ娘が「スピード」因子を持つ募集を検索する場合、サブコレクションでデータを持っていると
①ウマ娘サブコレクションから代表ウマ娘が「スピード」因子を持つ、代表ウマ娘ドキュメントを取得
parent を利用して、親ドキュメントの documentId を取得
②親ドキュメントを documentId を利用して取得する
③とったフローで検索をすることになります。

CollectionReference | JavaScript SDK | Firebase

FieldPath | JavaScript SDK | Firebase
表示と同じくドキュメント取得数が多くなるため、募集ドキュメントに検索に利用するデータをもたせています。
OR 検索の AND 検索
Firestore では (A or B or C) and (D or E or F) といった where-in の AND 検索はできません。
データのクエリとフィルタ | Firestore | Google Cloud
ウマ娘フレンド募集掲示板では、A or B or C で100件取得、D or E or F で100件取得し、重複を抽出をするようなロジックを組んで検索を実現しています。
ページング
ウマ娘フレンド募集掲示板では SNS の一覧のように、続きを読み込むというページング機能を実装しています(指定したページを表示する機能はありません)

クエリカーソルを使用したデータのページ設定 | Firebase
シンプルな AND 検索と、OR 検索の AND 検索で実装方法を変えているのでそれぞれ紹介します。
AND 検索
AND 検索の場合は公式の例のようにシンプルな実装になっています。
startAt, startAfterを利用することでクエリの開始点を指定することができるため、前回の末尾のスナップショットを保持して利用することで実現しています。
OR 検索の AND 検索
前述したように、 (A or B or C) and (D or E or F) といった where-in の AND 検索はできないため、A or B or C で100件取得、D or E or F で100件取得し、重複を抽出をするようなロジックを組んで検索を実現しています。
AND 検索のページングのように1回のクエリで取得できないため、各クエリ毎にスナップショットを保持する設計で実現しています。
また、取得後の結果で重複を判定するため、過去に取得したデータも保持してします(下記サンプルでは細かいところは省略しています)
終わりに
今回の知見をまとめると
- ドキュメント毎の課金なので、一覧を実装する際に可能な限り一覧のドキュメントでデータを持つ
- (A or B or C) and (D or E or F) といった where-in の AND 検索はできない
- シンプルなページングであれば簡単に実現ができる
上記3点がポイントとなります。
(A or B or C) and (D or E or F) といった where-in の AND 検索ができないのは、多くの人が詰まるポイントかなと今回感じました。
これからもチャレンジしていくので、よろしくおねがいします!
☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆
現在、GameWithでは「ゲームをより楽しめる世界を創る」というMissionの下、そんな世界を実現するべく仲間を募集しております。記事をご覧頂き、少しでもご興味を持って頂けましたら嬉しい限りです。