أفضل الممارسات المتعلّقة بتعديل البيانات

اتّبِع أفضل الممارسات التالية لتحسين الأداء وإدارة التبعيات في العمليات والتعامل مع الردود عند تعديل الموارد في Google Ads API.

أسماء الموارد المؤقتة

يتيح كلّ من GoogleAdsService.Mutate وBatchJobService استخدام أسماء الموارد المؤقتة التي يمكن الإشارة إليها في العمليات اللاحقة. يتيح لك ذلك إنشاء حملة ومجموعاتها الإعلانية وإعلاناتها وكلماتها الرئيسية المرتبطة بها في طلب تغيير واحد أو مهمة مجمّعة.

للإشارة إلى مورد تم إنشاؤه حديثًا ضمن طلب التغيير أو مهمة الدفعة نفسهما، حدِّد رقم تعريف صحيحًا سالبًا (مثل -1 أو -2، باستثناء 0) في حقل resource_name للمورد الجديد. على سبيل المثال، عند إنشاء حملة في طلب مجمّع، اضبط اسم المورد على customers/CUSTOMER_ID/campaigns/-1. عند إنشاء مجموعة إعلانية في عملية لاحقة ضمن الطلب نفسه، يجب الإشارة إلى customers/CUSTOMER_ID/campaigns/-1 كالحملة الرئيسية. تستبدل واجهة برمجة التطبيقات -1 تلقائيًا برقم تعريف الحملة الفعلي الذي تم إنشاؤه عند الإنشاء.

قيود الاستخدام

يُرجى مراعاة القواعد التالية عند استخدام أسماء الموارد المؤقتة:

  • الترتيب مهم: لا يمكنك الإشارة إلى اسم مورد مؤقت إلا بعد تحديده. في قائمة العمليات، يجب أن تظهر العملية التابعة (مثل إنشاء مجموعة إعلانية) بعد العملية التي تنشئ المورد الرئيسي (مثل إنشاء حملة).
  • نطاق الطلب الواحد أو مهمة الدفعة: لا تظل أسماء الموارد المؤقتة محفوظة في المهام أو طلبات التغيير المنفصلة. للإشارة إلى مورد تم إنشاؤه في مهمة سابقة أو طلب تغيير، استخدِم اسم المورد الفعلي الذي أنشأه النظام.
  • التفرّد على مستوى العالم: ضمن طلب وظيفة أو طلب تغيير واحد، يجب أن يستخدم كل اسم مورد مؤقت عددًا صحيحًا سالبًا فريدًا على مستوى جميع أنواع الموارد. على سبيل المثال، لا يمكنك تعيين -1 لكلّ من الحملة والمجموعة الإعلانية في الطلب نفسه. تؤدي إعادة استخدام معرّف مؤقت ضمن الطلب نفسه أو مهمة الدفعة إلى عرض الخطأ NewResourceCreationError.DUPLICATE_TEMP_IDS.

مثال على الحمولة

لنفترض أنّك تريد إضافة حملة ومجموعة إعلانية وإعلان في طلب واحد من واجهة برمجة التطبيقات أو مهمة مجمّعة. يمكنك تنظيم مصفوفة mutateOperations في حمولة طلب GoogleAdsService.Mutate أو BatchJobService.AddBatchJobOperations كما هو موضّح في مثال REST JSON التالي (مع حذف حقول الموارد الأخرى المطلوبة للإيجاز):

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

يوضّح هذا المثال التفاصيل الرئيسية التالية:

  • تستخدِم المجموعة الإعلانية رقم تعريف مؤقتًا جديدًا (-2) لأنّ -1 سبق أن تم تعيينه للحملة.
  • تشير المجموعة الإعلانية إلى customers/CUSTOMER_ID/campaigns/-1 لربط نفسها بالحملة التي تم إنشاؤها في العملية السابقة.
  • يشير adGroupAdOperation إلى customers/CUSTOMER_ID/adGroups/-2 ويحذف resourceName لأنّه ما مِن عملية لاحقة في الطلب تشير إلى الإعلان الجديد.

تجميع العمليات من النوع نفسه

عند استخدام GoogleAdsService.Mutate، يمكنك تجميع العمليات معًا حسب نوع المورد في مصفوفة mutate_operations المتكرّرة مع مراعاة التبعيات الرئيسية والفرعية. تقرأ هذه الطريقة العمليات بشكل تسلسلي إلى أن تصل إلى نوع مورد مختلف، ثم تجمّع كل العمليات المتجاورة من النوع نفسه في طلب خدمة خلفية واحد.

على سبيل المثال، إذا أدرجت 5 عمليات حملة تليها 10 عمليات مجموعة إعلانية في الحقل المتكرّر mutate_operations، سينفّذ النظام طلبَين في الخلفية: أحدهما إلى CampaignService لعمليات الحملة الخمس، والآخر إلى AdGroupService لعمليات المجموعة الإعلانية العشر.

في المقابل، يؤدي تداخل العمليات من خلال ترتيبها على النحو [campaign, ad group, campaign, ad group] إلى أربع طلبات منفصلة في الخلفية. تؤدي الطلبات المتداخلة إلى تدهور أداء واجهة برمجة التطبيقات، وقد تؤدي إلى انتهاء مهلة الطلبات في الدفعات الكبيرة.

التعامل مع حالات الفشل الجزئي وحدود الدفعات

بشكل تلقائي، يتراجع GoogleAdsService.Mutate عن الطلب بالكامل في حال تعذُّر تنفيذ أي عملية واحدة. لتنفيذ العمليات الصالحة حتى في حال تعذُّر تنفيذ عمليات أخرى في الطلب نفسه، اضبط partial_failure على true في الطلب وافحص partial_failure_error في الردّ. عندما تكون قيمة partial_failure هي true، إذا تعذّر التحقّق من صحة عملية رئيسية تحدّد معرّفًا مؤقتًا (مثل customers/CUSTOMER_ID/campaigns/-1)، ستتعذّر أيضًا أي عمليات فرعية تابعة تشير إلى هذا المعرّف المؤقت في الطلب نفسه، وسيظهر الخطأ NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. لمزيد من التفاصيل، يُرجى الاطّلاع على دليل الفشل الجزئي.

يجب أيضًا مراعاة حجم الطلب والتقسيم إلى دفعات فرعية وحدود المعدّل:

  • الحدود القصوى لحجم الطلب والتقسيم: يفرض طلب GoogleAdsService.Mutate واحد حدًا أقصى يبلغ 10,000 عملية تعديل (أو ما يصل إلى 20,000 عملية عندما تكون جميع العمليات في الطلب من نوع AdGroupCriterionOperation، مع عرض الخطأ RequestError.TOO_MANY_MUTATE_OPERATIONS في حال تجاوز هذا الحدّ) و100 عملية إجراء (RequestError.TOO_MANY_ACTION_OPERATIONS) كحدّ أقصى. يفرض BatchJobService.AddBatchJobOperations حدًا أقصى يبلغ 10,000 عملية لكل طلب، و10,484,504 بايت لكل MutateOperation فردي، و41,937,920 بايت لكل AddBatchJobOperationsRequest (مع عرض الخطأ BatchJobError.REQUEST_TOO_LARGE في حال تجاوز أي حدّ، مع ما يصل إلى 1,000,000 عملية إجمالية لكل مهمة مجمّعة). يمكن أن تؤدي التغييرات المتزامنة التي تستهدف الحملة أو الحساب نفسهما إلى حدوث أخطاء DatabaseError.CONCURRENT_MODIFICATION.
  • BatchJobService تجميع فرعي ذري: على الرغم من تنفيذ مهام الدفعات وفقًا لدلالات الفشل الجزئي (يتم ضبطها تلقائيًا على 1,000 عملية لكل دفعة فرعية داخلية)، فإنّ BatchJobService يجمع تلقائيًا عمليات تابعة متجاورة معيّنة لرقم تعريف رئيسي نفسه في دفعات فرعية ذرية:
    • AssetGroupOperation (create) متبوعًا بعمليات AssetGroupAssetOperation (create) متجاورة لمعرّف AssetGroup نفسه (ما يصل إلى 1,000 عملية إجمالاً، مع تعذُّر التنفيذ بشكل ذري باستخدام BatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE؛ يتم تنفيذ كل AssetGroupOperation update أو remove في مجموعة فرعية مستقلة من عملية واحدة).
    • عملية CampaignOperation في "حملات الأداء الأفضل" (create، عند تفعيل إرشادات العلامة التجارية، وهو الإعداد التلقائي ما لم يتم ضبط brand_guidelines_enabled على false أو ضبط hotel_property_asset_set)، تليها عمليات CampaignAssetOperation (create) متجاورة لرقم تعريف Campaign نفسه (ما يصل إلى 1,000 عملية في المجموع، مع تعذّر تنفيذها بشكل كامل في حال حدوث خطأ BatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE).
    • عمليات متتالية AssetGroupListingGroupFilterOperation (max 10,000، مع تعذُّر التنفيذ بشكل ذري مع BatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) أو AdGroupCriterionOperation (listing_group، max 20,000، مع تعذُّر التنفيذ بشكل ذري مع CriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) للعنصر الأصلي نفسه (AssetGroup أو AdGroup).

استرداد السمات القابلة للتغيير من الردّ

إذا ضبطت قيمة response_content_type في طلب التعديل على MUTABLE_RESOURCE، سيتضمّن الردّ resource_name وكائن المرجع الذي تم ملء حقوله القابلة للتعديل (بالإضافة إلى الحقول الرئيسية التي يملؤها النظام في المرجع الذي تم عرضه، مثل ExperimentArm.in_design_campaigns) لكل كائن متوافق تم إنشاؤه أو تعديله (وليس إزالته) من خلال الطلب. بالنسبة إلى عمليات remove أو أنواع الموارد التي لا تتيح عرض MUTABLE_RESOURCE، يعرض الرد دائمًا resource_name فقط. استخدِم هذه الميزة لتجنُّب إرسال طلب Search أو SearchStream إضافي بعد كل طلب تعديل.

إذا لم تضبط response_content_type، ستستخدم Google Ads API تلقائيًا RESOURCE_NAME_ONLY وستعرض فقط resource_name لكل مورد تم تعديله.

يوضّح المثال التالي كيفية استرداد مورد قابل للتغيير من طلب تغيير:

جافا

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