0

I've been referring to this SO post for a few days now: Filtering results with Geofire + Firebase

My issue is that for my iOS app I need to make a single list of nearby users that is ordered by credentials, for example: premium member (highest and at top of list), donator (next highest after premium), member (basic/lowest).

I've created 3 entries in my Firebase server for GeoFire locations that split users based on these credentials, and therefore need to run 3 queries to retrieve them.

GeoFire* geoFirePremium = [[GeoFire alloc] initWithFirebaseRef:[[[FIRDatabase database] reference] child:@"geofire-premium-members"]];
GeoFire* geoFireDonator = [[GeoFire alloc] initWithFirebaseRef:[[[FIRDatabase database] reference] child:@"geofire-donator-members"]];
GeoFire* geoFireRegular = [[GeoFire alloc] initWithFirebaseRef:[[[FIRDatabase database] reference] child:@"geofire-regular-members"]];
NSMutableDictionary* query1Items = [[NSMutableDictionary alloc] init];
NSMutableDictionary* query2Items = [[NSMutableDictionary alloc] init];
NSMutableDictionary* query3Items = [[NSMutableDictionary alloc] init];

CLLocation* coord = [[CLLocation alloc] initWithLatitude:34.2499 longitude:-85.4399]; // Test location
long searchDistance = 8;
float mile2Kilo = 1.60934;
float kilo2mile = 0.62137;
GFCircleQuery* query1 = [geoFirePremium queryAtLocation:coord withRadius:(CGFloat)(searchDistance * mile2Kilo)]; // Miles to Kilometers
[query1 observeEventType:GFEventTypeKeyEntered withBlock:^(NSString* key, CLLocation* location)
{
    // Store results in query1Items
}];
GFCircleQuery* query2 = [geoFireDonator queryAtLocation:coord withRadius:(CGFloat)(searchDistance * mile2Kilo)];
[query2 observeEventType:GFEventTypeKeyEntered withBlock:^(NSString* key, CLLocation* location)
{
    // Store results in query2Items
}];
GFCircleQuery* query3 = [geoFireRegular queryAtLocation:coord withRadius:(CGFloat)(searchDistance * mile2Kilo)];
[query3 observeEventType:GFEventTypeKeyEntered withBlock:^(NSString* key, CLLocation* location)
{
    // Store results in query3Items
}];

My thoughts are to add some code that recognizes when all 3 queries complete and then merges them into 1 list.

NSMutableDictionary* mergedItems = [[NSMutableDictionary alloc] init];
// For example:  { query1Items[], query2Items[], query3Items[], ... }

[query1 observeReadyWithBlock:^{
    NSLog(@"Query 1 is finished");
    // Check for queries 2 & 3 completion
    // Perform merge if all are completed
}];
[query2 observeReadyWithBlock:^{
    NSLog(@"Query 2 is finished");
    // Check for queries 1 & 3 completion
    // Perform merge if all are completed
}];
[query3 observeReadyWithBlock:^{
    NSLog(@"Query 3 is finished");
    // Check for queries 1 & 2 completion
    // Perform merge if all are completed
}];

Where the JSON structure for all Firebase/GeoFire references follows:

- geofire-premium-members
    - userid
        - g: geohash
        - l
            - 0: lat
            - 1: lon

- geofire-donator-members   //same format

- geofire-regular-members   //same format

- users
    - userid
        - …

Is it a good approach to use multiple queries like this? It is possible I may need to add more credentials in the future and do not know if my approach will scale well. Is there perhaps a better way to achieve what I need that maybe only uses a single query instead? I greatly appreciate any insight

Community
  • 1
  • 1
codeflow
  • 25
  • 3
  • Most of us find it easier to parse code than to parse words describing that code. If you have concerns about the structure of your current code, share the minimal code that reproduces those concerns. If you then also add a snippet of the JSON structure (as text, no screenshots please), you're much more likely to get a useful answer. – Frank van Puffelen Jan 18 '17 at 15:13
  • @FrankvanPuffelen thank you, I have updated with relevant code – codeflow Jan 18 '17 at 17:00

1 Answers1

0

There's nothing wrong with your concept. It may be over complex though as there are three queries to return the same type of data and combining the data in code and doing additional queries to get other user data.

i.e. The three queries in the code return a bunch of user id's to which you then need to do more queries to (for example) look up the individual users names

As long as all of the members in the users node have the same structure you could just put them in one node and use a child property to indicate what kind of user they are

userid_0
    name: "some name"
    user_type: "regular"
userid_1
    name: "another name"
    user_type: "donator"

Then the GeoFire query to return all users within the specified radius.

Assuming the users have to be looked up for their name (as an example) anyway, you will have the rest of the data in the user node and can simply read them in, sort by user_type (in code) and display.

That being said, if you know for sure how you want them sorted, then a simple change to the structure will allow for that easily.

userid_0
    name: "some premium name"
    user_type: 0
userid_1
    name: "a donator name"
    user_type: 1
user_id_2
    name: "regular name"
    user_type: 2

where 0 = a premium user, 1 = a donator user and 2 is a regular user.

Then when sorting in code, sort by user_type and the premiums will float to the top.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • I see where this type of structure is useful, but I will have, in most cases, at least 1000 results returned. I would like to avoid client-side sorting at all costs. Multiple queries seems to get around that issue – codeflow Jan 18 '17 at 21:05
  • @codeflow 1000 results would be a small amount of data so no big deal there. Also, sorting 1000 results won't take much processing power - that being said, why sort in code? Firebase can sort for you in a query when it retrieves the data if you would prefer. What do you want to sort by? also, you can easily perform multiple queries on this structure as well if you want to go that direction. The idea is that it's denormalized, expandable and maintainable, and can be approached easily in code. – Jay Jan 19 '17 at 13:20
  • Thanks @Jay I was unaware that I could sort a GeoFire query using Firebase? I have not really seen how to do this. And as of right now it has not been easily approachable in the code, even though my data is fairly denormalized I'd say – codeflow Jan 21 '17 at 01:30
  • @codeflow GeoFire is built on Firebase but maintains it's own magical dataset so it's queries are based on a specific structure so it's 'different' than a Firebase query. More data was added to your question (thanks) and I mis-read it to start with so changed my answer to better address it. – Jay Jan 21 '17 at 16:27
  • Thanks for the suggestions. Still have some work arounds to try since I've recently realized that one of my node structures will actually need to be different from the other 2. I have some sense of direction now at least – codeflow Jan 22 '17 at 19:06