Sprawdzone metody modyfikowania

Aby zoptymalizować skuteczność, zarządzać zależnościami między operacjami i obsługiwać odpowiedzi podczas modyfikowania zasobów w interfejsie Google Ads API, postępuj zgodnie z tymi sprawdzonymi metodami.

Tymczasowe nazwy zasobów

Zarówno GoogleAdsService.Mutate, jak i BatchJobService obsługują tymczasowe nazwy zasobów, do których można się odwoływać w kolejnych operacjach. Dzięki temu możesz utworzyć kampanię oraz powiązane z nią grupy reklam, reklamy i słowa kluczowe w ramach jednego żądania modyfikacji lub zadania wsadowego.

Aby odwołać się do nowo utworzonego zasobu w ramach tego samego żądania modyfikacji lub zadania wsadowego, w polu resource_name nowego zasobu podaj ujemny identyfikator liczby całkowitej (np. -1 lub -2, z wyłączeniem 0). Na przykład podczas tworzenia kampanii w żądaniu zbiorczym ustaw jej nazwę zasobu na customers/CUSTOMER_ID/campaigns/-1. Podczas tworzenia grupy reklam w późniejszej operacji w ramach tego samego żądania odwołaj się do customers/CUSTOMER_ID/campaigns/-1 jako kampanii nadrzędnej. Interfejs API automatycznie zastępuje -1 rzeczywistym identyfikatorem kampanii wygenerowanym podczas tworzenia.

Ograniczenia użytkowania

Podczas korzystania z tymczasowych nazw zasobów pamiętaj o tych regułach:

  • Kolejność ma znaczenie: możesz odwoływać się do tymczasowej nazwy zasobu tylko po jej zdefiniowaniu. Na liście operacji operacja zależna (np. utworzenie grupy reklam) musi występować po operacji, która tworzy jej zasób nadrzędny (np. utworzenie kampanii).
  • Zakres pojedynczego żądania lub zadania wsadowego: tymczasowe nazwy zasobów nie są zachowywane w przypadku oddzielnych zadań ani żądań modyfikacji. Aby odwołać się do zasobu utworzonego w poprzednim zadaniu lub żądaniu modyfikacji, użyj jego rzeczywistej nazwy zasobu wygenerowanej przez system.
  • Globalna unikalność: w ramach jednego zadania lub żądania modyfikacji każda tymczasowa nazwa zasobu musi używać unikalnej ujemnej liczby całkowitej we wszystkich typach zasobów. Na przykład nie możesz przypisać -1 zarówno do kampanii, jak i do grupy reklam w tym samym żądaniu. Ponowne użycie tymczasowego identyfikatora w ramach tego samego żądania lub zadania wsadowego spowoduje zwrócenie błędu NewResourceCreationError.DUPLICATE_TEMP_IDS.

Przykładowy ładunek

Załóżmy, że chcesz dodać kampanię, grupę reklam i reklamę w ramach jednego żądania do interfejsu API lub zadania wsadowego. Możesz utworzyć tablicę mutateOperations w ładunku żądania GoogleAdsService.Mutate lub BatchJobService.AddBatchJobOperations, jak pokazano w tym przykładzie JSON w interfejsie REST (inne wymagane pola zasobu zostały pominięte dla zwięzłości):

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

Ten przykład pokazuje te kluczowe szczegóły:

  • Grupa reklam używa nowego tymczasowego identyfikatora (-2), ponieważ -1 jest już przypisany do kampanii.
  • Grupa reklam odwołuje się do customers/CUSTOMER_ID/campaigns/-1, aby połączyć się z kampanią utworzoną w poprzedniej operacji.
  • Element adGroupAdOperation odwołuje się do customers/CUSTOMER_ID/adGroups/-2 i pomija resourceName, ponieważ żadna kolejna operacja w żądaniu nie odwołuje się do nowej reklamy.

Grupowanie operacji tego samego typu

Gdy używasz GoogleAdsService.Mutate, grupuj operacje według typu zasobu w powtarzającej się tablicy mutate_operations, zachowując zależności między elementami nadrzędnymi i podrzędnymi. Ta metoda sekwencyjnie odczytuje operacje, dopóki nie napotka innego typu zasobu, a następnie łączy wszystkie sąsiadujące operacje tego samego typu w jedno żądanie usługi backendu.

Jeśli na przykład w powtarzającym się polu mutate_operations umieścisz 5 operacji na kampanii, a następnie 10 operacji na grupie reklam, system wykona 2 wywołania backendu: jedno do CampaignService w przypadku 5 operacji na kampanii, a drugie do AdGroupService w przypadku 10 operacji na grupie reklam.

Z kolei przeplatanie operacji przez ich uporządkowanie w postaci [campaign, ad group, campaign, ad group] powoduje 4 oddzielne wywołania backendu. Przeplatane wywołania obniżają wydajność interfejsu API i mogą prowadzić do przekroczenia limitu czasu żądania w przypadku dużych partii.

Obsługa częściowych błędów i limitów partii

Domyślnie GoogleAdsService.Mutate wycofuje całe żądanie, jeśli jakakolwiek operacja się nie powiedzie. Aby zatwierdzić prawidłowe operacje, nawet jeśli inne operacje w tym samym żądaniu się nie powiodą, ustaw partial_failure na true w żądaniu i sprawdź partial_failure_error w odpowiedzi. Gdy partial_failure ma wartość true, jeśli operacja nadrzędna, która definiuje tymczasowy identyfikator (np. customers/CUSTOMER_ID/campaigns/-1), nie przejdzie weryfikacji, wszystkie zależne operacje podrzędne odwołujące się do tego tymczasowego identyfikatora w tym samym żądaniu również się nie powiodą z powodu błędu NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Więcej informacji znajdziesz w przewodniku dotyczącym częściowych błędów.

Pamiętaj też o rozmiarze żądania, podziale na partie i ograniczaniu liczby żądań:

  • Limity rozmiaru żądania i fragmentu: pojedyncze żądanie GoogleAdsService.Mutate ma limit 10 000 operacji modyfikacji (lub do 20 000, jeśli wszystkie operacje w żądaniu to AdGroupCriterionOperation, a w przypadku przekroczenia limitu zwracany jest błąd RequestError.TOO_MANY_MUTATE_OPERATIONS) i co najwyżej 100 operacji działania (RequestError.TOO_MANY_ACTION_OPERATIONS). BatchJobService.AddBatchJobOperations ma limit 10 000 operacji na wywołanie, 10 484 504 bajtów na pojedynczą MutateOperation i 41 937 920 bajtów na AddBatchJobOperationsRequest (w przypadku przekroczenia dowolnego limitu zwracany jest błąd BatchJobError.REQUEST_TOO_LARGE, a łączna liczba operacji na zadanie wsadowe wynosi maksymalnie 1 000 000). Jednoczesne modyfikacje dotyczące tej samej kampanii lub konta mogą powodować błędy DatabaseError.CONCURRENT_MODIFICATION.
  • BatchJobService niepodzielny podział na partie: chociaż zadania wsadowe są wykonywane zgodnie z semantyką częściowych błędów (domyślnie 1000 operacji na wewnętrzną podgrupę), BatchJobService automatycznie grupuje niektóre sąsiadujące operacje zależne dla tego samego identyfikatora nadrzędnego w niepodzielne podgrupy:
    • Operacja AssetGroupOperation (create) poprzedzona sąsiadującymi AssetGroupAssetOperation (create) operacjami dla tego samego AssetGroup ID (łącznie do 1000 operacji, które nie powiodą się niepodzielnie z powodu błędu BatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; każda AssetGroupOperation update lub remove jest wykonywana w osobnej podgrupie z jedną operacją).
    • Operacja Performance Max CampaignOperation (create, gdy włączone są wytyczne dotyczące marki – jest to ustawienie domyślne,chyba że brand_guidelines_enabled ma wartość false lub ustawiono hotel_property_asset_set) poprzedzona sąsiadującymi operacjami CampaignAssetOperation (create) dla tego samego identyfikatora Campaign (łącznie do 1000 operacji, które nie powiodą się niepodzielnie z powodu błędu BatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE).
    • Kolejne AssetGroupListingGroupFilterOperation (max 10,000, które nie powiodą się niepodzielnie z powodu błędu BatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) lub AdGroupCriterionOperation (listing_group, max 20,000, które nie powiodą się niepodzielnie z powodu błędu CriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) operacje dla tego samego elementu nadrzędnego (AssetGroup lub AdGroup).

Pobieranie atrybutów, które można modyfikować, z odpowiedzi

Jeśli ustawisz response_content_type żądania modyfikacji na MUTABLE_RESOURCE, odpowiedź będzie zawierać resource_name i obiekt zasobu wypełniony jego polami mutable (a także kluczowymi polami wypełnionymi przez system w zwracanym zasobie, takimi jak ExperimentArm.in_design_campaigns) dla każdego obsługiwanego obiektu utworzonego lub zaktualizowanego (nie usuniętego) przez żądanie. W przypadku operacji remove lub typów zasobów, które nie obsługują zwracania MUTABLE_RESOURCE, odpowiedź zawsze zawiera tylko resource_name. Dzięki tej funkcji nie musisz wysyłać dodatkowego Search ani SearchStream żądania po każdym wywołaniu modyfikacji.

Jeśli nie ustawisz response_content_type, interfejs Google Ads API domyślnie użyje RESOURCE_NAME_ONLY i zwróci tylko resource_name każdego zmodyfikowanego zasobu.

Ten przykład pokazuje, jak pobrać zasób, który można modyfikować, z wywołania modyfikacji:

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