Hãy làm theo các phương pháp hay nhất này để tối ưu hoá hiệu suất, quản lý các phần phụ thuộc trên nhiều thao tác và xử lý các phản hồi khi biến đổi tài nguyên trong Google Ads API.
Tên tài nguyên tạm thời
Cả GoogleAdsService.Mutate và BatchJobService đều hỗ trợ tên tài nguyên tạm thời có thể được tham chiếu trong các thao tác tiếp theo. Nhờ đó, bạn có thể tạo một chiến dịch và các nhóm quảng cáo, quảng cáo và từ khoá được liên kết trong một yêu cầu thay đổi tài nguyên hoặc công việc hàng loạt.
Để tham chiếu đến một tài nguyên mới tạo trong cùng một yêu cầu thay đổi tài nguyên hoặc lô công việc, hãy chỉ định một mã nhận dạng số nguyên âm (chẳng hạn như -1 hoặc -2, ngoại trừ 0) trong trường resource_name của tài nguyên mới. Ví dụ: khi tạo một chiến dịch trong yêu cầu hàng loạt, hãy đặt tên tài nguyên của chiến dịch thành customers/CUSTOMER_ID/campaigns/-1.
Khi tạo một nhóm quảng cáo trong một thao tác sau đó trong cùng một yêu cầu, hãy tham chiếu customers/CUSTOMER_ID/campaigns/-1 làm chiến dịch mẹ. API này sẽ tự động thay thế -1 bằng mã chiến dịch thực tế được tạo khi tạo chiến dịch.
Các quy tắc ràng buộc đối với việc sử dụng
Khi sử dụng tên tài nguyên tạm thời, hãy lưu ý các quy tắc sau:
- Thứ tự quan trọng: Bạn chỉ có thể tham chiếu tên tài nguyên tạm thời sau khi xác định tên đó. Trong danh sách các thao tác, thao tác phụ thuộc (chẳng hạn như tạo một nhóm quảng cáo) phải xuất hiện sau thao tác tạo tài nguyên mẹ (chẳng hạn như tạo một chiến dịch).
- Phạm vi yêu cầu đơn lẻ hoặc công việc hàng loạt: Tên tài nguyên tạm thời không duy trì trên các yêu cầu riêng biệt hoặc yêu cầu biến đổi. Để tham chiếu đến một tài nguyên được tạo trong một yêu cầu công việc hoặc yêu cầu thay đổi tài nguyên trước đó, hãy sử dụng tên tài nguyên thực do hệ thống tạo.
- Tính duy nhất trên toàn cầu: Trong một yêu cầu duy nhất về công việc hoặc yêu cầu thay đổi, mỗi tên tài nguyên tạm thời phải sử dụng một số nguyên âm duy nhất trên tất cả các loại tài nguyên.
Ví dụ: bạn không thể chỉ định
-1cho cả chiến dịch và nhóm quảng cáo trong cùng một yêu cầu. Việc sử dụng lại mã tạm thời trong cùng một yêu cầu hoặc lệnh hàng loạt sẽ trả về lỗiNewResourceCreationError.DUPLICATE_TEMP_IDS.
Ví dụ về tải trọng
Giả sử bạn muốn thêm một chiến dịch, một nhóm quảng cáo và một quảng cáo trong một yêu cầu API hoặc lô công việc duy nhất. Bạn có thể cấu trúc mảng mutateOperations trong tải trọng yêu cầu GoogleAdsService.Mutate hoặc BatchJobService.AddBatchJobOperations như trong ví dụ về JSON REST sau đây (các trường tài nguyên bắt buộc khác bị bỏ qua để cho ngắn gọn):
{
"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"
}
}
}
]
}
Ví dụ này minh hoạ các thông tin chi tiết chính sau:
- Nhóm quảng cáo sử dụng một mã tạm thời mới (
-2) vì-1đã được chỉ định cho chiến dịch. - Nhóm quảng cáo tham chiếu
customers/CUSTOMER_ID/campaigns/-1để liên kết chính nó với chiến dịch được tạo trong thao tác trước đó. adGroupAdOperationtham chiếu đếncustomers/CUSTOMER_ID/adGroups/-2và bỏ quaresourceNamevì không có thao tác nào tiếp theo trong yêu cầu tham chiếu đến quảng cáo mới.
Nhóm các thao tác cùng loại
Khi sử dụng GoogleAdsService.Mutate, hãy nhóm các thao tác lại với nhau theo loại tài nguyên trong mảng mutate_operations lặp lại trong khi vẫn tuân thủ các phần phụ thuộc mẹ và con. Phương thức này đọc các thao tác theo trình tự cho đến khi gặp một loại tài nguyên khác, sau đó gộp tất cả các thao tác liền kề cùng loại vào một yêu cầu dịch vụ phụ trợ duy nhất.
Ví dụ: nếu bạn thêm 5 thao tác chiến dịch, theo sau là 10 thao tác nhóm quảng cáo vào trường mutate_operations lặp lại, thì hệ thống sẽ thực hiện 2 lệnh gọi phụ trợ: một lệnh gọi đến CampaignService cho 5 thao tác chiến dịch và một lệnh gọi thứ hai đến AdGroupService cho 10 thao tác nhóm quảng cáo.
Ngược lại, việc xen kẽ các thao tác bằng cách sắp xếp chúng dưới dạng [campaign, ad group,
campaign, ad group] sẽ dẫn đến 4 lệnh gọi riêng biệt ở phần phụ trợ. Các lệnh gọi xen kẽ làm giảm hiệu suất API và có thể dẫn đến hết thời gian chờ yêu cầu đối với các lô lớn.
Xử lý lỗi một phần và hạn mức theo lô
Theo mặc định, GoogleAdsService.Mutate sẽ khôi phục toàn bộ yêu cầu nếu có bất kỳ thao tác nào không thành công. Để thực hiện các thao tác hợp lệ ngay cả khi các thao tác khác trong cùng một yêu cầu không thành công, hãy đặt partial_failure thành true trong yêu cầu và kiểm tra partial_failure_error trong phản hồi. Khi partial_failure là true, nếu một thao tác gốc xác định mã tạm thời (chẳng hạn như customers/CUSTOMER_ID/campaigns/-1) không xác thực được, thì mọi thao tác con phụ thuộc tham chiếu đến mã tạm thời đó trong cùng một yêu cầu cũng sẽ không thành công với NewResourceCreationError.TEMP_ID_RESOURCE_HAD_ERRORS. Để biết thêm thông tin chi tiết, hãy xem hướng dẫn về lỗi một phần.
Ngoài ra, hãy lưu ý đến kích thước yêu cầu, việc chia thành các lô nhỏ và giới hạn về tốc độ:
- Giới hạn về yêu cầu và kích thước khối: Một yêu cầu
GoogleAdsService.Mutateduy nhất áp dụng giới hạn là 10.000 thao tác biến đổi (hoặc tối đa 20.000 khi tất cả các thao tác trong yêu cầu đều làAdGroupCriterionOperation, trả vềRequestError.TOO_MANY_MUTATE_OPERATIONSnếu vượt quá) và tối đa 100 thao tác hành động (RequestError.TOO_MANY_ACTION_OPERATIONS).BatchJobService.AddBatchJobOperationsáp dụng tối đa 10.000 thao tác cho mỗi lệnh gọi, 10.484.504 byte cho mỗiMutateOperationvà 41.937.920 byte cho mỗiAddBatchJobOperationsRequest(trả vềBatchJobError.REQUEST_TOO_LARGEnếu vượt quá bất kỳ giới hạn nào, với tổng số tối đa 1.000.000 thao tác cho mỗi công việc hàng loạt). Các đột biến đồng thời nhắm đến cùng một chiến dịch hoặc tài khoản có thể kích hoạt lỗiDatabaseError.CONCURRENT_MODIFICATION. BatchJobServicephân chia lô con dạng nguyên tử: Mặc dù các lô công việc thực thi theo ngữ nghĩa thất bại một phần (mặc định là 1.000 thao tác cho mỗi lô con nội bộ), nhưngBatchJobServicesẽ tự động nhóm một số thao tác phụ thuộc liên tục cho cùng một mã nhận dạng mẹ thành các lô con dạng nguyên tử:- Một
AssetGroupOperation(create) theo sau là các thao tácAssetGroupAssetOperation(create) liền kề cho cùng một mã nhận dạngAssetGroup(tổng cộng tối đa 1.000 thao tác, không thành công theo cách nguyên tử vớiBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; mỗiAssetGroupOperationupdatehoặcremovethực thi trong một lô con gồm một thao tác độc lập). - Một
CampaignOperationchiến dịch Tối đa hoá hiệu suất (create, khi Nguyên tắc sử dụng thương hiệu được bật – đây là chế độ mặc định trừ phibrand_guidelines_enabledđược đặt thànhfalsehoặchotel_property_asset_setđược đặt) theo sau là các thao tácCampaignAssetOperation(create) liên tục cho cùng một mã nhận dạngCampaign(tổng cộng tối đa 1.000 thao tác, không thành công theo cách thức nguyên tử vớiBatchJobError.CAMPAIGN_AND_CAMPAIGN_ASSET_TRANSACTION_FAILURE). - Các thao tác
AssetGroupListingGroupFilterOperationliên tiếp (max 10,000, không thành công theo cách thức nguyên tử vớiBatchJobError.ASSET_GROUP_LISTING_GROUP_FILTER_TRANSACTION_FAILURE) hoặcAdGroupCriterionOperation(listing_group,max 20,000, không thành công theo cách thức nguyên tử vớiCriterionError.LISTING_GROUP_ERROR_IN_ANOTHER_OPERATION) cho cùng một phần tử mẹ (AssetGrouphoặcAdGroup).
- Một
Truy xuất các thuộc tính có thể thay đổi từ phản hồi
Nếu bạn đặt response_content_type của yêu cầu thay đổi tài nguyên thành MUTABLE_RESOURCE, thì phản hồi sẽ chứa resource_name và đối tượng tài nguyên được điền sẵn các trường có thể thay đổi (cũng như các trường khoá do hệ thống điền sẵn trên tài nguyên được trả về, chẳng hạn như ExperimentArm.in_design_campaigns) cho mọi đối tượng được hỗ trợ mà yêu cầu đã tạo hoặc cập nhật (không bị xoá). Đối với các thao tác remove hoặc đối với các loại tài nguyên không hỗ trợ việc trả về MUTABLE_RESOURCE, phản hồi luôn chỉ trả về resource_name. Sử dụng tính năng này để tránh gửi yêu cầu Search hoặc SearchStream bổ sung sau mỗi lệnh gọi đột biến.
Nếu bạn không đặt response_content_type, thì Google Ads API sẽ mặc định là RESOURCE_NAME_ONLY và chỉ trả về resource_name của mỗi tài nguyên bị thay đổi.
Ví dụ sau đây minh hoạ cách truy xuất một tài nguyên có thể thay đổi từ một lệnh gọi đột biến:
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]; }