Несколько запросов Geofire и фильтрация на стороне клиента

Я уже несколько дней ссылаюсь на этот пост SO: Фильтрация результатов с помощью Geofire + Firebase< /а>

Моя проблема заключается в том, что для моего приложения iOS мне нужно составить единый список ближайших пользователей, упорядоченный по учетным данным, например: премиум-участник (самый высокий и вверху списка), жертвователь (следующий по величине после премиум-класса), участник (базовый/ самый низкий).

Я создал 3 записи на своем сервере Firebase для местоположений GeoFire, которые разделяют пользователей на основе этих учетных данных, и поэтому для их получения необходимо выполнить 3 запроса.

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
}];

Я думаю добавить некоторый код, который распознает, когда все 3 запроса завершены, а затем объединяет их в 1 список.

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
}];

Где структура JSON для всех ссылок Firebase/GeoFire выглядит следующим образом:

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

- geofire-donator-members   //same format

- geofire-regular-members   //same format

- users
    - userid
        - …

Это хороший подход к использованию нескольких запросов, подобных этому? Возможно, в будущем мне потребуется добавить больше учетных данных, и я не знаю, будет ли мой подход хорошо масштабироваться. Возможно, есть лучший способ добиться того, что мне нужно, который вместо этого может использовать только один запрос? Я очень ценю любое понимание


person codeflow    schedule 18.01.2017    source источник
comment
Большинству из нас легче анализировать код, чем слова, описывающие этот код. Если у вас есть опасения по поводу структуры вашего текущего кода, поделитесь минимальным кодом, который воспроизводит эти опасения. Если вы затем также добавите фрагмент структуры JSON (в виде текста, пожалуйста, без скриншотов), у вас гораздо больше шансов получить полезный ответ.   -  person Frank van Puffelen    schedule 18.01.2017
comment
@FrankvanPuffelen спасибо, я обновил соответствующий код   -  person codeflow    schedule 18.01.2017


Ответы (1)


В вашей концепции нет ничего плохого. Однако это может быть слишком сложным, поскольку есть три запроса для возврата данных одного типа, объединения данных в коде и выполнения дополнительных запросов для получения других пользовательских данных.

т. е. три запроса в коде возвращают набор идентификаторов пользователей, к которым вам затем нужно выполнить дополнительные запросы, чтобы (например) найти имена отдельных пользователей.

Пока все члены в узле пользователей имеют одинаковую структуру, вы можете просто поместить их в один узел и использовать дочернее свойство, чтобы указать, к какому типу пользователей они относятся.

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

Затем запрос GeoFire возвращает всех пользователей в указанном радиусе.

Предполагая, что пользователи все равно должны искать свое имя (в качестве примера), вы будете иметь остальные данные в пользовательском узле и можете просто прочитать их, отсортировать по user_type (в коде) и отобразить.

При этом, если вы точно знаете, как вы хотите их отсортировать, то простое изменение структуры позволит легко это сделать.

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

где 0 = премиум-пользователь, 1 = пользователь-донор и 2 — обычный пользователь.

Затем при сортировке в коде сортируйте по user_type и надбавки будут всплывать наверх.

person Jay    schedule 18.01.2017
comment
Я вижу, где этот тип структуры полезен, но в большинстве случаев я получу не менее 1000 результатов. Я хотел бы любой ценой избежать сортировки на стороне клиента. Несколько запросов, кажется, обходят эту проблему - person codeflow; 19.01.2017
comment
Результаты @codeflow 1000 будут небольшим объемом данных, так что в этом нет ничего страшного. Кроме того, сортировка 1000 результатов не требует больших вычислительных мощностей — при этом зачем сортировать в коде? Firebase может сортировать для вас в запросе, когда он извлекает данные, если вы предпочитаете. По чему вы хотите сортировать? Кроме того, вы можете легко выполнять несколько запросов к этой структуре, если хотите двигаться в этом направлении. Идея состоит в том, что он денормализован, расширяем и удобен в сопровождении, и к нему можно легко обратиться в коде. - person Jay; 19.01.2017
comment
Спасибо, @Jay, я не знал, что могу отсортировать запрос GeoFire с помощью Firebase? Я действительно не видел, как это сделать. И на данный момент это не было легко доступно в коде, хотя мои данные довольно денормализованы, я бы сказал - person codeflow; 21.01.2017
comment
@codeflow GeoFire построен на Firebase, но поддерживает свой собственный волшебный набор данных, поэтому его запросы основаны на определенной структуре, поэтому он «отличается» от запроса Firebase. К вашему вопросу было добавлено больше данных (спасибо), и я неправильно прочитал его для начала, поэтому изменил свой ответ, чтобы лучше решить его. - person Jay; 21.01.2017
comment
Спасибо за предложения. Все еще есть некоторые обходные пути, которые нужно попробовать, так как я недавно понял, что одна из моих структур узлов на самом деле должна отличаться от двух других. По крайней мере, теперь у меня есть некоторое чувство направления. - person codeflow; 22.01.2017