2016-06-09 34 views
10

Sto seguendo la raccomandazione di Firebase di appiattire i dati, ma ho difficoltà ad elencare una serie di elementi dal mio database.Query chiavi multiple su Firebase

Ecco un esempio del mio file di database:

"users" : { 
    "UID12349USER" : { 
     "firstName" : "Jon", 
     "lastName" : "Snow", 
     "email" : "[email protected]", 
     "albums" : { 
     "UID124ALBUM" : true, 
     "UID125ALBUM" : true 
     } 
    } 
}, 
"albums" : { 
    "UID124ALBUM" : { 
     "name" : "My Artwork", 
    }, 
    "UID125ALBUM" : { 
     "name" : "My Sketches", 
    } 
} 

sto recuperando l'elenco degli album per un determinato utente:

let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey) 
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in 
    // fetch [UID124ALBUM: 1, UID125ALBUM: 1] 
}) 

Ora Vorrei poter recuperare tutti gli album degli utenti in una sola query. ho potuto fare una partita di query, e compilare una matrice asincrona, ma che non sembra un buon approccio per me ...

for key in albumKeys { 
    let album = database.child(self.albumsKey).child(key) 
    album.observeSingleEventOfType(.Value, withBlock: { snapshot in 
     // fetch album.. append to array 
    }) 
} 

Utilizzando questo approccio rende difficile da rilevare quando le query hanno finito , a causa della natura asincrona delle richieste. Aggiungete a ciò il fatto che alcune richieste potrebbero non riuscire, a causa di una cattiva connessione.

Inoltre, se voglio filtrare uno degli album con un nome specifico (ad esempio "My Artwork") o restituire nil se non esiste, ho anche una fine difficile.

var found = false 

for key in albumKeys { 
    let album = database.child(self.albumsKey).child(key) 
    album.observeSingleEventOfType(.Value, withBlock: { snapshot in 
     // if current.name == "My Artwork" 
     // completion(current) 
    }) 
} 
// This block will be called before observeSingleEventOfType =.= 
if !found { 
    completion(nil) 
} 

Ho un buon background su iOS e Swift, ma sono a conoscenza dei database Firebase e NoSQL. Qualcuno può indicarmi una buona direzione? Devo buttare Firebase e provare qualcos'altro? Mi manca qualche metodo in grado di interrogare ciò di cui ho bisogno? La mia struttura JSON è sbagliata e mancano alcune chiavi extra?

Grazie

+3

"Aggiungete a ciò il fatto che alcune delle richieste potrebbe fallire, a causa di una cattiva connessione" tutte le richieste di andare oltre la stessa connessione, che viene mantenuto automaticamente dal Firebase SDK. Per ulteriori informazioni, consultare http: // StackOverflow.it/questions/35931526/speed-up-fetching-posts-per-my-social-network-app-di-utilizzo-query-invece-di-obse/35932786 # 35932786 –

+0

Questo è bello, almeno una cosa non è una preoccupazione. Tuttavia, non cambia il fatto che ci deve essere un modo migliore per filtrare i dati. Dato l'esempio di Firebase per le chat di gruppo, nelle Guide, in cui un utente può far parte di più chat room e una chat room può avere più membri. Come si ottiene (elenca) tutte le chat room per un dato utente, dal momento che tutto ciò che si ha nell'oggetto utente è un elenco di id della chat? Grazie –

+0

Normalmente manterrai gli indici in entrambi i gruppi e utenti. Quindi per ogni gruppo, si mantiene un elenco degli utenti in quel gruppo. E per ogni utente, tieni un elenco dei gruppi in cui si trovano. Vedi https://firebase.google.com/docs/database/ios/structure-data#fanout –

risposta

0

Nel tuo caso, l'unico modo possibile intorno è quello di chiamare un altro ascoltatore all'interno di un ascoltatore. In questo modo, non è necessario gestire separatamente la natura asincrona delle richieste.

Per esempio, nel tuo caso: -

let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey) 

userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in 
if(snapshot.exists()) { 
     // fetch [UID124ALBUM: 1, UID125ALBUM: 1] in the albumKeys 
     for key in albumKeys { 
      let album = database.child(self.albumsKey).child(key) 
      album.observeSingleEventOfType(.Value, withBlock: { snapshot in 
        // fetch album.. append to array 
     }) 
     } 
} 
}) 

Ora, per filtrare uno dei tuoi album, è possibile utilizzare il completamento come questo all'interno di una funzione:

var found = false 
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey) 

userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in 
    if(snapshot.exists()) { 
     // fetch [UID124ALBUM: 1, UID125ALBUM: 1] in the albumKeys 
     for key in albumKeys { 
      let album = database.child(self.albumsKey).child(key) 
      album.observeSingleEventOfType(.Value, withBlock: { snapshot in 
        // if current.name == "My Artwork" 
        found = true 
        completion(current) 
     }) 
     } 
    } 
}) 
if !found { 
    completion(nil) 
} 
+0

Sì, era quello che ho finito, comunque, e alla fine ho avuto risposte lente dal database di Firebase, quindi mi sto allontanando da esso. Meglio via con le alternative –

+0

Beh, secondo me, se vuoi farlo funzionare con un singolo listener, potresti usare la ridondanza vs la velocità tr adeoff. Come in, è possibile copiare i dati dell'album nell'oggetto utente. Le tue domande non sarebbero lente in questo modo. –

0

Sono nuovo di sia iOS che Firebase, quindi prendi la mia soluzione con un pizzico di sale. È una soluzione alternativa e potrebbe non essere a tenuta stagna.

Se ho capito correttamente la tua domanda, mi trovo di fronte a un problema simile. Hai "utenti" e "album". Ho "utenti" e "globalWalls". All'interno di "utenti" ho "wallsBuiltByUser" che contiene le chiavi di globalWalls.

Volevo eseguire il loop through wallsBuiltByUser e recuperare il loro nodo corrispondente da globalWalls. Alla fine, devo chiamare il mio delegato per notificare che i muri sono stati recuperati.

Credo che questo potrebbe essere simile a quello che stai cercando di fare.

Ecco la mia soluzione:

 databaseRef.child("users/\(userID)/WallsBuiltByUser/").observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot:FIRDataSnapshot) in 


     let numOfWalls = Int(snapshot.childrenCount) 
     var wallNum:Int = 0 

     for child in snapshot.children { 

      let wallId = (child as! FIRDataSnapshot).key 

      self.databaseRef.child("globalWalls").child(wallId).observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in 

       wallNum = wallNum + 1 
       let wallServerInfo = snapshot.value as? NSDictionary! 

       if (wallServerInfo != nil){ 
        let wall = Wall(wallInfo: wallServerInfo) 
        self.returnedWalls.append(wall) 
       } 

       if(wallNum == numOfWalls){ 
        print("this should be printed last") 
        self.delegate?.retrieved(walls: self.returnedWalls) 
       } 
      })//end of query of globalWalls 


     }//end of for loop 


    })//end of query for user's walls