結論:orderByは指定フィールドの存在でもフィルタリングする

FirestoreのorderBy()句は、指定したフィールドが存在しないドキュメントを自動的に除外する。

並べ替えだけでなく、暗黙的にフィルタリングも行うため、予期しないデータ欠落が発生する可能性がある。

問題:orderByで結果が減る

// 「createdAt」フィールドでソート
query := client.Collection("users").OrderBy("createdAt", firestore.Asc)

このクエリでは、createdAtフィールドが存在しないドキュメントは結果に含まれない。

例:

  • {id: 1, name: "Alice", createdAt: "2025-01-01"} → 結果に含まれる
  • {id: 2, name: "Bob"}結果から除外される(createdAtがない)

公式ドキュメントの記載

orderBy() 句は、指定したフィールドの有無によるフィルタも行います。指定したフィールドがないドキュメントは結果セットには含まれません。

Cloud Firestore でデータを並べ替えたり制限する | Firebase

範囲比較との組み合わせ制約

範囲比較(<, <=, >, >=)のフィルタがある場合、最初のorderBy()は同じフィールドで行う必要がある。

// ❌ NG: 範囲比較(age)と異なるフィールド(name)で最初にorderBy
query := client.Collection("users").
    Where("age", ">", 20).
    OrderBy("name", firestore.Asc)
// エラー: 最初のorderByはageにする必要がある

// ✅ OK: 範囲比較と同じフィールド(age)で最初にorderBy
query := client.Collection("users").
    Where("age", ">", 20).
    OrderBy("age", firestore.Asc).
    OrderBy("name", firestore.Asc)

対策:フィールド存在を意識する

1. デフォルト値を設定

// ドキュメント作成時に必ずcreatedAtを設定
user := map[string]interface{}{
    "name":      "Alice",
    "createdAt": time.Now(),
}

2. フィールド存在チェック

// 存在しないフィールドでのソートは避ける
// または、存在チェックのWhere句を追加(複合インデックス必要)

3. 複合インデックスを作成

範囲比較と複数フィールドでのソートを行う場合、複合インデックスが必要。

まとめ

  • orderBy()は並べ替えだけでなく、指定フィールドの存在でもフィルタリングする
  • 指定フィールドが存在しないドキュメントは結果から除外される
  • 範囲比較がある場合、最初のorderBy()は同じフィールドで行う必要がある
  • ドキュメント作成時にソート用フィールドを必ず設定することを推奨

参考