web-dev-qa-db-ja.com

子の子に値が含まれている場合のFirebaseクエリ

テーブルの構造は次のとおりです。

  • チャット
  • -> randomId
  • ->->参加者
  • ->->-> 0: 'name1'
  • ->->-> 1: 'name2'
  • ->-> chatItems

私がやろうとしているのは、チャットテーブルをクエリして、渡されたユーザー名文字列で参加者を保持しているすべてのチャットを見つけることです。

ここに私がこれまでに持っているものがあります:

 subscribeChats(username: string) {
    return this.af.database.list('chats', {
        query: {
            orderByChild: 'participants',
            equalTo: username, // How to check if participants contain username
        }
    });
 }
51
John

現在のデータ構造は、特定のチャットの参加者を検索するのに最適です。ただし、逆を検索するにはあまり良い構造ではありません。ユーザーが参加しているチャットです。

ここにいくつかの問題:

  • setを配列として保存しています
  • 固定パスでのみインデックスを作成できます

セットと配列

チャットには複数の参加者がいる可能性があるため、これを配列としてモデル化しました。しかし、これは実際には理想的なデータ構造ではありません。おそらく、各参加者は一度だけチャットに参加できます。しかし、配列を使用することで、次のことが可能になります。

_participants: ["puf", "puf"]
_

それは明らかにあなたが念頭に置いているものではありませんが、データ構造はそれを可能にします。コードとセキュリティルールでこれをセキュリティで保護することはできますが、暗黙的にモデルによく一致するデータ構造から始める方が簡単です。

私の経験則:array.contains()を書いていることに気付いたら、setを使用する必要があります。

セットは、各子が1回しか存在できない構造であるため、重複から自然に保護されます。 Firebaseでは、セットを次のようにモデル化します。

_participants: {
  "puf": true
}
_

ここのtrueは実際には単なるダミー値です。重要なことは、名前をキーに移動したことです。もう一度このチャットに参加しようとすると、何もしません。

_participants: {
  "puf": true
}
_

そして、あなたが参加するとき:

_participants: {
  "john": true,
  "puf": true
}
_

これは、要件を最も直接的に表現したものです。各参加者を1回しか含めることができないコレクションです。

既知のプロパティのみにインデックスを付けることができます

上記の構造では、あなたがいるチャットをcouldクエリできます:

_ref.child("chats").orderByChild("participants/john").equalTo(true)
_

問題は、「participants/john」でインデックスを定義するよりもこれが必要なことです。

_{
  "rules": {
    "chats": {
      "$chatid": {
        "participants": {
          ".indexOn": ["john", "puf"]
        }
      }
    }
  }
}
_

これはうまく機能し、うまく機能します。ただし、チャットアプリに新しい誰かが参加するたびに、別のインデックスを追加する必要があります。これは明らかにスケーラブルなモデルではありません。必要なクエリを許可するには、データ構造を変更する必要があります。

インデックスを反転します-カテゴリを引き上げ、ツリーを平坦化します

2番目の経験則:データをモデル化し、アプリに表示する内容を反映します

ユーザーのチャットルームのリストを表示するため、各ユーザーのチャットルームを保存します。

_userChatrooms: {
  john: {
    chatRoom1: true,
    chatRoom2: true
  },
  puf: {
    chatRoom1: true,
    chatRoom3: true
  }
}
_

これで、チャットルームのリストを次のように簡単に決定できます。

_ref.child("userChatrooms").child("john")
_

次に、キーをループして各部屋を取得します。

アプリには2つの関連リストがあります。

  • 特定のユーザーのチャットルームのリスト
  • 特定のチャットルームの参加者のリスト

その場合、データベースにも両方のリストがあります。

_chatroomUsers
  chatroom1
    user1: true
    user2: true
  chatroom2
    user1: true
    user3: true
userChatrooms
  user1:
    chatroom1: true
    chatroom2: true
  user2:
    chatroom1: true
  user2:
    chatroom2: true
_

Firebaseはデータのネストを推奨していないため、両方のリストをツリーの最上位にプルしました。

NoSQLソリューションでは、両方のリストを作成することは完全に正常です。上記の例では、userChatroomschatroomsUsersの逆インデックスとして参照します。

クラウドファイヤーストア

これは、Cloud Firestoreがこのタイプのクエリをより適切にサポートするケースの1つです。 _array-contains_演算子を使用すると、配列内の特定の値を持つドキュメントをフィルター処理できますが、arrayRemoveを使用すると、配列をセットとして扱うことができます。詳細については、「 Cloud Firestoreのより良い配列 」を参照してください。

71