1

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?

Bruno Galinari
  • 155
  • 2
  • 12
  • Can you filter it in code? We are having really great success query'ing the group of data we want (the 20 future matches) and then narrow it down to a subset (championship) with an NSPredicate array search. The advantage here is that if the user wants to view view one subset of data (say a different championship) then you don't have to hit the server again, you can just change your NSPredicate search criteria. And, if you are observing a matches node for add/edit/delete your 'master' array of matches will always be up-to-date. – Jay Jun 09 '15 at 17:17
  • We've thought about that, but hit the server would be our first choice, because we are querying the future 20 matches, and when the user filter by championship we wanted the next 20 matches from that championship, not say 3.. – Bruno Galinari Jun 09 '15 at 17:38
  • The only option would be to combine the values you want to query into a single property. E.g. `"time_and_champ": "1433962855_ libertadores"`. It won't be possible everywhere, but where it is possible it is probably your best bet. Also see http://stackoverflow.com/questions/30654872/how-to-query-based-on-multiple-conditions-in-firebase/30655736#30655736 – Frank van Puffelen Jun 09 '15 at 19:01
  • That wouldn't work at all. I couldn't get future matches this way, or all matches from a championship. Only matches from a specific championship that starts on a very specific time, which doesn't help me. I'm new to firebase and finding it a bit too much inflexible, but realtime is worth it. I'll stick to code filtering anyway. Thnx guys!! – Bruno Galinari Jun 10 '15 at 00:30

2 Answers2

3

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
        })
    })
Bruno Galinari
  • 155
  • 2
  • 12
0

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

Henrik Hartz
  • 3,677
  • 1
  • 27
  • 28