商品を頻繁に更新する

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 を更新し、imageLink を削除します。descriptionpriceupdateMask に含まれておらず、変更されません。

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 が更新され、imageLinkupdateMask に含まれているがリクエスト本文に含まれていないため削除されます。descriptionpriceupdateMask にリストされていないため、変更されません。

{
  "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"
   }
}'

カスタム属性を使用して更新する

標準属性とカスタム属性の両方を 1 回の呼び出しで更新できます。カスタム属性を更新するには、updateMask で名前の前に customAttributes を付けます。

この例では、1 つのリクエストで複数のアクションを実行します。

  • 標準の 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. 処理と統合: 入力が更新されると、処理が開始されます。

    • フィードルールと補助データソース: 商品のメインソースで設定されたルールは、メインソースと補助ソースの ProductInput を組み合わせます。これらのルールは、属性を変更したり、新しい属性を導出したりできます。ルールの設定について詳しくは、https://support.google.com/merchants/answer/14994083 をご覧ください。
    • その他のデータソース: その他のソース(自動改善など)からのデータも、プライマリ データソースの入力と統合されます。
    • 検証: 統合されたデータは、商品データ仕様と Google のショッピング ポリシーに照らして検証されます。
  3. 最終プロダクト: このパイプラインの結果は、products.get または products.list を使用して返すことができる最終的な処理済み Product リソースです。このバージョンは Merchant Center にも表示され、さまざまな掲載先で表示される対象となります。

この複数ステップのプロセスにより、更新リクエストを送信してから、products.get で取得できる最終的な Product リソースに変更が反映されるまでに、通常は数分の遅延が発生します。

例: 単一のプライマリ入力で商品を更新する

これが最も一般的なユースケースです。商品が 1 つのプライマリ データソースに存在し、その属性の一部を更新したい。

  1. 初期状態: メインのデータソースに、title: "Classic T-Shirt"price: 15.99 USD を含む商品 en~US~SKU12345 が存在します。
  2. 更新リクエスト: patch リクエストを送信して、price14.99 USD に更新し、availabilityout of stock に設定します。
  3. 処理:
    • SKU12345ProductInput が更新されます。
  4. 最終製品: 最終的な Producttitle: "Classic T-Shirt"price: 14.99 USDavailability: "out of stock" が含まれるようになりました。

例: 補助データとルールを使用して商品を更新する

この例では、フィードルールが更新に影響し、一部の変更が適用され、他の変更がオーバーライドされる仕組みを示します。

  1. 初期状態:
    • プライマリ入力: en~US~SKU12345title: "Great T-Shirt"description: "A great short-sleeve t-shirt." があります。
    • 補足入力: 同じ商品が、title: "Awesome T-Shirt"description: "An awesome short-sleeve t-shirt." を含む補足データソースにエントリを持っています。
    • フィードのルール: 補助データソースから 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 の場合、フィードルールでは、補助データソース(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 全体で動作します。
永続性 変更は、同じ ProductInput が完全な insert または別の patch で上書きされるまで保持されます。 永続性はフィードルールによって制御されます。ルールで優先順位が付けられている場合、プライマリ データを無期限でオーバーライドできます。
ルール インタラクション 既存のデータソースと ProductInput を更新するため、フィードルールなしで使用できます。 補助ソースをリンクするには、メインソースでルールを明示的に設定する必要があります。
データソースのセットアップ 既存のデータソースで動作します。新しいソースは必要ありません。 補助データソースを個別に作成して管理し、フィードルールを使用してリンクする必要があります。