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á liên quan trong một yêu cầu thay đổi tài nguyên hoặc công việc theo lô.
Để 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 công việc theo lô, 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 chiến dịch trong một yêu cầu theo lô, 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 gốc. API sẽ tự động thay thế -1 bằng mã chiến dịch thực tế được tạo khi tạo.
Các quy tắc ràng buộc về 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ự rấ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 nhóm quảng cáo) phải xuất hiện sau thao tác tạo tài nguyên gốc (chẳng hạn như tạo chiến dịch).
- Phạm vi yêu cầu đơn lẻ hoặc công việc theo lô: Tên tài nguyên tạm thời không tồn tại trong các công việc hoặc yêu cầu thay đổi tài nguyên riêng biệt. Để tham chiếu một tài nguyên được tạo trong một 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 tế do hệ thống tạo.
- Tính duy nhất trên toàn hệ thống: Trong một yêu cầu thay đổi tài nguyên hoặc một công việc, 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 công việc theo lô sẽ trả về lỗiNewResourceCreationError.DUPLICATE_TEMP_IDS.
Phần tải trọng mẫu
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ể định 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ề REST JSON sau đây (với các trường tài nguyên bắt buộc khác được 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 tuần tự các thao tác 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ó 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, sau đó là 10 thao tác nhóm quảng cáo trong trường mutate_operations lặp lại, thì hệ thống sẽ thực hiện hai 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 đến 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 việc 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à giới hạn 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 phân nhóm phụ và giới hạn số lượng yêu cầu:
- Giới hạn về kích thước yêu cầu và phân đoạn: Một yêu cầu
GoogleAdsService.Mutateduy nhất áp dụng giới hạn là 10.000 thao tác thay đổi (hoặc tối đa 20.000 khi tất cả thao tác trong yêu cầ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ỗiMutateOperationriêng lẻ và 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ố thao tác tối đa là 1.000.000 cho mỗi công việc theo lô). 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ể gây ra lỗiDatabaseError.CONCURRENT_MODIFICATION. BatchJobServicephân lô phụ nguyên tử: Mặc dù các công việc theo lô thực thi theo ngữ nghĩa lỗi một phần (mặc định là 1.000 thao tác cho mỗi lô phụ nội bộ), nhưngBatchJobServicesẽ tự động nhóm một số thao tác phụ thuộc liền kề cho cùng một mã mẹ vào các lô phụ nguyên tử:- Một thao tác
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ực hiện được theo nguyên tử vớiBatchJobError.ASSET_GROUP_AND_ASSET_GROUP_ASSET_TRANSACTION_FAILURE; mỗiAssetGroupOperationupdatehoặcremovethực thi trong một lô phụ thao tác đơn lẻ độc lập). - Một thao tác
CampaignOperationcủa chiế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 kề 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 về mặt 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 thao tác
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 biến đổi 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. Hãy sử dụng tính năng này để tránh gửi thêm yêu cầu Search hoặc SearchStream sau mỗi lệnh gọi thay đổi.
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 đã 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]; }