اتّبِع أفضل الممارسات التالية لتحسين الأداء وإدارة التبعيات في العمليات والتعامل مع الردود عند تعديل الموارد في 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 إضافي بعد كل طلب تعديل.
إذا لم تضبط 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]; }