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 einzelnen Mutationsanfrage oder einem Batch-Job 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 (außer 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 innerhalb 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 beim Erstellen generiert wird.

Nutzungseinschränkungen

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

  • Reihenfolge wichtig:Sie können erst 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).
  • Bereich für einzelne Anfragen oder Batchjobs:Temporäre Ressourcennamen bleiben nicht über separate Jobs oder Änderungsanfragen hinweg erhalten. 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:In einem einzelnen Job oder einer einzelnen Mutationsanfrage muss für jeden temporären Ressourcennamen eine eindeutige negative Ganzzahl für alle Ressourcentypen verwendet werden. Sie können -1 beispielsweise nicht in derselben Anfrage sowohl einer Kampagne als auch einer Anzeigengruppe 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 eine Kampagne, eine Anzeigengruppe und eine Anzeige in einer einzelnen API-Anfrage oder einem Batch-Job hinzufügen. Sie können das mutateOperations-Array in einer GoogleAdsService.Mutate- oder BatchJobService.AddBatchJobOperations-Anfrage-Nutzlast strukturieren, wie im folgenden REST-JSON-Beispiel gezeigt (andere erforderliche Ressourcenfelder wurden der Einfachheit halber 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"
        }
      }
    }
  ]
}

In diesem Beispiel werden die folgenden wichtigen Details veranschaulicht:

  • 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 im vorherigen Vorgang erstellten Kampagne zu verknüpfen.
  • Die adGroupAdOperation verweist auf customers/CUSTOMER_ID/adGroups/-2 und lässt resourceName aus, da kein nachfolgender Vorgang in der Anfrage auf die neue Anzeige verweist.

Vorgänge desselben Typs gruppieren

Wenn Sie GoogleAdsService.Mutate verwenden, fassen Sie Vorgänge nach Ressourcentyp im wiederholten mutate_operations-Array zusammen und berücksichtigen Sie dabei die Abhängigkeiten zwischen übergeordneten 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 einzelnen Back-End-Dienstanfrage zusammengefasst.

Wenn Sie beispielsweise fünf Kampagnenvorgänge und dann zehn Anzeigengruppenvorgänge in das wiederholte Feld mutate_operations einfügen, 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 die 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.

Partielle Fehler und Batch-Limits verarbeiten

Standardmäßig wird mit 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 gleich true ist und die Validierung eines übergeordneten Vorgangs, der eine temporäre ID definiert (z. B. customers/CUSTOMER_ID/campaigns/-1), fehlschlägt, 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 im Leitfaden zu Teilausfällen.

Beachten Sie auch die Anfragengröße, die Aufteilung in Unter-Batches und die Ratenlimits:

  • Anfrage- und Chunk-Größenbeschränkungen:Für eine einzelne GoogleAdsService.Mutate-Anfrage gilt ein Limit von 10.000 Mutationsvorgängen (oder bis zu 20.000, wenn alle Vorgänge in der Anfrage AdGroupCriterionOperations sind, wobei RequestError.TOO_MANY_MUTATE_OPERATIONS zurückgegeben wird, wenn das Limit überschritten wird) und maximal 100 Aktionsvorgängen (RequestError.TOO_MANY_ACTION_OPERATIONS). Für BatchJobService.AddBatchJobOperations gilt ein Limit von maximal 10.000 Vorgängen pro Aufruf, 10.484.504 Byte pro einzelnem MutateOperation und 41.937.920 Byte pro AddBatchJobOperationsRequest (wobei BatchJobError.REQUEST_TOO_LARGE zurückgegeben wird, wenn ein Limit überschritten wird, mit insgesamt bis zu 1.000.000 Vorgängen pro Batchjob). Gleichzeitige Mutationen, die auf dieselbe Kampagne oder dasselbe Konto ausgerichtet sind, können DatabaseError.CONCURRENT_MODIFICATION-Fehler auslösen.
  • BatchJobService Atomare Sub-Batch-Verarbeitung:Batch-Jobs werden zwar mit Semantik für Teilausfälle ausgeführt (standardmäßig 1.000 Vorgänge pro internem Sub-Batch), aber BatchJobService gruppiert bestimmte zusammenhängende abhängige Vorgänge für dieselbe übergeordnete ID automatisch in atomaren Sub-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; jede AssetGroupOperation update oder remove wird in einem eigenständigen Sub-Batch mit einem einzelnen Vorgang ausgeführt).
    • Eine Performance Max-Kampagne CampaignOperation (create, wenn Markenrichtlinien aktiviert sind, was die Standardeinstellung ist, es sei denn, brand_guidelines_enabled ist auf false festgelegt 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-Vorgänge (max 10,000, die atomar mit BatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE fehlschlagen) oder AdGroupCriterionOperation-Vorgänge (listing_group, max 20,000, die atomar mit CriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION fehlschlagen) für dasselbe übergeordnete Element (AssetGroup oder AdGroup).

Veränderbare Attribute aus der Antwort abrufen

Wenn Sie response_content_type Ihrer Mutationsanfrage auf MUTABLE_RESOURCE setzen, enthält die Antwort die resource_name und das Ressourcenobjekt, das mit den änderbaren Feldern (sowie den vom Schlüsselsystem ausgefüllten Feldern der zurückgegebenen Ressource, z. B. ExperimentArm.in_design_campaigns) für jedes unterstützte Objekt gefüllt ist, das durch die Anfrage erstellt oder aktualisiert (nicht entfernt) wurde. Bei remove-Vorgängen oder bei Ressourcentypen, die die Rückgabe von MUTABLE_RESOURCE nicht unterstützen, wird in der Antwort immer nur resource_name zurückgegeben. Mit dieser Funktion können Sie vermeiden, dass nach jedem Mutate-Aufruf eine zusätzliche Search- oder SearchStream-Anfrage gesendet wird.

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

Das folgende Beispiel zeigt, wie eine veränderliche Ressource aus einem Mutate-Aufruf abgerufen wird:

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