Получение данных очень быстро из облачного хранилища Google

Я делаю флаттер-приложение и использую облачный магазин в качестве своей онлайн-базы данных. Одна из функций моего приложения — поиск ближайших пользователей и отображение их профиля текущему пользователю в пользовательском виджете на экране. Как я это делаю, я получаю текущее местоположение пользователя (либо текущее местоположение, либо адрес, сохраненный в базе данных), а затем просматриваю каждого отдельного пользователя в моей коллекции базы данных для пользователей. Я получаю адрес пользователя из сохраненных данных, рассчитываю расстояние с помощью API матрицы расстояний, а затем, если расстояние меньше определенного числа (например, 10000 метров), я создаю виджет профиля, чтобы пользователь отображал его на экране.

Есть 2 проблемы:

1- если количество моих пользователей растет (например, миллион пользователей), просматривая каждую информацию о пользователе и вычисляя расстояние, может потребоваться очень много времени, чтобы получить результаты на экране. Прямо сейчас у меня есть только 20 пользователей для целей тестирования, и когда я ищу ближайших пользователей, результаты отображаются на экране через 30 секунд.

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

Как я могу улучшить эту функцию и сделать ее быстрее?

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

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

final List<UserBoxDesign> listOfBoxes = [];
final FirebaseUser currentUser = await auth.currentUser();
final String currentUserId = currentUser.uid;
if (_userLocationSwitchValue == false) { //use default address of the user
  currentUserAddress = await _databaseManagement.getUserCollectionValues(
      currentUserId, "address");
} else {
  //currentUserAddress = //to do, get device location here.
}
if (_searchValue == SearchValues.Users) {
  final List<String> userIds = await _databaseManagement.getUserIds();
  for (String id in userIds) {
    final String otherUserLocations =
        await _databaseManagement.getUserCollectionValues(id, "address");
    final String distanceMeters = await _findDistanceGoogleMaps(
        currentUserAddress, otherUserLocations);
    if (distanceMeters == "Address can't be calculated" ||
        distanceMeters == "Distance is more than required by user") {
      //if it's not possible to calculate the address then just don't do anything with that.
    } else {
      final double distanceValueInKilometers = (double.parse(
                  distanceMeters) /
              1000)
          .roundToDouble(); 
      final String userProfileImageUrl =
          await _databaseManagement.getUserCollectionValues(id, "image");
      final String username =
          await _databaseManagement.getUserCollectionValues(id, "username");
      listOfBoxes.add(
        UserBoxDesign( //it creates a custom widget for user if user is nearby
          userImageUrl: userProfileImageUrl,
          distanceFromUser: distanceValueInKilometers,
          userId: id,
          username: username,
        ),
      ); //here we store the latest values inside the reserved data so when we create the page again, the value will be the reservedData value which is not empty anymore

    }
    print(listOfBoxes);
  }
  listOfBoxes.sort((itemA,itemB)=>itemA.distanceFromUser.compareTo(itemB.distanceFromUser)); //SORTs the items from closer to more far from user (we can reverse it to far comes first and close goes last)
  setState(() {
    _isSearchingForUser = false;
  });
  return listOfBoxes;

Вот код, в котором я вычисляю расстояние между адресом отправления и адресом назначения.

Future<String> _findDistanceGoogleMaps(
  String originAddress, String destinationAddress) async {
final String url =
    "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=$originAddress&destinations=$destinationAddress&key=$GoogleMapsAPIKey";
try {
  final response = await http.get(url);
  final responseDecoded = json.decode(response.body);

  final distanceInMeters = double.parse(responseDecoded["rows"][0]
          ["elements"][0]["distance"]["value"]
      .toString()); //this is the value in meters always so for km , divide by 1000.
  if (distanceInMeters < 100000) {
    return distanceInMeters.toString();
  } else {
    return "Distance is more than required by user";
  }
} catch (e) {
  return "Address can't be calculated";
}

}

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


person Morez490    schedule 24.11.2019    source источник


Ответы (1)


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

Таким образом, вам не нужен Distance Matrix API (я думаю, что это будет дорого), и ваши запросы будут быстрыми и дешевыми.

Я погуглил и нашел ответ здесь Как выполнить запрос о географическом соседстве с огнем?

==после обновления вопроса==

  1. Вы пытаетесь использовать всю логику внутри экрана и делаете это синхронно. Из-за этого у вас такое долгое время рендеринга. Вы вычисляете все на пользовательском устройстве и переходите к виджету return listOfBoxes; Вместо этого вы можете попробовать использовать Streambuilder, пример здесь Как эффективно получить доступ к данным ссылочного поля firestore во флаттере?

  2. Организуйте данные в своей базе данных таким образом, чтобы вы могли сделать запрос для своих целей: "Find all users within X range sorted by distance AND ...". В этом случае Firebase сделает это очень быстро и асинхронно передаст данные вашему Streambuilder. Я предполагаю, что вы можете сохранить долготу, широту и работать с ними вместо адресов.

Извините, я не могу переписать ваш код, недостаточно информации. Просто посмотрите пример по ссылке, есть хороший пример.

==обновление 2==

Пакет https://pub.dev/packages/geoflutterfire позволяет сохранять геоданные в Firestore и как делать запросы

// Create a geoFirePoint
GeoFirePoint center = geo.point(latitude: 12.960632, longitude: 77.641603);

// get the collection reference or query
var collectionReference = _firestore.collection('locations');

double radius = 50;
String field = 'position';

Stream<List<DocumentSnapshot>> stream = geo.collection(collectionRef: collectionReference)
                                        .within(center: center, radius: radius, field: field);
person awaik    schedule 24.11.2019
comment
Спасибо. Обязательно попробую StreamBuilder. - person Morez490; 24.11.2019
comment
У меня просто есть вопрос, для номера 1, я должен использовать построитель потоков, поэтому всякий раз, когда я вычисляю новое расстояние, я снова вызываю свой виджет, чтобы сделать поля профиля пользователя. А вот пункт 2 не совсем понял. Если я храню Lat и Lng в пользовательском документе, то как мне посчитать расстояние в firebase? А где хранить результат расстояния? Потому что я получаю текущее местоположение пользователя на устройстве, а затем вычисляю расстояние в своем приложении. Итак, не могли бы вы объяснить номер 2 немного больше? Спасибо - person Morez490; 24.11.2019
comment
Привет, для 2. Я думаю, вы можете использовать pub.dev/packages/geoflutterfire, у них есть именно то, что вы нужно - как хранить данные в Firestore и как делать запросы (приведите пример к основному ответу) - person awaik; 25.11.2019