Firebase のデータベースでファイルの情報を管理!

記事
IT・テクノロジー

Firebase のデータベースでファイルの情報を管理!

Firebase のストレージにブログの投稿を保存してブログのサービスの基本の準備ができました。更に使いやすいサービスにするために Firebase のデータベースの Cloud Firestore のデータベースに投稿の情報を入れておくと、より効率的に投稿の情報が取得できます。


Firebase のストレージに保存したファイルからのデータの取得

Markdown にブログの投稿のメタデータを埋め込んで置くと、そこから投稿の情報が取得できます。この情報を利用すれば、投稿の詳細を表示することが可能になります。

しかし、この方法では情報を取得するには非常に手間がかかります。

主な手順は以下の通りです。

1. Firebase ストレージに保存されているファイル(フォルダ)の一覧を取得
2. ファイル(フォルダ)の一覧を元にファイルの中身を取得
3. ファイルの中身からファイルのメタデータ(ブログの情報)を取得
というステップでブログのデータを取得します。

問題は、実際にブログの投稿を表示しない場合でもファイルの中身を取得する必要があります。要は不要なアクセスが増える事になります。

投稿時に必要なデータを取得しておく

不要なアクセスを最小限にするために、ブログの原稿を投稿する際に Markdown のファイルに埋め込んだメタデータを取得して Firebase のデータベースである Cloud Firestore に情報を保存しておきます。 このようにする事で、ブログの原稿の情報は、Cloud Firestore から取得できるので、毎回投稿の原稿全体の情報を取得する必要がなくなります。

こうすることで、通常の一覧などの表示の際は、Cloud Firestore のアクセスだけで済むことになります。 実際のアクセスも、Cloud Firestore のアクセスの方が高速なので一覧の表示の際に必要な時間が短縮できます。

投稿のコードの例

投稿の後に、コードを追加して、ファイルの中身を見てブログの投稿データのメタデータを取得して、Firebase のデータベースである Cloud Firestore にデータを保存するコードを追加しています。

function fileSelectedEvent(e: React.ChangeEvent<HTMLInputElement>): void {
  if (e.target.files && e.target.files[0]) {
    const file: File = e.target.files[0];
    console.log(file.name);
    uploadFile(folderName, file).then((result: boolean) => {
      if (result) {
        alert(file.name + " is uploaded.");
      } else {
        alert("Upload is failed");
      }
    });
    // Extract meta data from Markdown header
    if (file.name.indexOf(".md") === file.name.length - ".md".length) {
      const reader: FileReader = new FileReader();
      reader.readAsText(file);
      reader.onerror = (e: ProgressEvent<FileReader>) => {
        // Error
      };
      reader.onload = (e: ProgressEvent<FileReader>) => {
        if (e.target && e.target.result) {
          const contents: string = e.target.result.toString();
          const matterProcessed = graymatter(contents);
          const metadata = {
            title: matterProcessed.data.title,
            date: matterProcessed.data.date,
            description: matterProcessed.data.description,
            type: matterProcessed.data.type,
            folder: folderName,
            file: file.name,
          };
          console.log(metadata);
          const fbRef: firebase.firestore.CollectionReference = firebase
            .firestore()
            .collection(CONATANT.FB_COLLECTION_STORAGE)
            .doc(folderName)
            .collection(folderName);
          fbRef
            .where("folder", "==", folderName)
            .where("file", "==", file.name)
            .get()
            .then(
              (
                querySnapshot: firebase.firestore.QuerySnapshot<firebase.firestore.DocumentData>
              ) => {
                console.log(querySnapshot);
                if (querySnapshot.size === 0) {
                  // The file does not exists in firebase cloud firestore
                  fbRef
                    .add(metadata)
                    .then(
                      (
                        doc: firebase.firestore.DocumentReference<firebase.firestore.DocumentData>
                      ) => {
                        // Successful
                        console.log(doc);
                      }
                    )
                    .catch((err: any) => {
                      // Error
                      console.log("Add Error:", err.message);
                    });
                } else if (querySnapshot.size === 1) {
                  // The file exist in firebase cloud firestore
                  const id: string = querySnapshot.docs[0].id;
                  fbRef
                    .doc(id)
                    .set(metadata)
                    .then(() => {
                      // Successful
                      console.log("update successful");
                    })
                    .catch((err: any) => {
                      //Error
                      console.log("Error:", err.message);
                    });
                } else {
                  // Error -- Multiple entries are found
                  console.log("Multiple entries are found.");
                }
              }
            )
            .catch((err: any) => {
              // Error
              console.log(err.message);
            });
        } else if (e.target && e.target.error) {
          // Error
          console.log(e.target.error);
        } else {
          // Error
          console.log("Unexpected error");
        }
      };
    }
  }
}

セキュリティルールの設定が必要になります

Firebase のストレージにファイルを保存する場合と、Firebase のデータベースの Cloud Firestore にブログの投稿のメタデータを保存する場合には Firebase のセキュリティルールが必要になります。

セキュリティルールの設定にはルールの基本方針が必要になります。

ブログの投稿
ブログの投稿は、基本的にサイトの運営者(管理者)が行う事になるので必要な権限は、書き込み(新規作成と内容の更新)、削除の権限が必要になります。

ブログの投稿を表示する場合には、読み込みが必要になるので、一般利用者に読み込みの権限が必要です。ただし、これは、読み込みをどの様に行うかで変わってきます。この辺の詳細は、次回の記事で詳しく説明します。

Cloud Firestore のアクセス
Firebase のデータベースのアクセスも2通りあります。

ブログの投稿の際にメタデータを保存するのは、これもサイトの運営者(管理者)が行う必要があるので、書き込み、データの更新、削除はサイトの運営者(管理者)のみになります。

一方で、データの取得は一覧の表示などの際に必要になるので一般の利用者全体に読み込みの権限を与える必要があります。

まとめ
Firebase を利用したブログのサービスの開発の例を紹介しています。先週までに、基本機能の作り方を紹介してきましたが、今週からは、サービスをより使いやすくするためと、実際の運営に必要な機能の追加を紹介しています。

今回は、ブログの情報(Markdown のメタデータ)を Firebase のデータベースに保存してデータ取得のためのアクセスの効率化を行う方法を紹介してきました。

次回は、Firebase のストレージやデータベースにアクセスする際は、セキュリティルールの設定が必要になりますが、その辺りを紹介していきます。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す