Praktik terbaik mutasi

Ikuti praktik terbaik ini untuk mengoptimalkan performa, mengelola dependensi di seluruh operasi, dan menangani respons saat mengubah resource di Google Ads API.

Nama resource sementara

GoogleAdsService.Mutate dan BatchJobService mendukung nama resource sementara yang dapat direferensikan dalam operasi berikutnya. Dengan demikian, Anda dapat membuat kampanye dan grup iklan, iklan, serta kata kunci terkaitnya dalam satu permintaan modifikasi atau tugas batch.

Untuk mereferensikan resource yang baru dibuat dalam permintaan mutasi atau tugas batch yang sama, tentukan ID bilangan bulat negatif (seperti -1 atau -2, tidak termasuk 0) di kolom resource_name resource baru. Misalnya, saat membuat kampanye dalam permintaan batch, tetapkan nama resource-nya ke customers/CUSTOMER_ID/campaigns/-1. Saat membuat grup iklan dalam operasi berikutnya dalam permintaan yang sama, referensikan customers/CUSTOMER_ID/campaigns/-1 sebagai kampanye induk. API akan otomatis mengganti -1 dengan ID kampanye sebenarnya yang dibuat saat pembuatan.

Batasan penggunaan

Perhatikan aturan berikut saat menggunakan nama resource sementara:

  • Urutan itu penting: Anda hanya dapat mereferensikan nama resource sementara setelah menentukannya. Dalam daftar operasi, operasi dependen (seperti membuat grup iklan) harus muncul setelah operasi yang membuat resource induknya (seperti membuat kampanye).
  • Cakupan permintaan tunggal atau tugas batch: Nama resource sementara tidak akan dipertahankan di seluruh tugas atau permintaan mutasi yang terpisah. Untuk mereferensikan resource yang dibuat dalam tugas atau permintaan mutasi sebelumnya, gunakan nama resource sebenarnya yang dihasilkan sistem.
  • Keunikan global: Dalam satu tugas atau permintaan modifikasi, setiap nama resource sementara harus menggunakan bilangan bulat negatif yang unik di semua jenis resource. Misalnya, Anda tidak dapat menetapkan -1 ke kampanye dan grup iklan dalam permintaan yang sama. Menggunakan kembali ID sementara dalam permintaan atau tugas batch yang sama akan menampilkan error NewResourceCreationError.DUPLICATE_TEMP_IDS.

Contoh payload

Misalnya, Anda ingin menambahkan kampanye, grup iklan, dan iklan dalam satu permintaan API atau tugas batch. Anda dapat menyusun array mutateOperations dalam payload permintaan GoogleAdsService.Mutate atau BatchJobService.AddBatchJobOperations seperti yang ditunjukkan dalam contoh JSON REST berikut (dengan kolom resource wajib lainnya dihilangkan agar lebih singkat):

{
  "mutateOperations": [
    {
      "campaignOperation": {
        "create": {
          "resourceName": "customers/CUSTOMER_ID/campaigns/-1"
        }
      }
    },
    {
      "adGroupOperation": {
        "create": {
          "resourceName": "customers/CUSTOMER_ID/adGroups/-2",
          "campaign": "customers/CUSTOMER_ID/campaigns/-1"
        }
      }
    },
    {
      "adGroupAdOperation": {
        "create": {
          "adGroup": "customers/CUSTOMER_ID/adGroups/-2"
        }
      }
    }
  ]
}

Contoh ini menunjukkan detail penting berikut:

  • Grup iklan menggunakan ID sementara baru (-2) karena -1 sudah ditetapkan ke kampanye.
  • Grup iklan mereferensikan customers/CUSTOMER_ID/campaigns/-1 untuk menautkan dirinya sendiri ke kampanye yang dibuat dalam operasi sebelumnya.
  • adGroupAdOperation mereferensikan customers/CUSTOMER_ID/adGroups/-2 dan menghilangkan resourceName karena tidak ada operasi berikutnya dalam permintaan yang mereferensikan iklan baru.

Mengelompokkan operasi berjenis sama

Saat menggunakan GoogleAdsService.Mutate, kelompokkan operasi berdasarkan jenis resource dalam array mutate_operations berulang, dengan tetap memperhatikan dependensi induk dan turunan. Metode ini membaca operasi secara berurutan hingga menemukan jenis resource yang berbeda, lalu mengelompokkan semua operasi yang berdekatan dari jenis yang sama ke dalam satu permintaan layanan backend.

Misalnya, jika Anda menyertakan 5 operasi kampanye yang diikuti dengan 10 operasi grup iklan di kolom mutate_operations berulang, sistem akan melakukan dua panggilan backend: satu ke CampaignService untuk 5 operasi kampanye, dan yang kedua ke AdGroupService untuk 10 operasi grup iklan.

Sebaliknya, operasi interleaving dengan mengurutkannya sebagai [campaign, ad group, campaign, ad group] akan menghasilkan empat panggilan backend terpisah. Panggilan yang disisipkan akan menurunkan performa API dan dapat menyebabkan waktu tunggu permintaan habis pada batch besar.

Menangani kegagalan parsial dan batas batch

Secara default, GoogleAdsService.Mutate akan me-roll back seluruh permintaan jika ada satu operasi yang gagal. Untuk meng-commit operasi yang valid meskipun operasi lain dalam permintaan yang sama gagal, tetapkan partial_failure ke true pada permintaan dan periksa partial_failure_error dalam respons. Jika partial_failure adalah true, jika operasi induk yang menentukan ID sementara (seperti customers/CUSTOMER_ID/campaigns/-1) gagal divalidasi, setiap operasi turunan dependen yang merujuk ke ID sementara tersebut dalam permintaan yang sama juga akan gagal dengan NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Untuk mengetahui detail selengkapnya, lihat panduan kegagalan parsial.

Perhatikan juga ukuran permintaan, sub-batching, dan batas kapasitas:

  • Batas ukuran permintaan dan potongan: Satu permintaan GoogleAdsService.Mutate menerapkan batas 10.000 operasi mutasi (atau hingga 20.000 jika semua operasi dalam permintaan adalah AdGroupCriterionOperation, yang menampilkan RequestError.TOO_MANY_MUTATE_OPERATIONS jika terlampaui) dan maksimal 100 operasi tindakan (RequestError.TOO_MANY_ACTION_OPERATIONS). BatchJobService.AddBatchJobOperations menerapkan maksimum 10.000 operasi per panggilan, 10.484.504 byte per MutateOperation individual, dan 41.937.920 byte per AddBatchJobOperationsRequest (menampilkan BatchJobError.REQUEST_TOO_LARGE jika ada batas yang terlampaui, dengan total hingga 1.000.000 operasi per tugas batch). Mutasi serentak yang menargetkan kampanye atau akun yang sama dapat memicu error DatabaseError.CONCURRENT_MODIFICATION.
  • Sub-batching atomik BatchJobService: Meskipun tugas batch dijalankan berdasarkan semantik kegagalan parsial (secara default 1.000 operasi per sub-batch internal), BatchJobService otomatis mengelompokkan operasi dependen yang berdekatan tertentu untuk ID induk yang sama ke dalam sub-batch atomik:
    • AssetGroupOperation (create) diikuti dengan operasi AssetGroupAssetOperation (create) yang berdekatan untuk ID AssetGroup yang sama (total hingga 1.000 operasi, gagal secara atomik dengan BatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; setiap AssetGroupOperation update atau remove dijalankan dalam sub-batch operasi tunggal mandiri).
    • CampaignOperation Performa Maksimal (create, jika Panduan Merek diaktifkan—yang merupakan setelan default kecuali jika brand_guidelines_enabled ditetapkan ke false atau hotel_property_asset_set ditetapkan) diikuti dengan operasi CampaignAssetOperation (create) yang berdekatan untuk ID Campaign yang sama (total hingga 1.000 operasi, gagal secara atomik dengan BatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE).
    • Operasi AssetGroupListingGroupFilterOperation (max 10,000, gagal secara atomik dengan BatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) atau AdGroupCriterionOperation (listing_group, max 20,000, gagal secara atomik dengan CriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) berturut-turut untuk induk yang sama (AssetGroup atau AdGroup).

Mengambil atribut yang dapat diubah dari respons

Jika Anda menetapkan response_content_type permintaan mutasi ke MUTABLE_RESOURCE, respons akan berisi resource_name dan objek resource yang diisi dengan kolom yang dapat diubah (serta kolom utama yang diisi sistem pada resource yang ditampilkan, seperti ExperimentArm.in_design_campaigns) untuk setiap objek yang didukung yang dibuat atau diperbarui (tidak dihapus) oleh permintaan. Untuk operasi remove—atau untuk jenis resource yang tidak mendukung pengembalian MUTABLE_RESOURCE—respons selalu hanya menampilkan resource_name. Gunakan fitur ini untuk menghindari pengiriman permintaan Search atau SearchStream tambahan setelah setiap panggilan mutasi.

Jika Anda tidak menetapkan response_content_type, Google Ads API akan ditetapkan secara default ke RESOURCE_NAME_ONLY dan hanya menampilkan resource_name dari setiap resource yang diubah.

Contoh berikut menunjukkan cara mengambil resource yang dapat diubah dari panggilan mutasi:

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.V26.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];
}
      

Python

def create_experiment_arms(
    client: GoogleAdsClient,
    customer_id: str,
    base_campaign_id: str,
    experiment: str,
) -> str:
    """Creates a control and treatment experiment arms.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        base_campaign_id: the campaign ID to associate with the control arm of
          the experiment.
        experiment: the resource name for an experiment.

    Returns:
        the resource name for the new treatment experiment arm.
    """
    operations: List[ExperimentArmOperation] = []

    campaign_service: CampaignServiceClient = client.get_service(
        "CampaignService"
    )

    # The "control" arm references an already-existing campaign.
    operation_1: ExperimentArmOperation = client.get_type(
        "ExperimentArmOperation"
    )
    exa_1: ExperimentArm = operation_1.create
    exa_1.control = True
    exa_1.campaigns.append(
        campaign_service.campaign_path(customer_id, base_campaign_id)
    )
    exa_1.experiment = experiment
    exa_1.name = "control arm"
    exa_1.traffic_split = 40
    operations.append(operation_1)

    # In standard campaign experiments, creating the treatment arm automatically
    # generates a draft campaign that you can modify before starting the experiment.
    operation_2: ExperimentArmOperation = client.get_type(
        "ExperimentArmOperation"
    )
    exa_2: ExperimentArm = operation_2.create
    exa_2.control = False
    exa_2.experiment = experiment
    exa_2.name = "experiment arm"
    exa_2.traffic_split = 60
    operations.append(operation_2)

    experiment_arm_service: ExperimentArmServiceClient = client.get_service(
        "ExperimentArmService"
    )
    request: MutateExperimentArmsRequest = client.get_type(
        "MutateExperimentArmsRequest"
    )
    request.customer_id = customer_id
    request.operations = 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.
    request.response_content_type = (
        client.enums.ResponseContentTypeEnum.MUTABLE_RESOURCE
    )
    response: MutateExperimentArmsResponse = (
        experiment_arm_service.mutate_experiment_arms(request=request)
    )

    # Results always return in the order that you specify them in the request.
    # Since we created the treatment arm second, it will be the second result.
    control_arm_result: Any = response.results[0]
    treatment_arm_result: Any = response.results[1]

    print(
        f"Created control arm with resource name {control_arm_result.resource_name}"
    )
    print(
        f"Created treatment arm with resource name {treatment_arm_result.resource_name}"
    )

    return treatment_arm_result.experiment_arm.in_design_campaigns[0]
      

Ruby

def create_experiment_arms(client, customer_id, base_campaign_id, experiment)
  operations = []
  operations << client.operation.create_resource.experiment_arm do |ea|
    # The "control" arm references an already-existing campaign.
    ea.control = true
    ea.campaigns << client.path.campaign(customer_id, base_campaign_id)
    ea.experiment = experiment
    ea.name = 'control arm'
    ea.traffic_split = 40
  end
  operations << client.operation.create_resource.experiment_arm do |ea|
    # In standard campaign experiments, creating the treatment arm automatically
    # generates a draft campaign that you can modify before starting the
    # experiment.
    ea.control = false
    ea.experiment = experiment
    ea.name = 'experiment arm'
    ea.traffic_split = 60
  end

  response = client.service.experiment_arm.mutate_experiment_arms(
    customer_id: customer_id,
    operations: 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.
    response_content_type: :MUTABLE_RESOURCE,
  )

  # Results always return in the order that you specify them in the request.
  # Since we created the treatment arm second, it will be the second result.
  control_arm_result = response.results.first
  treatment_arm_result = response.results.last

  puts "Created control arm with resource name #{control_arm_result.resource_name}."
  puts "Created treatment arm with resource name #{treatment_arm_result.resource_name}."

  treatment_arm_result.experiment_arm.in_design_campaigns.first
end
      

Perl

sub create_experiment_arms {
  my ($api_client, $customer_id, $base_campaign_id, $experiment) = @_;

  my $operations = [];
  push @$operations,
    Google::Ads::GoogleAds::V26::Services::ExperimentArmService::ExperimentArmOperation
    ->new({
      create => Google::Ads::GoogleAds::V26::Resources::ExperimentArm->new({
          # The "control" arm references an already-existing campaign.
          control   => "true",
          campaigns => [
            Google::Ads::GoogleAds::V26::Utils::ResourceNames::campaign(
              $customer_id, $base_campaign_id
            )
          ],
          experiment   => $experiment,
          name         => "control arm",
          trafficSplit => 40
        })});

  push @$operations,
    Google::Ads::GoogleAds::V26::Services::ExperimentArmService::ExperimentArmOperation
    ->new({
      create => Google::Ads::GoogleAds::V26::Resources::ExperimentArm->new({
          # 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   => $experiment,
          name         => "experiment arm",
          trafficSplit => 60
        })});

  my $response = $api_client->ExperimentArmService()->mutate({
    customerId => $customer_id,
    operations => $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.
    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.
  my $control_arm_result   = $response->{results}[0];
  my $treatment_arm_result = $response->{results}[1];

  printf "Created control arm with resource name '%s'.\n",
    $control_arm_result->{resourceName};
  printf "Created treatment arm with resource name '%s'.\n",
    $treatment_arm_result->{resourceName};
  return $treatment_arm_result->{experimentArm}{inDesignCampaigns}[0];
}
      

curl