Consulta de Firebase si hijo de hijo contiene un valor

La estructura de la tabla es:

  • chats
  • -> randomId
  • -> -> participantes
  • -> -> -> 0: ‘nombre1’
  • -> -> -> 1: ‘nombre2’
  • -> -> chatItems

etc

Lo que bash hacer es consultar la tabla de chats para encontrar todos los chats que retienen a un participante mediante una cadena de nombre de usuario pasada.

Esto es lo que tengo hasta ahora:

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

Su estructura de datos actual es excelente para buscar a los participantes de un chat específico. Sin embargo, no es una estructura muy buena para buscar lo inverso: los chats en los que participa un usuario.

Algunos problemas aquí:

  • estás almacenando un conjunto como una matriz
  • solo puedes indexar en rutas fijas

Set vs array

Un chat puede tener múltiples participantes, por lo que se modeló como una matriz. Pero esta no es la estructura de datos ideal. Es probable que cada participante solo pueda estar en el chat una vez. Pero al usar una matriz, podría haber:

 participants: ["puf", "puf"] 

Claramente, eso no es lo que tienes en mente, pero la estructura de datos lo permite. Puede tratar de proteger esto en código y reglas de seguridad, pero sería más fácil si comienza con una estructura de datos que implícitamente concuerda mejor con su modelo.

Mi regla de oro: si te encuentras escribiendo array.contains() , deberías estar usando un conjunto .

Un conjunto es una estructura donde cada niño puede estar presente a lo sumo una vez, por lo que protege de forma natural contra los duplicados. En Firebase modelarías un conjunto como:

 participants: { "puf": true } 

La true aquí es realmente solo un valor ficticio: lo importante es que hemos movido el nombre a la clave. Ahora, si intentara unirme a este chat de nuevo, sería un noop:

 participants: { "puf": true } 

Y cuando te unes:

 participants: { "john": true, "puf": true } 

Esta es la representación más directa de su requerimiento: una colección que solo puede contener a cada participante una vez.

Solo puedes indexar rutas fijas

Con la estructura anterior, puede consultar los chats en los que se encuentra:

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

El problema es que esto requiere que usted defina un índice en `participants / john ‘:

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

Esto funcionará y funcionará bien. Pero ahora, cada vez que alguien nuevo se une a la aplicación de chat, deberá agregar otro índice. Claramente, no es un modelo escalable. Tendremos que cambiar nuestra estructura de datos para permitir la consulta que desee.

Invierta el índice: tire de las categorías hacia arriba, aplanando el árbol

Segunda regla general: modele sus datos para reflejar lo que muestra en su aplicación .

Como desea mostrar una lista de salas de chat para un usuario, almacene las salas de chat para cada usuario:

 userChatrooms: { john: { chatRoom1: true, chatRoom2: true }, puf: { chatRoom1: true, chatRoom3: true } } 

Ahora puede simplemente determinar su lista de salas de chat con:

 ref.child("userChatrooms").child("john") 

Y luego recorra las teclas para obtener cada habitación.

Te gustará tener dos listas relevantes en tu aplicación:

  • la lista de salas de chat para un usuario específico
  • la lista de participantes en una sala de chat específica

En ese caso, también tendrá ambas listas en la base de datos.

 chatroomUsers chatroom1 user1: true user2: true chatroom2 user1: true user3: true userChatrooms user1: chatroom1: true chatroom2: true user2: chatroom1: true user2: chatroom2: true 

Llevé ambas listas al nivel superior del árbol, ya que Firebase recomienda no anidar datos.

Tener ambas listas es completamente normal en las soluciones NoSQL. En el ejemplo anterior nos referiríamos a userChatrooms como el índice invertido de chatroomsUsers .