概要
firestoreからデータを取得するときに、CollectionReference
とかQuerySnapshot
とかいろんなクラスやインターフェースが出てきすぎて悲しみに暮れたのでまとめました。記事中ではjavascriptクライアントを例に取っていますが、他の言語でもそんなに話は変わらないのではないかと思います。
登場するクラスやインターフェース
普通にfirestoreを使ってるとよく使うことになるであろう以下のクラスやインターフェースについて役割や使い方を確認していきます。その他のクラスやインターフェースについては リファレンス を見ると良いでしょう。
CollectionReference
Query
DocumentReference
QuerySnapshot
QueryDocumentSnapshot
DocumentSnapshot
説明に使用する例
公式ドキュメント に出てくる都市のデータベースを例として使います。この記事では、以下のクエリで作成されるcities
というコレクションがあるものとして進めます。
var citiesRef = db.collection("cities");
citiesRef.doc("SF").set({
name: "San Francisco", state: "CA", country: "USA",
capital: false, population: 860000 });
citiesRef.doc("LA").set({
name: "Los Angeles", state: "CA", country: "USA",
capital: false, population: 3900000 });
citiesRef.doc("DC").set({
name: "Washington, D.C.", state: null, country: "USA",
capital: true, population: 680000 });
citiesRef.doc("TOK").set({
name: "Tokyo", state: null, country: "Japan",
capital: true, population: 9000000 });
citiesRef.doc("BJ").set({
name: "Beijing", state: null, country: "China",
capital: true, population: 21500000 });
firestoreからのデータ取得の流れ
具体的な話の前に、firestoreからデータを取得するときに、クライアント側でどういう処理をするかの全体像を確認しておきます。
- データへの参照を作成する(例えば、
db.collection('cities')
やdb.collection('cities').doc('TOK')
) - 参照からスナップショットを得る(
get()
メソッド) - スナップショットからデータを得る(
data()
メソッド)
ここで、1の参照の作成と、3のスナップショットからデータへの変換は同期的で、2の参照からスナップショットへの変換だけが非同期(Promise
が返ってくる)ことを覚えておくと良いでしょう。
それでは、"参照"と"スナップショット"について、具体的に以下で見ていきます。
参照(CollectionReference
、Query
、DocumentReference
)
CollectionReference
、Query
、DocumentReference
はいずれもデータへの参照です。このうち、CollectionReference
とQuery
はほとんど同じもので、いずれも複数のドキュメントへの参照です。DocumentReference
は一つのドキュメントへの参照です。
CollectionReference
CollectionReference
はコレクションやサブコレクションへの参照で、以下のように作成できます。
const citiesRef = db.collection('cities')
CollectionReference
を使って、コレクションにドキュメントを追加することができます。
citiesRef.add(data)
また、条件を絞り込んだり、ドキュメントのidを指定することで、後述するQuery
やDocumentReference
を得ることができます。
const capitalsRef = citiesRef.where('capital', '==', true) // Query型
const SFRef = citiesRef.doc('SF') // DocumentReference型
Query
コレクションの中で、特定の条件を満たすドキュメントだけを取り出したいことや、一定の数のドキュメントを取り出したいことがあります。Query
は、その絞り込んだ結果への参照で、例えば以下のように生成します。
// capitalプロパティがtrueなドキュメントの集合への参照
const capitalsRef = citiesRef.where('capital', '==', true)
// ドキュメントの集合3つのみへの参照
const threeCitiesRef = citiesRef.limit(3)
// 人口で昇順にソートされたcitiesへの参照
const citiesSortedByPopulationRef = citiesRef.orderBy('population')
Query
の使い方は、CollectionReference
とほとんど同じです(ドキュメントによるとCollectionReference
はQuery
のサブクラスのようです)。そのため、例えばCollectionReference
から絞り込んでQuery
を生成したように、Query
型をさらに絞り込んでQuery
を生成することができます。以下の例では、citiesSortedByPopulationRef
というQuery
を絞り込んで、threeLargestCitiesRef
という別のQuery
を取得しています。
// 人口が多い順にソートしたcitiesへの参照
const citiesSortedByPopulationRef = citiesRef.orderBy('population', 'desc')
// もっとも人口が多い3つの都市への参照
const threeLargestCitiesRef = citiesSortedByPopulationRef.limit(3)
DocumentReference
ある一つのドキュメントの参照がDocumentReference
です。ドキュメントが所属するコレクションを参照するCollectionReference
のdoc
メソッドの引数にドキュメントのidを指定することで、以下のように生成できます。
const tokyoRef = citiesRef.doc('TOK')
スナップショット(QuerySnapshot
、QueryDocumentSnapshot
、DocumentSnapshot
)
ここまでは、あくまでデータへの"参照"を取得する話でした。firestoreに保存されているデータの内容を取得するには、"参照"をもとに、実際にデータを持っている"スナップショット"を得る必要があります。"スナップショット"について、以下で説明します。
DocumentSnapshot
DocumentSnapshot
は、DocumentReference
から得られるスナップショットで、単一のドキュメントのデータを持っています。
ここでもう一度、参照からスナップショットを得るための方法について確認しておきます。スナップショットを得るには参照に生えたget()
メソッドを呼べば良いです。get()
の返り値の型はスナップショットを返すPromise
です。例えば、DocumentReference
からDocumentSnapshot
を得るには以下のようにします。
const tokyoRef = citiesRef.doc('TOK')
tokyoRef.get().then(docSnapshot => {
// docSnapshotはidが'TOK'であるドキュメントのデータをもつDocumentSnapshot
// ここでdocSnapshotを使ってなんかやる
})
もちろん、async/await
を使っても良いです。
(async () => {
const tokyoRef = citiesRef.doc('TOK')
const docSnapshot = await tokyoRef.get()
// ここでquerySnapshotを使ってなんかやる
})()
さて、実際にfirestoreに保存されているデータを取得したいときは、上記の方法で生成したDocumentSnapshot
のdata()
メソッドを呼びます。
const tokyoRef = citiesRef.doc('TOK')
tokyoRef.get().then(docSnapshot => {
console.log(docSnapshot.data())
// { capital: true,
// country: 'Japan',
// name: 'Tokyo',
// population: 9000000,
// state: null }
})
また、特定のフィールドの値だけが欲しい時はdata()
の代わりにget(<field名>)
メソッドを呼びます。
const tokyoRef = citiesRef.doc('TOK')
tokyoRef.get().then(docSnapshot => {
console.log(docSnapshot.get('country'))
// Japan
})
QuerySnapshot
QuerySnapshot
は、CollectionReference
とQuery
から得られるスナップショットです。DocumentSnapshot
が単一のドキュメントのデータを持っていたのに対して、QuerySnapshot
は複数のドキュメントのデータを持つスナップショットです。Query
だけでなくCollectionReference
からもQuerySnapshot
が得られることに注意してください。
QuerySnapshot
は、以下のように使います。CollectionReference
であるcitiesRef
からスナップショットを得ている例です。
citiesRef.get().then(querySnapshot => {
// ここでquerySnapshotを使ってなんかやる
})
Query
からも全く同じようにQuerySnapshot
が得られます。以下の例では、querySnapshot
はcapital
がtrue
である都市のみのデータを持っています。
const capitalsRef = citiesRef.where('capital', '==', true) // capitalsRefはQuery型
capitalsRef.get().then(querySnapshot => {
// ここでquerySnapshotを使ってなんかやる
})
QueryDocumentSnapshot
複数のドキュメントのデータを持つQuerySnapshot
から実際にデータを取得する際には、一つ一つのドキュメントのデータを持つQueryDocumentSnapshot
に対して操作を行います。コレクションから一つ一つドキュメントを取り出して、何らかの操作を行うイメージです。具体的には、主に以下の2つの方法が取られます。
// 1. forEach
citiesRef.get().then(querySnapshot => {
querySnapshot.forEach(queryDocSnapshot => {
// ここでqueryDocSnapshotを使ってなんかやる
})
})
// 2. docs
citiesRef.get().then(querySnapshot => {
const queryDocsSnapshot = querySnapshot.docs
// ここでqueryDocsSnapshotを使ってなんかやる
})
1のqueryDocSnapshot
はQueryDocumentSnapshot
、2のqueryDocsSnapshot
はQueryDocumentSnapshot
の配列になっています。QueryDocumentSnapshot
はほとんどDocumentSnapshot
と同じように使えます。例えば、コレクションを構成するドキュメントのデータの配列が欲しいときには、data()
メソッドを使って以下のようにすれば良いです。
citiesRef.get().then(querySnapshot => {
const cities = querySnapshot.docs.map(doc => doc.data())
})
また、forEach
を使った例で、capital
である都市について出力したい場合は、例えば以下のようになるでしょう。
citiesRef.get().then(querySnapshot => {
querySnapshot.forEach(citySnapshot => {
const city = citySnapshot.data()
if (city.capital) {
console.log(`${city.name} is capital!`)
}
})
})
// Beijing is capital!
// Washington, D.C. is capital!
// Tokyo is capital!
QueryDocumentSnapshot
とDocumentSnapshot
の違いについては明示的に意識しなくても良いと思いますが、例えば Firestore - Difference between DocumentSnapshot and QueryDocumentSnapshot を読むとわかると思います。
まとめ
- firestoreに保存してあるデータを取得する時は、
"参照" --get()--> "スナップショット" --data()--> "データ"
という流れになる - 以下のクラス/インターフェースがある
- 参照
-
CollectionReference
:コレクション(or サブコレクション)への参照 -
Query
:コレクションから絞り込んだ結果への参照 -
DocumentReference
:ドキュメントへの参照
-
- スナップショット
-
QuerySnapshot
:CollectionReference
/Query
から得られる参照 -
QueryDocumentSnapshot
:QuerySnapshot
の構成要素で、単一のドキュメントのデータをもつ -
DocumentSnapshot
:DocumentSnapshot
から得られる参照で、単一のドキュメントのデータをもつ
-
- 参照
- APIリファレンス を読もう