The Query Builder Blog Series: Part 6 – Selecting and Deselecting Fields

This blog series follows the journey of building the new and improved Interactive Google Ads Query Builder tool. Part 5 of this series described how we determine whether or not a field is selectable. In Part 6, we will discuss how we add fields to a Google Ads Query Language (GAQL) string using the SelectionService.

GAQL String State

In order to track which fields have been selected, we need to keep track of the GAQL string’s state, which we can do in an instance variable called selectedFields that has the following interface definition:


interface SelectedFields {
select: string[];
where: Array<{field: string, context: string}>;
orderBy: Array<{field: string, context?: string}>;
limit?: string;
params?: string;
}



The select field holds an array of field names. The where field holds an array of objects, each of which contains a field name and context string. By context, we mean the filtering condition to apply to the field (or operator and operand). For example, if we added the filtering condition, ad_group.id = 1234567890 to the WHERE clause, the field would be ad_group.id, and the context would be = 1234567890. Similarly, the orderBy field holds an array of field names and optional context strings. Here, context is the optional ASC or DESC direction (default ASC). The limit field is the optional string representation of the integer LIMIT. Finally, the optional params field represents the optional string value of the PARAMETERS clause. We don’t need an array here because there is currently only a single option for this clause.

Selecting Fields

Now that we have a data structure in place to track the state of a user’s query, we can implement a method to select a field in any clause.


selectField(field: string, clause: string, context?: string): void {
...
}

If the clause is SELECT, we’ll add the provided field to the select array in selectedFields. When users add a field in the SELECT clause, no context is provided.


If the clause is WHERE, all three parameters are required because a filtering condition containing an operator and operand must be provided as a context string. When users click the checkbox to add a field in the WHERE clause, we’ll open a dialog prompting them to specify the operator, and then an operand once the operator has been set. Depending on the data_type of the field being selected, we’ll pre-populate the list of available operators a user can select, and depending on the operator selected, we’ll show the user a different component to input the operand. Once a user adds the filtering criteria, we’ll join the operator and operand into a single string to create the context string.





If the clause is ORDER BY, context is optional. When a user selects a field in the ORDER BY clause, we’ll call selectField without any context and add the field to the orderBy array of selectedFields without any context. We’ll also present users with radio buttons under the field name to specify an order of ASC or DESC. Upon clicking one of the options, we’ll update the respective field’s entry in the orderBy array to add the order context.




If the clause is LIMIT or PARAMETERS, we’ll update the limit and params entries of selectedFields, respectively, with the provided string as the field parameter. The limit must be a positive integer, the validation for which is performed in the related UI component. The only parameter currently available is include_drafts, and the default value is false. Therefore, we’ll present users with a checkbox in the UI component for PARAMETERS with a single option ‘include_drafts=true’. If the user clicks the checkbox, we’ll pass the string include_drafts=true to selectField as the field.

SELECT Presence

While the logic of selecting fields is straight-forward, there are two rules regarding selectability that we intentionally skipped in part 5. Certain fields must be present in the SELECT clause before they can be inserted into the WHERE or ORDER BY clauses.



Rule #1: with the exception of “core date segments” (segments.date, segments.week, segments.month, segments.quarter, segments.year), all segments and segmenting resources must be present in the SELECT clause before they can be inserted into the WHERE clause.



Rule #2: all segments, segmenting resources, metrics, and attributed resource fields must be present in the SELECT clause before they can be inserted into the ORDER BY clause. In other words, only fields on the resource in the FROM clause can be placed in the ORDER BY clause without first being inserted into the SELECT clause.


In these cases, we’ll present a dialog that enables the user to add the field to the SELECT clause as well as the desired clause in a single step.





Deselecting Fields

In order to deselect fields, we’ll implement a method in the SelectionService called deselectField.


deselectField(field: string, clause: string): void {

}




Deselecting a field is similar to selecting a field. As a safeguard, we’ll first check if the field is selected. Then, if the clause is SELECT, WHERE, or ORDER BY, we’ll remove the deselected field from the respective array entry of selectedFields. In the event the field that is required to be present in SELECT before being added to WHERE or ORDER BY according to the rules described above and is removed from SELECT, we’ll automatically remove the field from SELECT in a single operation. If the clause is LIMIT or PARAMETERS, we’ll update the respective entry of selectedFields to undefined.

Updating the Output

With the selectedFields variable, selectField method, and deselectField method in place, we can now track the state of the query string. In order to keep track of changes throughout the application, we’ll create an Observable in the SelectionService and call next on it each time selectField or deselectField is called. Then, we can subscribe to the observable in any component that needs to be aware of the GAQL query state.

Conclusion

We have now updated the SelectionService to select fields and deselect fields. In this post, we’ve covered:
  • GAQL query and clause structure.
  • Additional detail regarding selectability.
  • Using Observables in Angular.
Hopefully this has deepened your understanding of constructing GAQL queries with the Google Ads API. If you have any questions or need additional help, contact us via the forum or at [email protected]