Updates using field masks

  • Google Ads API updates use a field mask to specify the fields being changed, ignoring any others sent to the server.

  • The built-in FieldMaskUtil is recommended for automatically generating field masks by tracking changes to an entity's fields.

  • For complex operations requiring fine control, the client.field_mask.with method is used, while a simpler client.operation.update_resource method is available for most cases.

  • Manually creating a mask involves creating a Google::Protobuf::FieldMask and populating its path field with an array of field names to be changed.

  • When updating message fields with defined subfields or clearing fields, you must explicitly add the subfields or fields to the field mask, as automatic generation may not achieve the desired result.

In the Google Ads API, updates are done using a field mask. The field mask lists all the fields you intend to change with the update, and any specified fields that aren't in the field mask are ignored, even if sent to the server.

FieldMaskUtil

The recommended way to generate field masks is using the built-in field mask utility, which hides specific details and lets you generate field masks automatically by monitoring the changes you make to the entity's fields.

The following example shows how to generate a field mask for updating a campaign:

campaign = client.resource.campaign
campaign.resource_name = client.path.campaign(customer_id, campaign_id)

mask = client.field_mask.with campaign do
  campaign.status = :PAUSED
  campaign.network_settings = client.resource.network_settings do |ns|
    ns.target_search_network = false
  end
end

The code first creates an empty Campaign object, then sets its resource name to inform the API of the campaign being updated.

This example uses the client.field_mask.with method on the campaign to begin the block encompassing the updates. At the end of this block, the utility compares the current state of the campaign after the block with the initial state of the campaign before the block, and automatically produces a field mask enumerating the changed fields. You can provide that field mask to the operation when constructing it for the mutate call as follows:

operation = client.operation.campaign
operation.update = campaign
operation.update_mask = mask

This method is recommended when you're building a complex operation and want fine-grained control over every step. However, for most cases, you can pass the resource name (or an existing resource instance) to the Ruby library factory method:

campaign_resource_name = client.path.campaign(customer_id, campaign_id)

operation =
  client.operation.update_resource.campaign(campaign_resource_name) do |c|
    c.status = :PAUSED
    c.network_settings = client.resource.network_settings do |ns|
      ns.target_search_network = false
    end
  end

When given a resource name string, this method automatically creates a new campaign resource with resource_name populated, constructs the field mask based on changes you make within the block, builds the update operation, and returns the final operation with update and update_mask already populated. You can also pass an existing Campaign proto instance instead of a resource name string to specify the starting state of the campaign. This pattern works for all resources that support the update operation.

Manually create a field mask

To create a field mask from scratch without using library utilities, create a Google::Protobuf::FieldMask, make an array populated with the names of all the fields you intend to change, and assign the array to the field mask's paths field:

mask = Google::Protobuf::FieldMask.new
mask.paths = ['status', 'name']

Update message fields and their subfields

MESSAGE fields can have subfields (such as MaximizeConversions, which has three: target_cpa_micros, cpc_bid_ceiling_micros, and cpc_bid_floor_micros), or they can have none at all (such as ManualCpm).

Message fields with no defined subfields

When updating a MESSAGE field that is not defined with any subfields, use FieldMaskUtil to generate a field mask, as presented earlier.

Message fields with defined subfields

When updating a MESSAGE field that is defined with subfields without explicitly setting any of the subfields on that message, you must manually add each of the mutable MESSAGE subfields to the FieldMask, similar to the earlier example that created a field mask from scratch.

One common example is updating a campaign's bidding strategy without setting any of the fields on the new bidding strategy. The following example demonstrates how to update a campaign to use the MaximizeConversions bidding strategy without setting any of the subfields on the bidding strategy.

For this example, using the built-in comparison of FieldMaskUtil does not achieve the intended goal.

The following code generates a field mask that includes maximize_conversions. However, the Google Ads API doesn't allow this behavior in order to prevent accidentally clearing fields and produces a FieldMaskError.FIELD_HAS_SUBFIELDS error.

# Creates a campaign with the proper resource name.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
end

# Update the maximize conversions field within the update block, so it's
# captured in the field mask.
operation = client.operation.update_resource.campaign(campaign) do |c|
  c.maximize_conversions = client.resource.maximize_conversions
end

# Sends the operation in a mutate request that results in a
# FieldMaskError.FIELD_HAS_SUBFIELDS error because empty MESSAGE fields cannot
# be included in a field mask.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)
# Create the operation directly from the campaign's resource name. Don't do
# anything in the block so that the field mask starts empty. You can modify
# other fields in this block, except the message field intended to have a
# blank subfield.
campaign_resource_name = client.path.campaign(customer_id, campaign_id)
operation = client.operation.update_resource.campaign(campaign_resource_name) {}

# Manually add the maximize conversions subfield to the field mask so the API
# knows to clear it.
operation.update_mask.paths << 'maximize_conversions.target_cpa_micros'

# This operation succeeds.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)

Clear fields

Some fields can be explicitly cleared. Similar to the previous example, you must explicitly add these fields to the field mask. For example, assume you have a campaign that uses a MaximizeConversions bidding strategy and that the target_cpa_micros field is set with a value greater than 0.

In proto3, setting a non-optional scalar field to its default value (0) is indistinguishable from leaving it unset on a new message instance. As a result, FieldMaskUtil adds maximize_conversions to the field mask instead of maximize_conversions.target_cpa_micros, which causes a FieldMaskError.FIELD_HAS_SUBFIELDS error.

# Create a campaign object representing the campaign you want to change.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
end

# The field mask in this operation includes 'maximize_conversions',
# but not 'maximize_conversions.target_cpa_micros', so it results in an
# error.
operation = client.operation.update_resource.campaign(campaign) do |c|
  c.maximize_conversions = client.resource.maximize_conversions do |mc|
    mc.target_cpa_micros = 0
  end
end

# Operation fails because the field mask is invalid.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)
# Create a campaign including the maximize conversions fields right away, since
# they are manually added to the field mask.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
  c.maximize_conversions = client.resource.maximize_conversions do |mc|
    mc.target_cpa_micros = 0
  end
end

# Create the operation with an empty field mask. You can add a block here with
# other changes that are automatically added to the field mask.
operation = client.operation.update_resource.campaign(campaign) {}

# Add the field to the field mask so the API knows to clear it.
operation.update_mask.paths << 'maximize_conversions.target_cpa_micros'

# Operation succeeds because the correct field mask is specified.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)

Note that the automatic comparison approach works as intended for fields defined as optional in the Google Ads API protocol buffers. Because target_cpa_micros is not an optional field on MaximizeConversions, explicitly appending the path to update_mask.paths is required to clear it.