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

В 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.