Implement and Optimize Autocomplete with Google Places API
Tags: googlemaps, autocomplete, address, googleplaces
Published: 2020-11-27 11:35:26

The Google Places API is a service that returns places predictions using HTTP requests. 
The service can be used to provide an autocomplete functionality for text-based geographic searches by returning places such as businesses, addresses and points of interest as a user types.
Google provides a Places library through the Maps Javascript API. This blog post focus on the usage of this library.  

## Implementing Google Places Autocomplete 

### Enable the Places API

Before using the Places library in the Maps JavaScript API, you must enable the Places API in the Google Cloud Platform Console.

1. Go to the [Google Cloud Platform Console](https://console.developers.google.com/) .
2. Either create a new or select an existing project.
3. At the top of the page, click on the button **ENABLE APIS AND SERVICES**.
4. Search for **Places API**, then select it from the results list.
5. Select **ENABLE**. When the process finishes, Places API appears in the list of APIs on the Dashboard.

> Don't forget to apply Application restrictions (to limit usage for the dedicated APIs - at least Places API) and API restrictions (to enable the use of this API through specific domain). ([how to do it](https://blog.woosmap.com/registering-and-restricting-google-maps-and-woosmap-platform-api-keys#how-do-i-restrict-my-google-api-key-to-a-specific-domain)) 

### Loading the library

In order to use autocomplete, you must first load the Google Places library using the `libraries` parameter in the bootstrap URL for the Google Maps JavaScript API.
```html
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"></script>
```

### Autocompletion using the Widgets

The Places API offers two types of autocomplete widgets, which you can add via the `Autocomplete` and `SearchBox` classes respectively. 

#### The `Autocomplete` Widget 
```js
autocomplete = new google.maps.places.Autocomplete(input, options);
```
`Autocomplete` adds a text input field on your web page. As the user enters text, autocomplete returns place predictions in the form of a dropdown pick list. When the user selects a place from the list, place details is returned in response to a `getPlace()` request. Each entry in the pick list corresponds to a single place (as defined by the Places API).
You can restrict the search to a particular country and particular place types, as well as setting the bounds.

In the below sample, search for *"Old Coff"* and select the first item in the pick list (*Old Coffee House*).
Results are biased towards with the current map bounds using `autocomplete.bindTo('bounds', map)` but to instruct the Place Autocomplete service to return *only* results within that region, you should set the `strictbounds: true` option.

{% jsfiddle https://jsfiddle.net/woosmap/zsh0d8vx result,js %}

#### The `SearchBox` Widget
```js
searchBox = new google.maps.places.SearchBox(input, options);
```
`SearchBox` adds a text input field to your web page, in much the same way as `Autocomplete`. The main difference lies in the results that appear in the pick list. SearchBox supplies an extended list of predictions, which can include places (as defined by the Places API) plus suggested search terms.
`SearchBox` offers fewer options than Autocomplete for restricting the search. You can only bias the search towards a given `LatLngBounds`.

In the below sample, search for *"Coffee near Lond"* and select the first suggested search term in the pick list.

{% jsfiddle https://jsfiddle.net/woosmap/qcLkw8j3 result,js %}

### Autocompletion using the `AutocompleteService`
```js
service = new google.maps.places.AutocompleteService();
service.getQueryPredictions({ input: 'hotels near Lond' }, function(predictions, status){});
```
You can create an `AutocompleteService` object to retrieve predictions programmatically. Call `getPlacePredictions()` to retrieve matching places, or call `getQueryPredictions()` to retrieve matching places plus suggested search terms. 
`AutocompleteService` does not add any UI controls. Instead, the above methods return an array of prediction objects. Each prediction object contains the text of the prediction, as well as reference information and details of how the result matches the user input.
This is useful if you want more control over the user interface than is offered by the Autocomplete and SearchBox described above.

To get the location and more information about any of the places which are returned (when a user select the place in the pick list for example), you need to send a Place Details request with `getDatails(request, callback)` specifying the `place_id` and wanted `fields` to be returned. 

In the below sample, search for *"Old Coff"* and select the first item in the pick list (*Old Coffee House*).

{% jsfiddle https://jsfiddle.net/woosmap/xs8a06nh result,js %}

## Optimization

Here are different solutions to help you reduce the cost of your Places license.

### Return data for specific fields in Place Details requests
When the Places Autocomplete service returns results from a search, it places them within a predictions array.
Each prediction result contains the following fields (check [here](https://developers.google.com/places/web-service/autocomplete#place_autocomplete_results) for more details):
`description`, `place_id`, `terms`, `types`, `matched_substrings`, `structured_formatting`

By default, when a user selects a place from the pick list, autocomplete returns all of the available data fields for the selected place, and you will be [billed accordingly](https://developers.google.com/maps/billing/gmp-billing#places-details). 
You can customize Place Detail requests to return data for specific fields used in your application and so decrease the cost of your Places API License. These fields are divided into categories: Basic, Contact, and Atmosphere. 

#### Basic

The **Basic** category includes fields to locate the place such as the address components and geometry location. [see `basic` fields](https://developers.google.com/maps/documentation/places/web-service/place-data-fields#basic).

#### Contact

The **Contact** category includes the opening hours and fields to contact the business such as the phone number and website. [see `contact` fields](https://developers.google.com/maps/documentation/places/web-service/place-data-fields#contact).


#### Atmosphere

The **Atmosphere** category includes more specific fields (`price_level`, `rating`, `review`,...). [see `atmosphere` fields](https://developers.google.com/maps/documentation/places/web-service/place-data-fields#atmosphere).


#### How to specify desired data?

For example, to retrieve only `geometry` field in the details response (when user select an item in the pick list), define as below: 

- Places **Autocomplete widget**: use `autocomplete.setFields(['geometry'])`. check the jsFiddle above to see how.
- Places **SearchBox widget**: there is no way to constrain SearchBox requests to only return specific fields.
- Places **AutocompleteService**: you have to use the `PlacesService` to call the `getDetails(resuest, callback)` and specify the fields in your `request` parameter.
```js
let request = {
  placeId: place_id,
  fields: ['geometry']
}
``` 


### Per request vs Session Token

> If your using the Autocomplete Widget you don't need to implement sessions, as the widget handles sessions automatically in the background.

The **Per Request** is the default billing option when you use `AutocompleteService`. Charges are applied per keystroke, that could lead to higher billings.

You should use **Session Tokens** when implementing `AutocompleteService.getPlacePredictions()` to group together autocomplete requests for billing purposes. Session tokens group the query and selection phases of a user autocomplete search into a discrete session for billing purposes. The session begins when the user starts typing a query, and concludes when they select a place. Each session can have multiple queries, followed by one place selection.

The following example shows a request using the sessiontoken parameter:
```js
// Create a new session token.
let sessionToken = new google.maps.places.AutocompleteSessionToken();
// Pass the token to the autocomplete service.
let autocompleteService = new google.maps.places.AutocompleteService();
autocompleteService.getPlacePredictions({
  input: 'Coffee near Lond',
  sessionToken: sessionToken
}, displaySuggestions);
```

### Use the Geocoding API
If your application handles user-typed addresses, the addresses are sometimes ambiguous (incomplete, misspelled, or poorly formatted). You can disambiguate addresses using Autocomplete. Then, use the place IDs to get the place locations.
If you have an exact address (or close to it), however, you can reduce costs by using Geocoding instead of Autocomplete. 
You could for example use the AutocompleteService to get the exact address name and then execute a geocoding request to get the geometry and others desired fields (instead of `AutocompleteService.getDetails()`).
See [Geocoding API best practices](https://developers.google.com/maps/documentation/geocoding/best-practices).


### Use debounce and minChar with AutocompleteService

The debounce function limits the rate at which a function can fire.
It takes at least 2 arguments, the input function to call and a time to wait before call it.
As long as this method continues to be invoked, the function passed as parameter will not be triggered. It will be called only after the debounce function stops being called for an elapsed time (in milliseconds).
```js
function debounce(func, wait, immediate) {
	var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		};
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
};
```

You'll pass the debounce function the function to execute and the fire rate limit in milliseconds.
The following example call the `AutocompleteService.getPlacePredictions()` on `input` event of the dedicated input text only when two key strokes are separated by at least 150ms. 
```js
autocomplete_input.addEventListener('input', debounce(function() {
    let value = this.value;
    autocompleteService.getPlacePredictions({
        input: value
      }, displaySuggestions);
}, 150));
```

Autocomplete and SearchBox widgets do not implement a debounce method. Thus each keystroke calls AutocompleteService. 

In addition to this mechanism, you could combine your call with a `minChar` parameter to only trigger the Autocomplete when the text input reaches at least X characters.
```js
autocomplete_input.addEventListener('input', debounce(function() {
    let value = this.value;
    if (value.length > 2) {
       //call here autocompleteService.getPlacePredictions() 
    }
}, 150));
```

### Autocomplete on Stores

If your main goal to implement Google Places Autocomplete is to help your users find the right store for them, why not combine an autocompletion on your stores information (name and city for example) before possibly falling back to the Google Places Autocomplete.
If you're a Woosmap customer and already have your stores hosted on our own, you could use the [Woosmap Search API](https://developers.woosmap.com/products/search-api/get-started/) to achieve this.

To easily implement such a solution, have a look at [Woosmap MultiSearch](https://developers.woosmap.com/products/multisearch-lib/get-started/). This small JavaScript library is designed to return location suggestions through calling several autocomplete services. It enables you to combine stores search (through Woosmap Search API) and Google Places APIs (Places Autocomplete and Places Details). Moreover, the library implements natively debounce and minchars mechanisms. 

If your users are more likely to search for postal codes or localities, the MultiSearch can also retrieve suggestions from [Woosmap Localities](https://developers.woosmap.com/products/localities/get-started/).

#### How MultiSearch combines autocomplete services?

Autocomplete services are requested in your desired ordered. 
By comparing the user input to the returned results and computing a **string matching score** between these two values, the library can automatically switch to the next autocomplete service and thereby provide suggestions that better suits the needs. 

For more details, check [fallback concept doc](https://developers.woosmap.com/products/multisearch-lib/fallback-concept/).



#### Fallback Example with MultiSearch

The following sample autocomplete for Starbucks coffee shops. If no results are available (insufficient matching score between the field `name` of Starbucks coffee shops and the user input), a Google places Autocomplete is performed.
 
In the below sample, search for *"London"* and select one of the coffee shops suggestion in the pick list. 
If you search for *"Coleman Coffee"*, the autocomplete fall back on Google Places (there is no Starbucks coffee with this name).

{% codesandbox https://codesandbox.io/embed/multisearch-stores-google-places-jrfg5?hidedevtools=1&hidenavigation=1&theme=light&view=preview %}

## Conclusion

To optimize Google Places Autocomplete, we recommend the following guidelines:

- Prefer using the `AutocompleteService` instead of the Widgets to gain fine-grained control over the available optimizations.
- Use the Per Session billing option.
- Gather only required fields when getting details of a place.
- Implement Debounce and minChars mechanisms.
- Use the Geocoding API to retrieve address information instead of `getDetails()` from Autocomplete.
- Alternatively, offer to autocomplete on your stores information if the first use case is to help users find them.
 
The [last example](https://codesandbox.io/embed/multisearch-stores-google-places-jrfg5) implements [Woosmap MultiSearch](https://developers.woosmap.com/products/multisearch-lib/get-started/) with Google `AutocompleteService`, per Session billing, Debounce, and autocomplete on Stores before fall back to Google. It could be a good entry-point for dealing with Google Places Autocomplete.