اتّبِع أفضل الممارسات التالية لتحسين الأداء وإدارة التبعيات في العمليات والتعامل مع الردود عند تعديل الموارد في 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، ويتم تنفيذ كل عمليةAssetGroupOperationupdateأو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 إضافي بعد كل طلب mutate.
إذا لم تضبط 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.V26.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| # In standard campaign experiments, creating the treatment arm automatically # generates a draft campaign 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 second, it will be the second 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::V26::Services::ExperimentArmService::ExperimentArmOperation ->new({ create => Google::Ads::GoogleAds::V26::Resources::ExperimentArm->new({ # The "control" arm references an already-existing campaign. control => "true", campaigns => [ Google::Ads::GoogleAds::V26::Utils::ResourceNames::campaign( $customer_id, $base_campaign_id ) ], experiment => $experiment, name => "control arm", trafficSplit => 40 })}); push @$operations, Google::Ads::GoogleAds::V26::Services::ExperimentArmService::ExperimentArmOperation ->new({ create => Google::Ads::GoogleAds::V26::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]; }