经常更新商品

借助 Products 子 API,您可以对现有商品进行部分更新。 这非常适合经常变化的数据(例如价格和库存状况),因为这样一来,您就不必因小幅更改而重新提交整个商品。不过,您应定期重新插入商品,以确保所有商品数据保持同步。

本指南介绍了如何使用 productinputs.patch 方法更新商品。

前提条件

在更新商品之前,您需要满足以下条件:

更新特定商品详情

如需更改商品的某些详细信息(例如价格或供应情况),而无需重新提交其所有信息,请使用 productInputs.patch 方法。

您可以在 updateMask 参数中指定要更改的字段。updateMask 是您要更新的字段的英文逗号分隔列表。patch 方法的行为如下:

  • updateMask 和正文中的字段:这些字段会更新为新值。
  • updateMask 中有但正文中没有的字段:这些字段已从商品输入中删除。
  • 不在 updateMask 中的字段:这些字段保持不变。
  • 省略 updateMask 参数:系统会更新请求正文中提供的所有字段。请求正文中未提供的字段不会从商品输入中删除。

以下是更新前的商品数据示例:

{
  "name": "accounts/{ACCOUNT_ID}/productInputs/en~US~SKU12345",
  "product": "accounts/{ACCOUNT_ID}/products/en~US~SKU12345",
  "offerId": "SKU12345",
  "contentLanguage": "en",
  "feedLabel": "US",
  "productAttributes": {
    "title": "Classic Cotton T-Shirt",
    "description": "A comfortable, durable, and stylish t-shirt made from 100% cotton.",
    "link": "https://www.example.com/p/SKU12345",
    "availability": "IN_STOCK",
    "price": {
      "amountMicros": "15990000",
      "currencyCode": "USD"
    },
    "condition": "NEW",
    "gtins": [
      "9780007350896"
    ],
    "imageLink": "https://www.example.com/image/SKU12345"
  }
}

此示例更新了商品的 titleavailability,并删除了其 imageLinkdescriptionprice 不在 updateMask 中,因此将保持不变。

PATCH https://merchantapi.googleapis.com/products/v1/accounts/{ACCOUNT_ID}/productInputs/en~US~SKU12345?updateMask=productAttributes.title,productAttributes.availability,productAttributes.imageLink&dataSource=accounts/{ACCOUNT_ID}/dataSources/{DATASOURCE_ID}
{
 "productAttributes": {
   "title": "Classic Cotton T-Shirt - New Edition",
   "availability": "OUT_OF_STOCK",
    "description": "A comfortable T-shirt from premium cotton, newer edition.",
    "price": {
      "amountMicros": "9990000",
      "currencyCode": "USD"
    }
 }
}

成功调用会返回更新后的 ProductInput 资源。titleavailability 已更新,imageLink 已移除,因为它位于 updateMask 中,但不在请求正文中。由于 descriptionprice 未列在 updateMask 中,因此它们保持不变。

{
  "name": "accounts/{ACCOUNT_ID}/productInputs/en~US~SKU12345",
  "product": "accounts/{ACCOUNT_ID}/products/en~US~SKU12345",
  "offerId": "SKU12345",
  "contentLanguage": "en",
  "feedLabel": "US",
  "productAttributes": {
    "title": "Classic Cotton T-Shirt - New Edition",
    "description": "A comfortable, durable, and stylish t-shirt made from 100% cotton.",
    "link": "https://www.example.com/p/SKU12345",
    "availability": "OUT_OF_STOCK",
    "price": {
      "amountMicros": "15990000",
      "currencyCode": "USD"
    },
    "condition": "NEW",
    "gtins": [
      "9780007350896"
    ],
  }
}

以下代码示例展示了如何更新商品。

Java

import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.protobuf.FieldMask;
import com.google.shopping.merchant.datasources.v1.DataSourceName;
import com.google.shopping.merchant.products.v1.Availability;
import com.google.shopping.merchant.products.v1.Condition;
import com.google.shopping.merchant.products.v1.ProductAttributes;
import com.google.shopping.merchant.products.v1.ProductInput;
import com.google.shopping.merchant.products.v1.ProductInputName;
import com.google.shopping.merchant.products.v1.ProductInputsServiceClient;
import com.google.shopping.merchant.products.v1.ProductInputsServiceSettings;
import com.google.shopping.merchant.products.v1.UpdateProductInputRequest;
import com.google.shopping.type.CustomAttribute;
import shopping.merchant.samples.utils.Authenticator;
import shopping.merchant.samples.utils.Config;

/** This class demonstrates how to update a product input */
public class UpdateProductInputSample {

  public static void updateProductInput(Config config, String productId, String dataSourceId)
      throws Exception {

    // Obtains OAuth token based on the user's configuration.
    GoogleCredentials credential = new Authenticator().authenticate();

    // Creates service settings using the credentials retrieved above.
    ProductInputsServiceSettings productInputsServiceSettings =
        ProductInputsServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(credential))
            .build();

    // Creates product name to identify product.
    String name =
        ProductInputName.newBuilder()
            .setAccount(config.getAccountId().toString())
            .setProductinput(productId)
            .build()
            .toString();

    // Just productAttributes and customAttributes can be updated
    FieldMask fieldMask =
        FieldMask.newBuilder()
            .addPaths("product_attributes.title")
            .addPaths("product_attributes.description")
            .addPaths("product_attributes.link")
            .addPaths("product_attributes.image_link")
            .addPaths("product_attributes.availability")
            .addPaths("product_attributes.condition")
            .addPaths("product_attributes.gtins")
            .addPaths("custom_attributes.mycustomattribute")
            .build();

    // Calls the API and catches and prints any network failures/errors.
    try (ProductInputsServiceClient productInputsServiceClient =
        ProductInputsServiceClient.create(productInputsServiceSettings)) {

      ProductAttributes attributes =
          ProductAttributes.newBuilder()
              .setTitle("A Tale of Two Cities")
              .setDescription("A classic novel about the French Revolution")
              .setLink("https://exampleWebsite.com/tale-of-two-cities.html")
              .setImageLink("https://exampleWebsite.com/tale-of-two-cities.jpg")
              .setAvailability(Availability.IN_STOCK)
              .setCondition(Condition.NEW)
              .addGtins("9780007350896")
              .build();

      // The datasource can be either a primary or supplemental datasource.
      String dataSource =
          DataSourceName.newBuilder()
              .setAccount(config.getAccountId().toString())
              .setDatasource(dataSourceId)
              .build()
              .toString();

      UpdateProductInputRequest request =
          UpdateProductInputRequest.newBuilder()
              .setUpdateMask(fieldMask)
              // You can only update product attributes and custom_attributes
              .setDataSource(dataSource)
              .setProductInput(
                  ProductInput.newBuilder()
                      .setName(name)
                      .setProductAttributes(attributes)
                      .addCustomAttributes(
                          CustomAttribute.newBuilder()
                              .setName("mycustomattribute")
                              .setValue("Example value")
                              .build())
                      .build())
              .build();

      System.out.println("Sending update ProductInput request");
      ProductInput response = productInputsServiceClient.updateProductInput(request);
      System.out.println("Updated ProductInput Name below");
      // The last part of the product name will be the product ID assigned to a product by Google.
      // Product ID has the format `contentLanguage~feedLabel~offerId`
      System.out.println(response.getName());
      System.out.println("Updated Product below");
      System.out.println(response);
    } catch (Exception e) {
      System.out.println(e);
    }
  }

  public static void main(String[] args) throws Exception {
    Config config = Config.load();
    // An ID assigned to a product by Google. In the format
    // contentLanguage~feedLabel~offerId
    String productId = "en~label~sku123"; // Replace with your product ID.

    // Identifies the data source that will own the product input.
    String dataSourceId = "{INSERT_DATASOURCE_ID}"; // Replace with your datasource ID.

    updateProductInput(config, productId, dataSourceId);
  }
}

PHP

use Google\ApiCore\ApiException;
use Google\Protobuf\FieldMask;
use Google\Shopping\Merchant\Products\V1\Availability;
use Google\Shopping\Merchant\Products\V1\Condition;
use Google\Shopping\Merchant\Products\V1\ProductAttributes;
use Google\Shopping\Merchant\Products\V1\Client\ProductInputsServiceClient;
use Google\Shopping\Merchant\Products\V1\ProductInput;
use Google\Shopping\Merchant\Products\V1\UpdateProductInputRequest;
use Google\Shopping\Type\CustomAttribute;

/**
 * This class demonstrates how to update a product input.
 */
class UpdateProductInputSample
{
    // An ID assigned to a product by Google. In the format
    // contentLanguage~feedLabel~offerId
    // Please ensure this product ID exists for the update to succeed.
    private const PRODUCT_ID = "online~en~label~sku123";

    // Identifies the data source that will own the product input.
    // Please ensure this data source ID exists.
    private const DATASOURCE_ID = "<INSERT_DATASOURCE_ID>";

    /**
     * Helper function to construct the full product input resource name.
     *
     * @param string $accountId The merchant account ID.
     * @param string $productInputId The product input ID (e.g., "online~en~label~sku123").
     * @return string The full product input resource name.
     */
    private static function getProductInputName(string $accountId, string $productInputId): string
    {
        return sprintf("accounts/%s/productInputs/%s", $accountId, $productInputId);
    }

    /**
     * Helper function to construct the full data source resource name.
     *
     * @param string $accountId The merchant account ID.
     * @param string $dataSourceId The data source ID.
     * @return string The full data source resource name.
     */
    private static function getDataSourceName(string $accountId, string $dataSourceId): string
    {
        return sprintf("accounts/%s/dataSources/%s", $accountId, $dataSourceId);
    }

    /**
     * Updates an existing product input in your Merchant Center account.
     *
     * @param array $config The configuration array containing the account ID.
     * @param string $productId The ID of the product input to update.
     * @param string $dataSourceId The ID of the data source.
     */
    public static function updateProductInput(
        array $config,
        string $productId,
        string $dataSourceId
    ): void {
        // Gets the OAuth credentials to make the request.
        $credentials = Authentication::useServiceAccountOrTokenFile();

        // Creates options config containing credentials for the client to use.
        $options = ['credentials' => $credentials];

        // Creates a ProductInputsServiceClient.
        $productInputsServiceClient = new ProductInputsServiceClient($options);

        // Construct the full resource name of the product input to be updated.
        $name = self::getProductInputName($config['accountId'], $productId);

        // Define the FieldMask to specify which fields to update.
        // Only 'attributes' and 'custom_attributes' can be specified in the
        // FieldMask for product input updates.
        $fieldMask = new FieldMask([
            'paths' => [
                "product_attributes.title",
                "product_attributes.description",
                "product_attributes.link",
                "product_attributes.image_link",
                "product_attributes.availability",
                "product_attributes.condition",
                "product_attributes.gtin",
                "custom_attributes.mycustomattribute" // Path for a specific custom attribute
            ]
        ]);

        // Calls the API and handles any network failures or errors.
        try {
            // Define the new attributes for the product.
            $attributes = new ProductAttributes([
                'title' => 'A Tale of Two Cities 3',
                'description' => 'A classic novel about the French Revolution',
                'link' => 'https://exampleWebsite.com/tale-of-two-cities.html',
                'image_link' => 'https://exampleWebsite.com/tale-of-two-cities.jpg',
                'availability' => Availability::IN_STOCK,
                'condition' => Condition::PBNEW,
                'gtins' => ['9780007350896'] // GTIN is a repeated field.
            ]);

            // Construct the full data source name.
            // This specifies the data source context for the update.
            $dataSource = self::getDataSourceName($config['accountId'], $dataSourceId);

            // Create the ProductInput object with the desired updates.
            // The 'name' field must match the product input being updated.
            $productInput = new ProductInput([
                'name' => $name,
                'product_attributes' => $attributes,
                'custom_attributes' => [ // Provide the list of custom attributes.
                    new CustomAttribute([
                        'name' => 'mycustomattribute',
                        'value' => 'Example value'
                    ])
                ]
            ]);

            // Create the UpdateProductInputRequest.
            $request = new UpdateProductInputRequest([
                'update_mask' => $fieldMask,
                'data_source' => $dataSource,
                'product_input' => $productInput
            ]);

            print "Sending update ProductInput request\n";
            // Make the API call to update the product input.
            $response = $productInputsServiceClient->updateProductInput($request);

            print "Updated ProductInput Name below\n";
            // The name of the updated product input.
            // The last part of the product name is the product ID (e.g., contentLanguage~feedLabel~offerId).
            print $response->getName() . "\n";
            print "Updated Product below\n";
            // Print the full updated product input object.
            print_r($response);

        } catch (ApiException $e) {
            printf("ApiException caught: %s\n", $e->getMessage());
        }
    }

    /**
     * Executes the UpdateProductInput sample.
     */
    public function callSample(): void
    {
        $config = Config::generateConfig();
        $productId = self::PRODUCT_ID;
        $dataSourceId = self::DATASOURCE_ID;

        self::updateProductInput($config, $productId, $dataSourceId);
    }
}

// Run the script.
$sample = new UpdateProductInputSample();
$sample->callSample();

Python

"""A module to update a product input."""

from examples.authentication import configuration
from examples.authentication import generate_user_credentials
from google.protobuf import field_mask_pb2
from google.shopping.merchant_products_v1 import Availability
from google.shopping.merchant_products_v1 import Condition
from google.shopping.merchant_products_v1 import ProductAttributes
from google.shopping.merchant_products_v1 import ProductInput
from google.shopping.merchant_products_v1 import ProductInputsServiceClient
from google.shopping.merchant_products_v1 import UpdateProductInputRequest
from google.shopping.type import CustomAttribute


# Fetches the Merchant Center account ID from the authentication examples.
# This ID is needed to construct resource names for the API.
_ACCOUNT_ID = configuration.Configuration().read_merchant_info()


def update_product_input(account_id: str, product_id: str, data_source_id: str):
  """Updates an existing product input for a specific account.

  Args:
    account_id: The Merchant Center account ID.
    product_id: The ID of the product input to update. This ID is assigned by
      Google and has the format `contentLanguage~feedLabel~offerId`.
    data_source_id: The ID of the data source that owns the product input.
  """

  # Obtains OAuth credentials for authentication.
  credentials = generate_user_credentials.main()

  # Creates a ProductInputsServiceClient instance.
  client = ProductInputsServiceClient(credentials=credentials)

  # Constructs the full resource name for the product input.
  # Format: accounts/{account}/productInputs/{productinput}
  name = f"accounts/{account_id}/productInputs/{product_id}"

  # Defines the FieldMask to specify which fields of the product input
  # are being updated. Only 'attributes' and 'custom_attributes' can be updated.
  field_mask = field_mask_pb2.FieldMask(
      paths=[
          "product_attributes.title",
          "product_attributes.description",
          "product_attributes.link",
          "product_attributes.image_link",
          "product_attributes.availability",
          "product_attributes.condition",
          "product_attributes.gtins",
          "custom_attributes.mycustomattribute",
      ]
  )

  # Prepares the new attribute values for the product.
  attributes = ProductAttributes(
      title="A Tale of Two Cities updated",
      description="A classic novel about the French Revolution",
      link="https://exampleWebsite.com/tale-of-two-cities.html",
      image_link="https://exampleWebsite.com/tale-of-two-cities.jpg",
      availability=Availability.IN_STOCK,
      condition=Condition.NEW,
      gtins=["9780007350896"],  # GTIN is a repeated field.
  )

  # Constructs the full resource name for the data source.
  # The data source can be primary or supplemental.
  # Format: accounts/{account}/dataSources/{datasource}
  data_source = f"accounts/{account_id}/dataSources/{data_source_id}"

  # Prepares the ProductInput object with the updated information.
  product_input_data = ProductInput(
      name=name,
      product_attributes=attributes,
      custom_attributes=[
          CustomAttribute(
              name="mycustomattribute", value="Example value"
          )
      ],
  )

  # Creates the UpdateProductInputRequest.
  request = UpdateProductInputRequest(
      update_mask=field_mask,
      data_source=data_source,
      product_input=product_input_data,
  )

  # Sends the update request to the API.
  try:
    print("Sending update ProductInput request")
    response = client.update_product_input(request=request)
    print("Updated ProductInput Name below")
    # The response includes the name of the updated product input.
    # The last part of the product name is the product ID assigned by Google.
    print(response.name)
    print("Updated Product below")
    print(response)
  except RuntimeError as e:
    # Catches and prints any errors that occur during the API call.
    print(e)


if __name__ == "__main__":
  # The ID of the product to be updated.
  # This ID is assigned by Google and typically follows the format:
  # contentLanguage~feedLabel~offerId
  # Replace with an actual product ID from your Merchant Center account.
  product_id_to_update = "online~en~label~sku123"

  # The ID of the data source that will own the updated product input.
  # Replace with an actual data source ID from your Merchant Center account.
  data_source_id_for_update = "<INSERT_DATA_SOURCE_ID>"

  update_product_input(
      _ACCOUNT_ID, product_id_to_update, data_source_id_for_update
  )

cURL

curl --location --request PATCH 'https://merchantapi.googleapis.com/products/v1/accounts/{ACCOUNT_ID}/productInputs/en~US~SKU12345?updateMask=productAttributes.title,productAttributes.description&dataSource=accounts/{ACCOUNT_ID}/dataSources/{DATASOURCE_ID}' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Content-Type: application/json' \
--data '{
   "productAttributes": {
       "title": "A Tale of Two Cities",
       "description": "A classic novel about the French Revolution"
   }
}'

使用自定义属性进行更新

您可以在一次调用中更新标准属性和自定义属性。如需更新自定义属性,请在 updateMask 中为其名称添加 customAttributes 前缀。

此示例在一个请求中执行多项操作:

  • 直接更新标准 title 属性。
  • 更新现有自定义属性 (myCustomAttrToBeUpdated)。
  • 插入新的自定义属性 (myCustomAttrToBeInserted)。
  • 删除现有自定义属性 (myCustomAttrToBeDeleted)。
PATCH https://merchantapi.googleapis.com/products/v1/accounts/{ACCOUNT_ID}/productInputs/en~US~SKU12345?updateMask=productAttributes.title,customAttributes.myCustomAttrToBeInserted,customAttributes.myCustomAttrToBeUpdated,customAttributes.myCustomAttrToBeDeleted&dataSource=accounts/{ACCOUNT_ID}/dataSources/{DATASOURCE_ID}
{
  "productAttributes": {
    "title": "ProductTitle Updated"
  },
  "customAttributes": [
    {
      "name": "description",
      "value": "A newly updated description."
    },
    {
      "name": "myCustomAttrToBeUpdated",
      "value": "myCustomAttrToBeUpdated updated value"
    },
    {
      "name": "myCustomAttrToBeInserted",
      "value": "new from update"
    }
  ]
}

如果请求成功,则会返回更新后的 ProductInput,其中反映了所有指定更改。

了解自定义属性更新

您可以使用 customAttributes 字段更新您自己定义的属性。这些属性不符合标准规范,将作为自定义属性存储在最终商品中。

商品更新的处理方式

当您发送 patch 请求时,系统会在应用任何规则之前将更新应用于特定的 ProductInput 数据。这样一来,插入和更新商品时的行为就会保持一致。

以下是更新的处理方式:

  1. 更新输入:您的 patch 请求会修改与您提供的数据源关联的特定 ProductInput

  2. 处理和合并:输入更新后,系统会开始处理:

    • Feed 规则和补充数据源:在商品的主要来源上配置的规则会合并主要来源和补充来源的 ProductInput。这些规则可以更改属性或派生新属性。如需详细了解如何设置规则,请参阅 https://support.google.com/merchants/answer/14994083 这篇文章。
    • 其他数据源:来自其他来源(例如自动改进)的数据也会与主要数据源输入合并。
    • 验证:合并后的数据会根据商品数据规范和 Google 的购物政策进行验证。
  3. 最终产品:此流水线的结果是最终处理的 Product 资源,可以使用 products.getproducts.list 返回该资源。这也是在 Merchant Center 中显示的商品版本,并且符合在不同目标平台中展示的条件。

由于此过程包含多个步骤,因此从您发送更新请求到最终 Product 资源(您可以使用 products.get 进行检索)反映出相应更改之间存在延迟,通常为几分钟。

示例:使用单个主要输入源更新商品

这是最常见的用例。商品存在于单个主要数据源中,并且您想要更新该商品的某些属性。

  1. 初始状态:主要数据源中存在商品 en~US~SKU12345,其中包含 title: "Classic T-Shirt"price: 15.99 USD
  2. 更新请求:您发送 patch 请求,将 price 更新为 14.99 USD,并将 availability 设置为 out of stock
  3. 处理
    • 已更新 SKU12345ProductInput
  4. 最终产品:最终 Product 现在具有 title: "Classic T-Shirt"price: 14.99 USDavailability: "out of stock"

示例:使用补充数据和规则更新商品

此示例展示了 Feed 规则如何影响更新,导致应用某些更改,同时覆盖其他更改。

  1. 初始状态
    • 主要输入en~US~SKU12345 包含 title: "Great T-Shirt"description: "A great short-sleeve t-shirt."
    • 补充输入:同一商品在补充数据源中有一个条目,其中包含 title: "Awesome T-Shirt"description: "An awesome short-sleeve t-shirt."
    • Feed 规则:系统已设置规则,从补充数据源中提取 title。没有针对 description 的规则。
    • 结果:最终处理的 Product 具有 title: "Awesome T-Shirt"description: "A great short-sleeve t-shirt."
  2. 更新请求:您发送 patch 请求来更新主要数据源,并将 title 设置为 "Fantastic T-Shirt",将 description 设置为 "A fantastic short-sleeve t-shirt."
  3. 处理
    • 主要数据源中的 ProductInput 已更新为包含 title: "Fantastic T-Shirt"description: "A fantastic short-sleeve t-shirt."
    • 处理流水线运行。
    • 对于 title,Feed 规则规定补充数据源 (Awesome T-Shirt) 中的值优先,因此会覆盖您的更新。
    • 对于 description,由于没有覆盖规则,因此使用来自主要输入 (A fantastic short-sleeve t-shirt.) 的更新值。
  4. 最终商品:最终 Product 的商品名仍为 Awesome T-Shirt(您的更新被覆盖),但其说明现在为 A fantastic short-sleeve t-shirt.(您的更新已应用)。

在更新和补充数据源之间进行选择

您可以使用 productinputs.patch 修改商品数据,也可以通过将数据插入补充数据源来修改商品数据。最佳选择取决于您的数据管理策略。

为避免出现不可预测的结果,我们建议您不要同时使用 productinputs.patch 和补充数据源来管理同一商品的相同商品数据。

以下是详细比较:

功能 productinputs.patch(更新) 补充数据源
最适合 对现有数据(例如价格、库存状况)进行快速、频繁的部分更改。 分层处理逻辑上分离的数据、由不同系统管理不同属性,或基于复杂的规则进行替换。
机制 就地修改现有 ProductInput 在补充数据源中创建新的单独 ProductInput
数据粒度 对单个 ProductInput 的特定字段进行操作。 对补充来源中的整个 ProductInput 进行操作。
持久性 更改会一直保留,直到完整的 insert 或其他 patch 覆盖相同的 ProductInput 为止。 持久性由 Feed 规则控制。如果规则优先考虑此数据,则可以无限期地覆盖主要数据。
规则互动 无需 Feed 规则即可使用,因为它可以更新现有数据源和 ProductInput 需要在主来源中明确设置规则,以关联补充来源。
数据源设置 对现有数据源进行操作。无需添加新来源。 需要创建和管理单独的补充数据源,并使用 Feed 规则将其关联起来。