firestoreのwhere句で1時間くらいハマったのでメモ。
TL;DR
firestoreで、全てのcatカテゴリーが登録されてるデータを取得したい場合、カテゴリーObjectにcat: マップ値
のデータを登録するが
new Date()
ではなくDate.parse(new Date())
のように正しくマップ値に変換して登録しないとwhere('categories.cat', '>', 0)
は解釈できない。
そりゃそうだ。
公式Doc
cloud firestore: 配列、リスト、セットの操作
// The value of each entry in 'categories' is a unix timestamp { title: "My great post", categories: { technology: 1502144665, opinion: 1502144665, cats: 1502144665 } }
範囲フィルタを使用して複数のフィールドにクエリを実行するには、複合インデックスが必要です。しかし、上記の手法ではこれが不可能です。インデックスは、特定のフィールドパスに定義する必要があります。したがって、categories.cats や categories.opinion などの可能なフィールドパスごとにインデックスを作成しなければなりませんが、カテゴリがユーザー生成されている場合にはこれを事前に行うことができません。
この制限を回避するには、クエリのすべての情報をマップ値にエンコードします。
有効なクエリ
db.collection('posts') .where('categories.cats', '>', 0) .orderBy('categories.cats'); )
上記のようなfirestore設計、
フィールドの値をマップ値にエンコードすることで、categoriesにcatが存在する全てのドキュメントをorderBy込みで取得可能になります。
ハマった点
タイムスタンプ入れとけばいいのか〜と、そのままnew Date()
入れていたのでwhere(`members.${userId}`, '>', 0)
は解釈できなかった。
new Date()
ではなくDate.parse(new Date())
を保存が正しい。
sample document (firestore)
修正済みコード
// save
export const addSampleCollection = async ({ commit }, { doc }) => {
const now = new Date()
let members = {}
// members[doc.author] = now // [NG] 2018年2月22~~ (タイムスタンプ)
members[doc.author] = Date.parse(now) // [OK] 151923~~~
await docsRef.add({
author: doc.author,
members: members,
release_at: doc.release_at
title: doc.title,
create_at: now,
update_at: now,
}).then(function () {
console.log('[success] addDoc')
}).catch(function (error) {
console.log('[error] addDoc', error)
})
}
// get
export const getSampleCollectionDoc = async ({ commit }, { userId }) => {
let query = await partiesRef.where(`members.${userId}`, '>', 0).get()
.then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
console.log(doc.id, ' => ', doc.data())
})
})
.catch(function (error) {
console.log('Error getting documents: ', error)
})
}
画像のとおり、firestore上のmember.uri_m
に正しく数字が登録されており、where区は正しく働く。