← 返回目录
来源:Google Ads API 官方文档 · 中文版 · 本地归档

Mutate 最佳做法

临时资源名称

GoogleAdsService.Mutate 支持使用临时资源名称,以便稍后在同一个请求中进行引用。这样,您就可以在一个请求中(例如)制作广告系列及其关联的广告组、广告、关键字等。

为此,您可以指定新资源的 resource_name 使用负 ID。例如,假设您制作了一个广告系列,并将其资源名称 指定为 customers/<YOUR_CUSTOMER_ID>/campaigns/-1。那么在以后的操作中创建广告组时,就可以通过该资源名称进行引用,而您指定的 -1 会被自动替换为已制作广告系列的实际 ID。

使用临时资源名称时,请注意以下事项:

  • 临时资源名称只能在资源中定义后使用。在下例的操作列表中,广告组操作必须显示在广告系列操作之后。
  • 系统无法跨作业或 mutate 请求记住临时资源名称。 所以,要引用在此前的作业或 mutate 请求中创建的资源,请使用真实的资源名称。
  • 对于单个作业或 mutate 请求,每个临时资源名称都必须使用具有唯一性的负数,即使它们来自不同的资源类型。如果在单个作业或 mutate 请求中重复使用临时 ID,则会返回错误。

示例

假设您希望在单个 API 请求中添加一个广告系列、一个广告组和一个广告。您可以为该请求创建一个类似如下内容的结构:

mutate_operations: [
  {
    campaign_operation: {
      create: {
        resource_name: "customers/<YOUR_CUSTOMER_ID>/campaigns/-1",
        ...
      }
    }
  },
  {
    ad_group_operation: {
      create: {
        resource_name: "customers/<YOUR_CUSTOMER_ID>/adGroups/-2",
        campaign: "customers/<YOUR_CUSTOMER_ID>/campaigns/-1"
        ...
      }
    }
  },
  {
    ad_group_ad_operation: {
      create: {
        ad_group: "customers/<YOUR_CUSTOMER_ID>/adGroups/-2"
        ...
      }
    }
  },
]

广告组使用了新的临时 ID,这是因为我们无法重复利用广告系列已使用的 -1。我们还会在制作广告组广告时引用此广告组。广告组本身会引用资源名称,而该资源名称是我们为该请求的早期操作中的广告系列确定的,而 ad_group_ad_operation 中的 resource_name 则不是必需的,因为没有其他操作引用它。

组合相同类型的操作

使用 GoogleAdsService.Mutate 时,请务必根据重复操作数组中的资源将操作组合在一起。作为一种方式,该 mutate 方法发挥的作用其实是自动依序调用每个资源的自有 mutate 方法。为此,它会在操作中进行读取,直到找到一项面向不同资源类型的操作,然后在一个请求中批处理所有相同类型的操作。

例如,如果您在 Mutate 调用的重复 operations 字段中有 5 项广告系列操作,后跟 10 项广告组操作,那么系统会在后端总共执行 2 次调用,一次调用 CampaignService 执行 5 项操作,另一次调用 AdGroupService 执行 10 项操作。不过,如果以不同的方式对它们进行分组,性能可能会差很多。比如,假设您只制作 2 个广告系列,并仅创建 2 个广告组,虽然数量都比前例少,但按 [广告系列, 广告组, 广告系列, 广告组] 的操作顺序进行排列后,会导致系统一共要在后端执行 4 次调用。这会导致 API 性能降低,在极端情况下甚至会导致超时。

从响应中检索可变属性

如果您将 mutate 请求的 response_content_type 设置为 MUTABLE_RESOURCE, 则响应将包含请求创建或更新的每个对象的所有 可变 字段的值。使用此功能可避免在每个 mutate 请求后发出额外的 search 或 searchStream 请求。

如果您未设置 response_content_type,则 Google Ads API 会默认使用 RESOURCE_NAME_ONLY,并且响应将仅包含每个已创建或更新资源的资源名称。

以下示例展示了如何从 API 调用中检索可变资源:

Java

private String createExperimentArms(
    GoogleAdsClient googleAdsClient, long customerId, long campaignId, String experiment) {
  List<ExperimentArmOperation> operations = new ArrayList<>();
  operations.add(
      ExperimentArmOperation.newBuilder()
          .setCreate(
              // The "control" arm references an already-existing campaign.
              ExperimentArm.newBuilder()
                  .setControl(true)
                  .addCampaigns(ResourceNames.campaign(customerId, campaignId))
                  .setExperiment(experiment)
                  .setName("control arm")
                  .setTrafficSplit(40)
                  .build())
          .build());
  operations.add(
      ExperimentArmOperation.newBuilder()
          .setCreate(
              // In standard campaign experiments, creating the treatment arm automatically
              // generates a draft campaign that you can modify before starting the experiment.
              ExperimentArm.newBuilder()
                  .setControl(false)
                  .setExperiment(experiment)
                  .setName("experiment arm")
                  .setTrafficSplit(60)
                  .build())
          .build());

  try (ExperimentArmServiceClient experimentArmServiceClient =
      googleAdsClient.getLatestVersion().createExperimentArmServiceClient()) {
    // Constructs the mutate request.
    MutateExperimentArmsRequest mutateRequest =
        MutateExperimentArmsRequest.newBuilder()
            .setCustomerId(Long.toString(customerId))
            .addAllOperations(operations)
            // We want to fetch the draft campaign IDs from the treatment arm, so the easiest way
            // to do that is to have the response return the newly created entities.
            .setResponseContentType(ResponseContentType.MUTABLE_RESOURCE)
            .build();

    // Sends the mutate request.
    MutateExperimentArmsResponse response =
        experimentArmServiceClient.mutateExperimentArms(mutateRequest);

    // Results always return in the order that you specify them in the request. Since we created
    // the treatment arm last, it will be the last result.  If you don't remember which arm is the
    // treatment arm, you can always filter the query in the next section with
    // `experiment_arm.control = false`.
    MutateExperimentArmResult controlArmResult = response.getResults(0);
    MutateExperimentArmResult treatmentArmResult =
        response.getResults(response.getResultsCount() - 1);

    System.out.printf(
        "Created control arm with resource name '%s'%n", controlArmResult.getResourceName());
    System.out.printf(
        "Created treatment arm with resource name '%s'%n", treatmentArmResult.getResourceName());

    return treatmentArmResult.getExperimentArm().getInDesignCampaigns(0);
  }
}

      

C#

private static (MutateExperimentArmResult, MutateExperimentArmResult)
    CreateExperimentArms(GoogleAdsClient client, long customerId, long baseCampaignId,
        string experimentResourceName)
{
    // Get the ExperimentArmService.
    ExperimentArmServiceClient experimentService = client.GetService(
        Services.V25.ExperimentArmService);

    // Create the control arm. The control arm references an already-existing campaign.
    ExperimentArmOperation controlArmOperation = new ExperimentArmOperation()
    {
        Create = new ExperimentArm()
        {
            Control = true,
            Campaigns = {
                ResourceNames.Campaign(customerId, baseCampaignId)
            },
            Experiment = experimentResourceName,
            Name = "Control Arm",
            TrafficSplit = 40
        }
    };

    // Create the non-control arm.
    // In standard campaign experiments, creating the treatment arm automatically
    // generates a draft campaign that you can modify before starting the experiment.
    ExperimentArmOperation treatmentArmOperation = new ExperimentArmOperation()
    {
        Create = new ExperimentArm()
        {
            Control = false,
            Experiment = experimentResourceName,
            Name = "Experiment Arm",
            TrafficSplit = 60
        }
    };

    // We want to fetch the draft campaign IDs from the treatment arm, so the
    // easiest way to do that is to have the response return the newly created
    // entities.
    MutateExperimentArmsRequest request = new MutateExperimentArmsRequest
    {
        CustomerId = customerId.ToString(),
        Operations = { controlArmOperation, treatmentArmOperation },
        ResponseContentType = ResponseContentType.MutableResource
    };

    MutateExperimentArmsResponse response = experimentService.MutateExperimentArms(
        request
    );

    // Results always return in the order that you specify them in the request.
    // Since we created the treatment arm last, it will be the last result.
    MutateExperimentArmResult controlArm = response.Results.First();
    MutateExperimentArmResult treatmentArm = response.Results.Last();

    Console.WriteLine($"Created control arm with resource name " +
        $"'{controlArm.ResourceName}'.");
    Console.WriteLine($"Created treatment arm with resource name" +
      $" '{treatmentArm.ResourceName}'.");
    return (controlArm, treatmentArm);
}
      

PHP

private static function createExperimentArms(
    GoogleAdsClient $googleAdsClient,
    int $customerId,
    int $campaignId,
    string $experimentResourceName
): string {
    $operations = [];
    $experimentArm1 = new ExperimentArm([
        // The "control" arm references an already-existing campaign.
        'control' => true,
        'campaigns' => [ResourceNames::forCampaign($customerId, $campaignId)],
        'experiment' => $experimentResourceName,
        'name' => 'control arm',
        'traffic_split' => 40
    ]);
    $operations[] = new ExperimentArmOperation(['create' => $experimentArm1]);
    $experimentArm2 = new ExperimentArm([
        // The non-"control" arm, also called a "treatment" arm, will automatically
        // generate draft campaigns that you can modify before starting the
        // experiment.
        'control' => false,
        'experiment' => $experimentResourceName,
        'name' => 'experiment arm',
        'traffic_split' => 60
    ]);
    $operations[] = new ExperimentArmOperation(['create' => $experimentArm2]);

    // Issues a request to create the experiment arms.
    $experimentArmServiceClient = $googleAdsClient->getExperimentArmServiceClient();
    $response = $experimentArmServiceClient->mutateExperimentArms(
        MutateExperimentArmsRequest::build($customerId, $operations)
            // We want to fetch the draft campaign IDs from the treatment arm, so the easiest
            // way to do that is to have the response return the newly created entities.
            ->setResponseContentType(ResponseContentType::MUTABLE_RESOURCE)
    );
    // Results always return in the order that you specify them in the request.
    // Since we created the treatment arm last, it will be the last result.
    $controlArmResourceName = $response->getResults()[0]->getResourceName();
    $treatmentArm = $response->getResults()[count($operations) - 1];
    print "Created control arm with resource name '$controlArmResourceName'" . PHP_EOL;
    print "Created treatment arm with resource name '{$treatmentArm->getResourceName()}'"
        . PHP_EOL;

    return $treatmentArm->getExperimentArm()->getInDesignCampaigns()[0];
}
在接这个接口时卡住了?
Stuck wiring this API into production? We build ad-delivery tooling on these platforms — campaign automation, reporting pipelines, error-handling and migration between API versions.
联系我们 · Talk to us 每日平台更新简报 →