4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Firebase】Firestoreからランダムでデータを取得するにはどうすればいいの?

Posted at

背景

個人アプリを作っている中でFirestoreからランダムで5個だけデータを取得する実装をしたかったのですが、調査を進めていくとそのような処理はFirebase側では用意されていないようなので、参考資料を元にReact, Firebaseで実装しました。

実装までに躓いたことを主にコードの解説をしていきます。

実装したコード

hooks/useRandomDocument.tsx
import { useEffect, useState } from "react";
import { projectFirestore } from "../firebase/config";
import { ProductDoc } from "../@types/stripe";
import { useCookies } from "react-cookie";

export const useRandomDocument = () => {
  const [documents, setDocuments] = useState<Array<ProductDoc>>([]);
  const [cookies] = useCookies(["random"]);
  const randomIndex = Number(cookies.random);

  useEffect(() => {
    let indexs: Array<string> = [];
    let randomDocument: Array<ProductDoc> = [];

    async function handleAsync() {
      while (randomDocument.length < 5) {
        const queryIndex = String(Math.floor(Math.random() * randomIndex + 1)); // DBの中に格納されている商品数以下の数字をランダムで出力する
        if (!indexs.includes(queryIndex)) {
          indexs = [...indexs, queryIndex];
          const productsRef = await projectFirestore.collection("products");
          const snapshot = await productsRef
            .orderBy("metadata.random")
            .startAt(queryIndex)
            .endAt(queryIndex)
            .get(); // startAtとendAtを同一に指定することでユニークな結果を出力できる
          const data = snapshot.docs.map((doc) => {
            return { ...(doc.data() as ProductDoc), id: doc.id };
          });
          randomDocument = [...randomDocument, ...data];
          if (randomDocument.length === 5) {
            setDocuments(randomDocument);
          }
        }
      }
    }
    handleAsync();
  }, [randomIndex]);

  return { documents };
};

今回の味噌

該当ページに遷移したらuseRandomDocumentを発火させます。

Firestoreからランダムに1個データを取得する処理を5個データがrandomDocumentに溜まるまで繰り返し条件を満たしたらuseStateに入れて処理を終了します。

■あらかじめ取得するデータにユニークな値randomをつける

あらかじめ取得するデータにrandom:<数字>を持たせる必要があります。

この下準備をしておくことで後述するstartAt(queryIndex)endAt(queryIndex)を同じ値で指定して1個のみ取得することができます。

■数値をランダムで生成する

先に述べたようにFirestoreからランダムで値を取得できないのでランダムの処理は下記で再現しています。

const queryIndex = String(Math.floor(Math.random() * randomIndex + 1)); 

■between条件でクエリしてデータを取得する

const snapshot = await productsRef
            .orderBy("metadata.random")
            .startAt(queryIndex)
            .endAt(queryIndex)
            .get();

「あらかじめ取得するデータにつけたrandom」と「ランダムで生成した数値」が合致するデータを取得します。

本来ならstartAt(2月1日)endAt(2月28日)にして1ヶ月の値をまとめてとってくるのがユースケースらしいですが、startAtendAtの引数を同じ値にすることで1個だけデータを取得できます。

特定のデータを1個取得するならcollection().doc()の印象でしたがstartAtendAtの方法もあるのですね。目から鱗でした。

さいごに

ということで、Firestoreから値をランダムで取得する方法について述べてきましたが、(今回の場合は)5回Firestoreへアクセスするのでパフォーマンスは落ちますし、その際のLoadingなどのUI・UX対策は必須になります。

もし他に良い方法があればご教示いただけますと幸いです。

参考URL

4
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?