Обновления с использованием масок полей

В Google Ads API обновления выполняются с помощью маски поля. Маска поля содержит список всех полей, которые вы хотите изменить при обновлении. Любые указанные поля, которых нет в маске, игнорируются, даже если они отправлены на сервер.

FieldMaskUtil

Рекомендуемый способ создания масок полей – использование встроенной утилиты, которая скрывает определенные сведения и позволяет автоматически создавать маски полей, отслеживая изменения, которые вы вносите в поля объекта.

В следующем примере показано, как создать маску поля для обновления кампании:

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

Сначала код создает пустой объект Campaign, а затем задает его название ресурса, чтобы сообщить API, какая кампания обновляется.

В этом примере для начала блока, включающего обновления, используется метод client.field_mask.with в кампании. В конце этого блока утилита сравнивает текущее состояние кампании после блока с исходным состоянием кампании до блока и автоматически создает маску поля, перечисляющую измененные поля. Вы можете указать эту маску поля в операции, когда создаете ее для вызова mutate, как показано ниже.

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

Этот метод рекомендуется, если вы создаете сложную операцию и хотите точно контролировать каждый шаг. Однако в большинстве случаев вы можете передать название ресурса (или существующий экземпляр ресурса) в фабричный метод библиотеки Ruby:

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

Если указать строку с названием ресурса, этот метод автоматически создаст новый ресурс кампании с заполненным полем resource_name, создаст маску поля на основе изменений, внесенных вами в блок, создаст операцию обновления и вернет окончательную операцию с уже заполненными полями update и update_mask. Чтобы указать начальное состояние кампании, вместо строки с названием ресурса можно передать существующий экземпляр прототипа Campaign. Этот шаблон работает для всех ресурсов, поддерживающих операцию обновления.

Как создать маску поля вручную

Чтобы создать маску поля с нуля без использования утилит библиотеки, создайте объект Google::Protobuf::FieldMask, массив с названиями всех полей, которые вы хотите изменить, и назначьте этот массив полю paths маски поля:

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

Обновление полей сообщений и их подполей

Поля MESSAGE могут иметь подполя (например, у MaximizeConversions их три: target_cpa_micros, cpc_bid_ceiling_micros и cpc_bid_floor_micros) или не иметь их вовсе (например, ManualCpm).

Поля сообщений без определенных подполей

При обновлении поля MESSAGE, для которого не определены подполя, используйте FieldMaskUtil, чтобы создать маску поля, как показано выше.

Поля сообщений с определенными подполями

При обновлении поля MESSAGE, определенного с помощью подполей, без явного указания каких-либо подполей в этом сообщении необходимо вручную добавить каждое из изменяемых подполей MESSAGE в FieldMask, как в приведенном выше примере, где маска поля создается с нуля.

Распространенный пример – обновление стратегии назначения ставок в кампании без настройки полей в новой стратегии. В примере ниже показано, как обновить кампанию, чтобы использовать стратегию назначения ставок MaximizeConversions, не задавая никаких вложенных полей в стратегии назначения ставок.

В этом примере встроенное сравнение FieldMaskUtil не позволяет достичь желаемого результата.

Следующий код создает маску поля, которая включает maximize_conversions. Однако Google Ads API не позволяет этого, чтобы предотвратить случайную очистку полей, и выдает ошибку FieldMaskError.FIELD_HAS_SUBFIELDS.

# 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]
)

Очистить поля

Некоторые поля можно очистить явным образом. Как и в предыдущем примере, вы должны явно добавить эти поля в маску поля. Предположим, что в вашей кампании используется стратегия назначения ставок MaximizeConversions, а в поле target_cpa_micros задано значение, превышающее 0.

В proto3 установка для необязательного скалярного поля значения по умолчанию (0) ничем не отличается от того, чтобы оставить его незаданным в новом экземпляре сообщения. В результате FieldMaskUtil добавляет в маску поля maximize_conversions вместо maximize_conversions.target_cpa_micros, что приводит к ошибке FieldMaskError.FIELD_HAS_SUBFIELDS.

# 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]
)

Обратите внимание, что автоматическое сравнение работает должным образом для полей, определенных как optional в буферах протокола Google Ads API. Поскольку target_cpa_micros не является полем optional в MaximizeConversions, для его очистки необходимо явно добавить путь к update_mask.paths.