Firebase: combine filtering with ordering in swift

2.5k views Asked by At

So I have a sports app, which handle matches and championships data. I have two displays using the same data:

  • All future matches
  • All future matches from a certain championship

The data is structured like this

- championships
    - libertadores
        - name: Libertadores da América
- matches
    - 2j6g42kjhg62
        - champ: libertadores
        - time: 1433962855
        - <data> //teams, scores, stadium and so on

Now, showing future matches is easy:

matchesRef.queryOrderedByChild("time").queryStartingAtValue(currentTimestamp)
            .queryLimitedToFirst(20)
            .observeEventType(.ChildAdded, withBlock: { (snapshot) in
                // code
            })

However, because you can't use multiple queryOrderedByChild I'm stuck at combining this with filtering from championships.

I've thought about changing my data structure to support something like /championship/matches/<matchID> but that would give me a hard time showing all future matches.

Not filter is not an option here, so how can I accomplish the desired result?

2

There are 2 answers

0
Bruno Galinari On BEST ANSWER

Got it!

I had already read the documentation before posting the question, but haven't had the click that lead me to the solution.

This section on structuring data holds the answer: https://www.firebase.com/docs/ios/guide/structuring-data.html

Understand: The natural structure would be:

 - champs
    - champID
        - matches
            - matchID
            - <matchData>
        - champName

However, to work with firebase, we need to normalize the data:

- champs
    - champID
        - champName

- matches
    - matchID
        - champID
        - timestamp
        - <matchData>

Which is the structure I started my question with.

However, as championships have matches and matches belongs to championships, we need to cross-reference them both. The documentation linked above tells we can give the key a value of true as we are not interested in the key's value, only that it exists. However, as I needed only future matches, the solution was to put the match timestamp in the value.

- champs
    - champID
        - matches
            - matchID: timestamp
        - champName
- matches
    - matchID
        - champID
        - timestamp
        - <matchdata>

Now the final code:

Non-filtered View:

// Query non-filtered matches Reference
matchesRef.queryOrderedByChild("time").queryStartingAtValue(currentTimestamp)
    .queryLimitedToFirst(20).observeEventType(.ChildAdded, withBlock: { (snapshot) in
            // code
    })

Filtered View:

// Query filtered matches
champsRef.childByAppendingPath(champKey).childByAppendingPath("matches")
    .queryOrderedByValue().queryStartingAtValue(currentTimestamp)
    .queryLimitedToFirst(20)
    .observeEventType(.ChildAdded, withBlock: { (snapshot) in
        let key = snapshot.key
        matchesRef.childByAppendingPath(key).observeSingleEventOfType(.Value, withBlock: { (matchSnapshot) in
            // code
        })
    })
0
Henrik Hartz On

In firebase you can only filter on the queryOrderedBy(child: String) key. In my case I needed to filter on multiple date ranges (each being a section in a UITableView), and within these sort on a separate field. The solution is to use a FUISortedArray, which takes a sortDescriptor (DataSnapshot, DataSnapshot) -> ComparisonResult