بهترین شیوه‌ها را تغییر دهید

برای بهینه‌سازی عملکرد، مدیریت وابستگی‌ها در عملیات مختلف و مدیریت پاسخ‌ها هنگام تغییر منابع در API گوگل ادز، از این بهترین شیوه‌ها پیروی کنید.

نام‌های منابع موقت

هر دو GoogleAdsService.Mutate و BatchJobService از نام‌های منبع موقت پشتیبانی می‌کنند که می‌توانند در عملیات بعدی به آنها ارجاع داده شوند. این به شما امکان می‌دهد یک کمپین و گروه‌های تبلیغاتی، تبلیغات و کلمات کلیدی مرتبط با آن را در یک درخواست mutate یا کار دسته‌ای ایجاد کنید.

برای ارجاع به یک منبع تازه ایجاد شده در همان درخواست تغییر یا کار دسته‌ای، یک شناسه عدد صحیح منفی (مانند -1 یا -2 ، به استثنای 0 ) در فیلد resource_name منبع جدید مشخص کنید. برای مثال، هنگام ایجاد یک کمپین در یک درخواست دسته‌ای، نام منبع آن را روی customers/CUSTOMER_ID/campaigns/-1 تنظیم کنید. هنگام ایجاد یک گروه تبلیغاتی در یک عملیات بعدی در همان درخواست، customers/CUSTOMER_ID/campaigns/-1 را به عنوان کمپین والد ارجاع دهید. API به طور خودکار -1 را با شناسه واقعی کمپین ایجاد شده هنگام ایجاد جایگزین می‌کند.

محدودیت‌های استفاده

هنگام استفاده از نام‌های موقت منابع، قوانین زیر را در نظر داشته باشید:

  • اهمیت ترتیب: شما فقط می‌توانید پس از تعریف یک منبع موقت، به آن ارجاع دهید. در فهرست عملیات، عملیات وابسته (مانند ایجاد یک گروه تبلیغاتی) باید پس از عملیاتی که منبع والد خود را ایجاد می‌کند (مانند ایجاد یک کمپین) ظاهر شود.
  • دامنه تک درخواستی یا دسته‌ای: نام‌های منابع موقت در بین کارهای جداگانه یا درخواست‌های تغییر یافته باقی نمی‌مانند. برای ارجاع به منبعی که در یک کار قبلی یا درخواست تغییر یافته ایجاد شده است، از نام منبع واقعی تولید شده توسط سیستم استفاده کنید.
  • منحصر به فرد بودن سراسری: در یک درخواست تغییر یا کار واحد، هر نام منبع موقت باید از یک عدد صحیح منفی منحصر به فرد در تمام انواع منابع استفاده کند. به عنوان مثال، شما نمی‌توانید در یک درخواست واحد -1 را به یک کمپین و یک گروه تبلیغاتی اختصاص دهید. استفاده مجدد از یک شناسه موقت در یک درخواست یا کار دسته‌ای واحد، خطای NewResourceCreationError.DUPLICATE_TEMP_IDS را برمی‌گرداند.

مثال بار مفید

فرض کنید می‌خواهید یک کمپین، یک گروه تبلیغاتی و یک تبلیغ را در یک درخواست API یا کار دسته‌ای اضافه کنید. می‌توانید آرایه 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 گروه‌بندی کنید، ضمن اینکه وابستگی‌های والد و فرزند را نیز در نظر بگیرید. این روش به صورت متوالی عملیات را می‌خواند تا زمانی که با نوع منبع متفاوتی مواجه شود، و سپس تمام عملیات‌های پیوسته از همان نوع را در یک درخواست سرویس backend واحد دسته‌بندی می‌کند.

برای مثال، اگر ۵ عملیات کمپین و به دنبال آن ۱۰ عملیات گروه تبلیغاتی را در فیلد mutate_operations تکرار شده قرار دهید، سیستم دو فراخوانی backend انجام می‌دهد: یکی برای ۵ عملیات کمپین به CampaignService و دومی برای ۱۰ عملیات گروه تبلیغاتی به AdGroupService .

در مقابل، عملیات‌های درهم‌تنیده با مرتب‌سازی آنها به صورت [campaign, ad group, campaign, ad group] منجر به چهار فراخوانی backend جداگانه می‌شود. فراخوانی‌های درهم‌تنیده عملکرد API را کاهش می‌دهند و می‌توانند منجر به وقفه‌های زمانی درخواست در دسته‌های بزرگ شوند.

مدیریت خرابی‌های جزئی و محدودیت‌های دسته‌ای

به طور پیش‌فرض، GoogleAdsService.Mutate در صورت عدم موفقیت هر عملیات، کل درخواست را به حالت اولیه برمی‌گرداند. برای انجام عملیات معتبر حتی در صورت عدم موفقیت سایر عملیات در همان درخواست، partial_failure در درخواست روی true تنظیم کنید و partial_failure_error در پاسخ بررسی کنید. وقتی partial_failure برابر با true باشد، اگر یک عملیات والد که یک شناسه موقت تعریف می‌کند (مانند customers/CUSTOMER_ID/campaigns/-1 ) اعتبارسنجی را با شکست مواجه کند، هر عملیات فرزند وابسته‌ای که به آن شناسه موقت در همان درخواست ارجاع می‌دهد نیز با خطای NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS با شکست مواجه می‌شود. برای جزئیات بیشتر، به راهنمای شکست جزئی مراجعه کنید.

همچنین اندازه درخواست، دسته‌بندی فرعی و محدودیت‌های نرخ را در نظر داشته باشید:

  • محدودیت‌های اندازه درخواست و تکه‌ها: یک درخواست GoogleAdsService.Mutate محدودیت ۱۰،۰۰۰ عملیات جهش (یا تا ۲۰،۰۰۰ عملیات جهش زمانی که همه عملیات در درخواست AdGroupCriterionOperation هستند و در صورت تجاوز از RequestError.TOO_MANY_MUTATE_OPERATIONS را برمی‌گرداند) و حداکثر ۱۰۰ عملیات اکشن ( RequestError.TOO_MANY_ACTION_OPERATIONS ) را اعمال می‌کند. BatchJobService.AddBatchJobOperations حداکثر ۱۰،۰۰۰ عملیات در هر فراخوانی، ۱۰،۴۸۴،۵۰۴ بایت در هر MutateOperation و ۴۱،۹۳۷،۹۲۰ بایت در هر AddBatchJobOperationsRequest را اعمال می‌کند (در صورت تجاوز از هر محدودیتی، BatchJobError.REQUEST_TOO_LARGE را برمی‌گرداند، با حداکثر ۱،۰۰۰،۰۰۰ عملیات در مجموع در هر کار دسته‌ای). جهش‌های همزمان که یک کمپین یا حساب کاربری را هدف قرار می‌دهند، می‌توانند باعث ایجاد خطاهای DatabaseError.CONCURRENT_MODIFICATION شوند.
  • زیردسته‌بندی اتمی BatchJobService : اگرچه کارهای دسته‌ای تحت معنای شکست جزئی (با پیش‌فرض ۱۰۰۰ عملیات در هر زیردسته داخلی) اجرا می‌شوند، BatchJobService به‌طور خودکار عملیات وابسته‌ی پیوسته‌ی خاص را برای شناسه‌ی والد یکسان در زیردسته‌های اتمی گروه‌بندی می‌کند:
    • یک AssetGroupOperation ( create ) و به دنبال آن عملیات‌های پیوسته AssetGroupAssetOperation ( create ) برای همان شناسه AssetGroup (در مجموع تا ۱۰۰۰ عملیات، که به صورت اتمی با BatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE شکست می‌خورند؛ هر update یا remove AssetGroupOperation در یک زیرگروه عملیاتی مستقل اجرا می‌شود).
    • یک Performance Max CampaignOperation ( create ، زمانی که دستورالعمل‌های برند فعال هستند - که پیش‌فرض است مگر اینکه brand_guidelines_enabled روی false تنظیم شده باشد یا hotel_property_asset_set تنظیم شده باشد) و به دنبال آن عملیات پیوسته CampaignAssetOperation ( create ) برای همان شناسه Campaign (تا سقف ۱۰۰۰ عملیات در مجموع، که به صورت اتمی با 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 درخواست mutate خود را روی MUTABLE_RESOURCE تنظیم کنید، پاسخ شامل resource_name و شیء منبعی است که با فیلدهای قابل تغییر آن پر شده است (و همچنین فیلدهای کلیدی سیستمی در منبع برگشتی، مانند ExperimentArm.in_design_campaigns ) برای هر شیء پشتیبانی شده که توسط درخواست ایجاد یا به‌روزرسانی شده (حذف نشده) است. برای عملیات remove - یا برای انواع منابعی که از بازگرداندن MUTABLE_RESOURCE پشتیبانی نمی‌کنند - پاسخ همیشه فقط resource_name را برمی‌گرداند. از این ویژگی برای جلوگیری از ارسال درخواست Search یا SearchStream اضافی پس از هر فراخوانی mutate استفاده کنید.

اگر response_content_type تنظیم نکنید، API گوگل ادز به طور پیش‌فرض روی RESOURCE_NAME_ONLY تنظیم می‌شود و فقط resource_name هر منبع تغییر یافته را برمی‌گرداند.

مثال زیر نحوه بازیابی یک منبع تغییرپذیر از یک فراخوانی mutate را نشان می‌دهد:

جاوا

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

      

سی شارپ

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

پی اچ پی

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

پایتون

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]
      

روبی

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
      

پرل

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

حلقه زدن