Best Practices für Mutationen

Mit diesen Best Practices können Sie die Leistung optimieren, Abhängigkeiten zwischen Vorgängen verwalten und Antworten verarbeiten, wenn Sie Ressourcen in der Google Ads API ändern.

Temporäre Ressourcennamen

Sowohl GoogleAdsService.Mutate als auch BatchJobService unterstützen temporäre Ressourcennamen, auf die in nachfolgenden Vorgängen verwiesen werden kann. So können Sie eine Kampagne und die zugehörigen Anzeigengruppen, Anzeigen und Keywords in einer einzigen Mutationsanfrage oder einem Batchjob erstellen.

Wenn Sie in derselben Mutationsanfrage oder demselben Batchjob auf eine neu erstellte Ressource verweisen möchten, geben Sie im Feld resource_name der neuen Ressource eine negative Ganzzahl-ID an (z. B. -1 oder -2, aber nicht 0). Wenn Sie beispielsweise eine Kampagne in einer Batchanfrage erstellen, legen Sie den Ressourcennamen auf customers/CUSTOMER_ID/campaigns/-1 fest. Wenn Sie in einem späteren Vorgang in derselben Anfrage eine Anzeigengruppe erstellen, verweisen Sie auf customers/CUSTOMER_ID/campaigns/-1 als übergeordnete Kampagne. Die API ersetzt -1 automatisch durch die tatsächliche Kampagnen-ID, die bei der Erstellung generiert wurde.

Nutzungsbeschränkungen

Beachten Sie bei der Verwendung temporärer Ressourcennamen die folgenden Regeln:

  • Reihenfolge ist wichtig:Sie können nur auf einen temporären Ressourcennamen verweisen, nachdem Sie ihn definiert haben. In einer Liste von Vorgängen muss der abhängige Vorgang (z. B. das Erstellen einer Anzeigengruppe) nach dem Vorgang stehen, mit dem die übergeordnete Ressource erstellt wird (z. B. das Erstellen einer Kampagne).
  • Umfang der Einzelanfrage oder des Batchjobs:Temporäre Ressourcennamen bleiben nicht über separate Jobs oder Änderungsanfragen hinweg bestehen. Wenn Sie auf eine Ressource verweisen möchten, die in einem vorherigen Job oder einer vorherigen Mutationsanfrage erstellt wurde, verwenden Sie den tatsächlichen vom System generierten Ressourcennamen.
  • Globale Eindeutigkeit:Innerhalb eines einzelnen Jobs oder einer einzelnen Mutationsanfrage muss für jeden temporären Ressourcennamen eine eindeutige negative Ganzzahl für alle Ressourcentypen verwendet werden. Sie können beispielsweise nicht in derselben Anfrage sowohl einer Kampagne als auch einer Anzeigengruppe -1 zuweisen. Wenn Sie eine temporäre ID innerhalb derselben Anfrage oder desselben Batchjobs wiederverwenden, wird der Fehler NewResourceCreationError.DUPLICATE_TEMP_IDS zurückgegeben.

Beispielnutzlast

Angenommen, Sie möchten in einer einzigen API-Anfrage oder einem einzigen Batchjob eine Kampagne, eine Anzeigengruppe und eine Anzeige hinzufügen. Sie können das Array mutateOperations in einer GoogleAdsService.Mutate- oder BatchJobService.AddBatchJobOperations-Anfrage so strukturieren, wie im folgenden REST-JSON-Beispiel gezeigt (andere erforderliche Ressourcenfelder wurden aus Gründen der Übersichtlichkeit weggelassen):

{
  "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"
        }
      }
    }
  ]
}

Dieses Beispiel veranschaulicht die folgenden wichtigen Details:

  • Die Anzeigengruppe verwendet eine neue temporäre ID (-2), da -1 bereits der Kampagne zugewiesen ist.
  • Die Anzeigengruppe verweist auf customers/CUSTOMER_ID/campaigns/-1, um sich mit der Kampagne zu verknüpfen, die im vorherigen Vorgang erstellt wurde.
  • Der adGroupAdOperation-Vorgang verweist auf customers/CUSTOMER_ID/adGroups/-2 und lässt resourceName weg, da kein nachfolgender Vorgang in der Anfrage auf die neue Anzeige verweist.

Vorgänge desselben Typs gruppieren

Wenn Sie GoogleAdsService.Mutate verwenden, gruppieren Sie Vorgänge nach Ressourcentyp im wiederholten mutate_operations Array und berücksichtigen Sie dabei die Abhängigkeiten zwischen über- und untergeordneten Elementen. Bei dieser Methode werden Vorgänge sequenziell gelesen, bis ein anderer Ressourcentyp gefunden wird. Anschließend werden alle zusammenhängenden Vorgänge desselben Typs in einer einzigen Backend-Dienstanfrage zusammengefasst.

Wenn Sie beispielsweise fünf Kampagnenvorgänge und dann zehn Anzeigengruppenvorgänge im wiederholten mutate_operations Feld angeben, führt das System zwei Backend-Aufrufe aus: einen an CampaignService für die fünf Kampagnenvorgänge und einen zweiten an AdGroupService für die zehn Anzeigengruppenvorgänge.

Wenn Sie Vorgänge dagegen verschachteln, indem Sie sie als [campaign, ad group, campaign, ad group] anordnen, werden vier separate Backend-Aufrufe ausgeführt. Verschachtelte Aufrufe beeinträchtigen die API-Leistung und können bei großen Batches zu Zeitüberschreitungen bei Anfragen führen.

Teilweise Fehler und Batchlimits verarbeiten

Standardmäßig wird bei GoogleAdsService.Mutate die gesamte Anfrage zurückgesetzt, wenn ein einzelner Vorgang fehlschlägt. Wenn Sie gültige Vorgänge auch dann ausführen möchten, wenn andere Vorgänge in derselben Anfrage fehlschlagen, setzen Sie partial_failure in der Anfrage auf true und prüfen Sie partial_failure_error in der Antwort. Wenn partial_failure auf true gesetzt ist und ein übergeordneter Vorgang, der eine temporäre ID definiert (z. B. customers/CUSTOMER_ID/campaigns/-1), die Validierung nicht besteht, schlagen auch alle abhängigen untergeordneten Vorgänge, die in derselben Anfrage auf diese temporäre ID verweisen, mit NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS fehl. Weitere Informationen finden Sie unter siehe den Leitfaden zu teilweisen Fehlern.

Beachten Sie auch die Größenbeschränkungen für Anfragen, die Aufteilung in Unter-Batches und die Ratenbegrenzungen:

  • Größenbeschränkungen für Anfragen und Chunks:Bei einer einzelnen GoogleAdsService.Mutate-Anfrage gilt ein Limit von 10.000 Änderungsvorgängen (oder bis zu 20.000, wenn alle Vorgänge in der Anfrage AdGroupCriterionOperations sind). Wenn dieses Limit überschritten wird, wird RequestError.TOO_MANY_MUTATE_OPERATIONS zurückgegeben. Außerdem sind maximal 100 Aktionsvorgänge zulässig (RequestError.TOO_MANY_ACTION_OPERATIONS). BatchJobService.AddBatchJobOperations enforces a maximum of 10.000 Vorgängen pro Aufruf, 10.484.504 Byte pro einzelnem MutateOperation und 41.937.920 Byte pro AddBatchJobOperationsRequest (returning BatchJobError.REQUEST_TOO_LARGE if any limit is exceeded, with up to 1.000.000 Vorgängen insgesamt pro Batchjob). Gleichzeitige Änderungen an derselben Kampagne oder demselben Konto können DatabaseError.CONCURRENT_MODIFICATION-Fehler auslösen.
  • BatchJobService atomare Unter-Batches: Obwohl Batchjobs mit der Semantik für teilweise Fehler ausgeführt werden (standardmäßig 1.000 Vorgänge pro internem Unter-Batch), gruppiert BatchJobService bestimmte zusammenhängende abhängige Vorgänge für dieselbe übergeordnete ID automatisch in atomare Unter-Batches:
    • Ein AssetGroupOperation (create), gefolgt von zusammenhängenden AssetGroupAssetOperation (create) Vorgängen für dieselbe AssetGroup-ID (insgesamt bis zu 1.000 Vorgänge, die atomar mit BatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE fehlschlagen; jeder AssetGroupOperation update oder remove wird in einem eigenständigen Unter-Batch mit einem einzelnen Vorgang ausgeführt).
    • Ein CampaignOperation-Vorgang vom Typ create für Performance Max-Kampagnen (wenn Markenrichtlinien aktiviert sind – das ist die Standardeinstellung, es sei denn, brand_guidelines_enabled ist auf false oder hotel_property_asset_set ist festgelegt), gefolgt von zusammenhängenden CampaignAssetOperation-Vorgängen (create) für dieselbe Campaign-ID (insgesamt bis zu 1.000 Vorgänge, die atomar mit BatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE fehlschlagen).
    • Aufeinanderfolgende AssetGroupListingGroupFilterOperation (max 10,000, die atomar mit BatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE fehlschlagen) oder AdGroupCriterionOperation (listing_group, max 20,000, die atomar mit CriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION fehlschlagen) Vorgänge für dasselbe übergeordnete Element (AssetGroup oder AdGroup).

Änderbare Attribute aus der Antwort abrufen

Wenn Sie das response_content_type Ihrer Mutationsanfrage auf MUTABLE_RESOURCE setzen, enthält die Antwort für jedes unterstützte Objekt, das durch die Mutationsanfrage erstellt oder aktualisiert (nicht entfernt) wurde, das resource_name und das Ressourcenobjekt mit seinen änderbaren Feldern (sowie wichtige vom System ausgefüllte Felder der zurückgegebenen Ressource, z. B. ExperimentArm.in_design_campaigns). Bei remove-Vorgängen oder bei Ressourcentypen, die die Rückgabe von MUTABLE_RESOURCE nicht unterstützen, enthält die Antwort immer nur resource_name. Mit dieser Funktion können Sie vermeiden, nach jedem Änderungsaufruf eine zusätzliche Search oder SearchStream Anfrage zu senden.

Wenn Sie response_content_type nicht festlegen, wird in der Google Ads API standardmäßig RESOURCE_NAME_ONLY verwendet und es wird nur der resource_name jeder geänderten Ressource zurückgegeben.

Das folgende Beispiel zeigt, wie Sie eine änderbare Ressource aus einem Änderungsaufruf abrufen:

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

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|
    # The non-"control" arm, also called a "treatment" arm, will automatically
    # generate draft campaigns 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 last, it will be the last 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::V25::Services::ExperimentArmService::ExperimentArmOperation
    ->new({
      create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({
          # The "control" arm references an already-existing campaign.
          control   => "true",
          campaigns => [
            Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(
              $customer_id, $base_campaign_id
            )
          ],
          experiment   => $experiment,
          name         => "control arm",
          trafficSplit => 40
        })});

  push @$operations,
    Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation
    ->new({
      create => Google::Ads::GoogleAds::V25::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