Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Query multiple keys on Firebase

I'm following Firebase's recommendation of flattening data, but I'm having trouble listing a series of items from my database.

Here's a sample of my database file:

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

I'm retrieving the list of albums for a given user:

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

Now I wish I could retrieve all the user's albums in one single query. I could do a batch of queries, and populate an asynchronous array, but that doesn't seem like a good approach to me...

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

Using that approach makes it tricky to detect when the queries have finished, due to the asynchronous nature of the requests. Add to that the fact that some of the requests might fail, due to a bad connection.

Also, if I want to filter one of the albums with a given name (e.g. "My Artwork") or return nil if it doesn't exist, I also end up with a tricky end condition.

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)
}

I have a good background on iOS and Swift, but I'm knew to Firebase and NoSQL databases. Can someone point me a good direction? Should I ditch Firebase and try something else? Am I missing some method that can query what I need? Is my json structure wrong and missing some extra keys?

Thanks

like image 900
Gui Moura Avatar asked Jun 09 '16 03:06

Gui Moura


People also ask

What is getKey () in firebase?

public String getKey ()The key name for the source location of this snapshot or null if this snapshot points to the database root.

Can we use SQL queries in firebase?

FireSQL is a library built on top of the official Firebase SDK that allows you to query Cloud Firestore using SQL syntax. It's smart enough to issue the minimum amount of queries necessary to the Firestore servers in order to get the data that you request.

Is there primary key in firebase?

This chat application allows users to store the basic profile and contact list. The user profile would be located on a path such as Users/$uid. User is a node in it and will have a sort of primary key associated with an ID.

What is DataSnapshot in firebase?

A DataSnapshot is an efficiently-generated immutable copy of the data at a Firebase Location. They cannot be modified and will never change.


1 Answers

I would suggest using a DispatchGroup and mutual exclusion to handle asynchronous functions within a for loop. Here is the code you provided with a DispatchGroup to ensure that all of the asynchronous functions in the loop have completed before it checks the if statement:

let myGroup = DispatchGroup()
var found = false
// iterate through your array
for key in albumKeys {
    let album = database.child(self.albumsKey).child(key)
    // lock the group 
    myGroup.enter()
    album.observeSingleEventOfType(.Value, withBlock: { snapshot in
        if current.name == "My Artwork" {
             found = true
        }
        // after the async work has been completed, unlock the group
        myGroup.leave()
    })
}
// This block will be called after the final myGroup.leave() of the looped async functions complete
myGroup.notify(queue: .main) {
    if !found {
        completion(nil)
    }
}

Anything contained in the myGroup.notify(queue: .main) { codeblock will not execute until myGroup.enter() and myGroup.leave() have been called the same amount of times. Be sure to call myGroup.leave() within the Firebase observe block (after the async work) and make sure that it is called even if an error is produced from the observe.

like image 191
hackerman58888 Avatar answered Nov 10 '22 03:11

hackerman58888