The Query Builder Blog Series: Part 5 – Determining Field Selectability

This blog series follows the journey of building the new and improved Interactive Google Ads Query Builder tool. Part 4 of this series described how we created a ResourceService to display relevant fields given the resource in our FROM clause based on a user’s location in the app. In Part 5, we will discuss how to determine whether a field is selectable or not in a Google Ads Query Language (GAQL) query string.

Background & Objective

The determination of whether a field is selectable, or can be added to a clause of GAQL string, is dependent on both: 1) inherent properties of the main resource in the FROM clause and its fields’ properties as well as 2) the current state of the GAQL query string. The ResourceService we created in Part 4 addresses item (1) because users will only be shown fields that are able to be selected based on those inherent properties.

In order to address item (2), we will create a new service called the SelectionService. The responsibilities of this service will include determining selectability, selecting fields, and deselecting fields. We will discuss determining field selectability in this post, and in Part 6 of this series, we’ll discuss how the SelectionService selects and deselects fields.

Field Compatibility

Not all fields are compatible with each other. When two fields are compatible, both can be present in a GAQL string. If two fields are incompatible, only one of the two fields may be present in any clause of a GAQL string at a time. Therefore, if there is a field in the GAQL string that is incompatible with the field being evaluated, the field being evaluated is not selectable, and the UI will reflect this by presenting users with a corresponding error message.


We will keep track of selected incompatible fields for each field on the main resource with an instance variable called incompatibleSelected that maps each field to a Set of its incompatible fields that have been selected.

interface IncompatibleSelected: {[key: string]: Set<string>}

We’ll initialize this incompatibleSelected such that the keys of the map are the aggregated list of fields on the resource, as determined by the ResourceService, and the value of each entry is an empty Set.

Each time a field is selected, we will add the selected field to the incompatibleSelected Set of each field that is incompatible with the selected field. For example, let’s assume that ad_group is the resource in our FROM clause. segments.ad_destination_type is incompatible with metrics.absolute_top_impression_percentage and metrics.active_view_cpm, among other fields. The table below illustrates the incompatibility relationship among these fields.
Field Incompatible Fields
segments.ad_destination_type [metrics.absolute_top_impression_percentage, metrics.active_view_cpm, …]
metrics.absolute_top_impression_percentage [segments.ad_destination_type, …]
metrics.active_view_cpm [segments.ad_destination_type, …]
If segments.ad_destination_type is selected, we’ll add segments.ad_destination_type to the entries for metrics.absolute_top_impression_percentage and metrics.active_view_cpm in the incompatibleSelected map.

Following the addition of segments.ad_destination_type to any clause in our query, a subset of our incompatibleSelected field would look like this.

incompatibleSelected = {

'segments.ad_destination_type': {},
'metrics.absolute_top_impression_percentage': {'segments.ad_destination_type'}
'metrics.active_view_cpm': {'segments.ad_destination_type'}

Each time a field is deselected, we’ll first check if that field is present in any clause of the query (more detail to be provided in Part 6) because if so, incompatibleSelected should not be updated. For example, if segments.ad_destination_type had been selected in the SELECT and ORDER BY clauses, but was only deselected from ORDER BY, we don’t want to make any adjustments to the incompatibleSelected map because segments.ad_destination_type is still present in the query. However, if the deselected field is absent from all clauses, we’ll delete the deselected field from the Set for each field that is incompatible with the deselected field.

In our example, let’s assume we now deselect segments.ad_destination_type from the SELECT clause, and it is no longer present in the query. Now, we’ll remove this field from the metrics.absolute_top_impression_percentage and metrics.active_view_cpm entries of the incompatibleSelected map. At this point, every entry in the incompatibleSelected map would contain an empty Set.

With this data structure in place, we can create a method called isSelectable that accepts a field name as a parameter. isSelectable returns true if the Set for the field in incompatibleSelected is empty and false if it is not.

  isSelectable(field: string): boolean {
return this.incompatibleSelected[field]?.size === 0;


We have now implemented logic into a SelectionService that determines whether a field is selectable. As we display each field to users, we can simply call isSelectable(field) to determine what to show users in the UI. In this post we’ve covered field compatibility and how it relates to whether or not a field is selectable. In Part 6, we’ll build on this section to see how we can select fields, deselect fields, and keep track of the state of the GAQL query string with the SelectionService.

Hopefully this deepened your understanding how to construct GAQL queries using the Google Ads API. If you have any questions or need additional help, contact us via the forum or at [email protected]