简体   繁体   中英

Google Places Api Batch Requests Javascript

So i have heard that there is no real clean way to batch requests referenced here to the GooglePlacesAPI ; understood.

But there has to be a work around.

const retrievePlaces = (google, map, request) => {
  var places = [];
  var newPlaces = []
  var service = new google.maps.places.PlacesService(map);

  return new Promise(function(resolve, reject) {
    service.nearbySearch(request, function(results, status){

    if( status == "OK" ){
      for (var i = 0; i < results.length; i++) {
          var place = results[i];
          places.push(place);
        }
        resolve(places);
      }
    });
  });
}

I use the above function to retrieve my places first (this works fine). Then I use:

const retrieveDetails = ( google, map, places ) => {

   var gmap = {
     map: map,
     google: google
   };

   var placeIds = places.map(function(place){
     return { placeId: place.place_id }
   });

   var promiseArray = placeIds.map( place => getPlaceDetailsPromise( place, gmap )
                                        .then(  res => ({res}) ) 
                                        .catch( err => ({err}) ) );

   Promise.all(promiseArray)
     .then(results => {
       console.log(results);
   });

 }

and:

const getPlaceDetailsPromise = (obj, gmap) => new Promise((resolve, reject) => {
   var service = new gmap.google.maps.places.PlacesService(gmap.map);

   service.getDetails(obj, (place, status) => {
     if (status === google.maps.places.PlacesServiceStatus.OK) {
       console.log(" Status OK", place);
       resolve(place);   
     } else {
       console.log("Not OK");
     }
  });
});

to attempt to retrieve the details for all the places from the PlaceDetailsAPI . What sucks is that it actually works to a degree, but it always returns only 9 responses and no more. furthermore they are out of order.

Does anyone have any insight on how it might be possible to retrieve the details for each place?

Maps JavaScript API client side services have a per session limits. This is mentioned in the following section of the documentation:

https://developers.google.com/maps/documentation/javascript/geocoding#UsageLimits

Rate limit applied per user session, regardless of how many users share the same project.

The per-session rate limit prevents the use of client-side services for batch requests, such as batch geocoding. For batch requests, use the Google Maps Geocoding API web service.

Unfortunately, documentation doesn't include this notice in the places library part of the documentation, but it works the same.

As far as I know, initially you have a bucket of 10 requests. Once the bucket is empty request is denied. The bucket is refilled at the rate 1 request per second. So, you have to throttle your places details requests in order to stay within allowed per session limits. Alternatively, you can try to implement batch places requests on server side where you will have 50 queries per second (QPS) limit.

Thanks @xomena, and for anybody else that may stumble upon this question there is a way to do it and it involves what @xomena mentioned before. It is a little dirty and probably extremely inefficient but does what i need it to do.

I ended up doing a little ajax/curl inception to solve my issue and to get around the CORS issues i was having as well. I made a server side proxy that takes the formatted google api request uri ( sent via ajax from one of my functions ) and returns the details object.

After that everything worked like a charm. Updated Code is as follows.

const retrievePlaces = (google, map, request) => {
  var places = [],
      newPlaces = [],
      service = new google.maps.places.PlacesService(map);

  return new Promise(function(resolve, reject) {
    service.nearbySearch(request, function(results, status){
      if( status == "OK" ){
        for (var i = 0; i < results.length; i++) {
          var place = results[i];
          places.push(place);
        }
        resolve(places);
      }
     });
  });
}

const retrieveDetails = (google, map, places, api) => {

  var gmap = { map: map, google: google, api_key: api },
      placeDetails = [],
      placeIds = places.map(getPlaceIds),
      promiseArray = placeIds.map(getPlaceDetails);

  function getPlaceIds(place){
    return { placeId: place.place_id }
  }

  function getPlaceDetails(place){
    getPlaceDetailsPromise( place, gmap )
      .then(  res => ({res}) ) 
      .catch( err => ({err}) );
  }

  return Promise.all(promiseArray);

}

const getPlaceDetailsPromise = ( obj, gmap ) => new Promise((resolve, reject) => {

  var url = `https://maps.googleapis.com/maps/api/place/details/json?placeid=${obj.placeId}&key=${gmap.api_key}`,
      qsObj = Qs.parse({ url: url }),
      qsString = Qs.stringify(qsObj),
      headerConfig = { 
        headers: { 
          "Content-Type": "application/x-www-form-urlencoded" 
        } 
      };

  function resolveDetails(res){
    var result = res.data;
    resolve($.parseJSON(result));
  }

  axios.post('/a/random/path', qsString, headerConfig ).then(resolveDetails);

});

And then the proxy in php:

<?php 
 /* proxy.php */
 $url = $_POST["url"];

 $ch = curl_init();
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 $result = curl_exec ($ch);
 curl_close ($ch);

 $result = json_encode($result);

 echo $result;

 die();
?>

Of course this won't work if you plug and played it into your code but the idea is there. If anybody finds a better way I'd love to see how you did it, it would be good shit to know in the future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM