更新 Vertex AI Search 零售解决方案的商品目录

虽然使用 Product 创建、读取、更新和删除 (CRUD) 方法可广泛修改 Product 的属性,但有一组 Product 方法可用于用于更新具有不同粒度级别的商品目录特定字段。以下 Product 字段被视为商品目录字段:

  • Product.price_info
  • Product.availability
  • Product.available_quantity
  • Product.fulfillment_info

教程:设置商品目录

本教程介绍了如何使用 SetInventory 方法推送商品目录更新,而无需更新整个商品。


如需遵循有关此任务的分步指导,请直接在 Cloud Shell Editor 中点击操作演示

操作演示


教程:添加履单信息

我们建议使用 AddLocalInventories 方法,而不是 AddFulfillmentPlacesAddLocalInventories 可实现相同的结果,但可对本地商品目录数据的提取进行更精细的控制。如需了解详情,请参阅 AddLocalInventories 文档

本教程介绍了如何使用 AddFulfillmentPlaces 方法更新商品履单信息。这样一来,搜索结果中便会显示商品有货且可以履单的最新动态。例如,买家在商店中寻找蓝色牛仔裤,但商店中没有货。当这家商店或任何其他商店重新有这款牛仔裤的库存时,买家就会看到更新,并可以继续下单。


如需遵循有关此任务的分步指导,请直接在 Cloud Shell Editor 中点击操作演示

操作演示


教程:移除履单

我们建议使用 RemoveLocalInventories 方法,而不是 RemoveFulfillmentPlacesRmoveLocalInventories 可实现相同的结果,但可对本地商品目录数据的提取进行更精细的控制。如需了解详情,请参阅 RemoveLocalInventories 文档

本教程介绍了如何使用 RemoveFulfillmentPlaces 方法更新商品履单信息。这样一来,Vertex AI Search for Retail 便可在商品缺货且订单无法履行时显示更新。这样一来,搜索网络便可在商品缺货且订单无法履行时显示相关更新。例如,买家在商店里寻找蓝色牛仔裤。如果这家商店的牛仔裤缺货,买家会看到这一信息,并且无法继续下单。


如需遵循有关此任务的分步指导,请直接在 Cloud Shell Editor 中点击操作演示

操作演示


商品目录更新方法

更改产品目录信息的频率要比更改目录信息的频率高得多。因此,我们提供了一套专门的方法来处理大量特定于商品目录的更新。这些方法是异步的,因为下游优化支持每个产品数百个并发更新,而不会影响性能。

增量更新

请注意,建议您按照本地商品目录更新指南发布增量商品目录更新。较新的 API 方法可对每个地点的广告资源属性提供更精细的控制。

fulfillment_info 通常用于对 Product 的地点级 fulfillment 可用性进行编码。在某些情况下,某些特定地点的 fulfillment 可用性可能会发生变化,并且您可以决定发出描述此更改的更新,而不是使用 UpdateProduct 方法重新指定整个商品的履单信息。

在这种情况下,AddFulfillmentPlacesRemoveFulfillmentPlaces 方法可用于根据地点 ID 逐步更新商品的 fulfillment 更改为给定的 fulfillment 类型添加或移除资源。

Java

如需了解如何安装和使用 Vertex AI Search for Retail 客户端库,请参阅 Vertex AI Search for Retail 客户端库。 如需了解详情,请参阅 Vertex AI Search for Retail Java API 参考文档

如需向 Vertex AI Search for Retail 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

public static AddFulfillmentPlacesResponse addFulfillmentPlaces(
    Product productToUpdate, String fulfillmentInfoType, ImmutableList<String> placeIds)
    throws IOException, InterruptedException, ExecutionException {
  ProductServiceClient productClient = getProductServiceClient();

  AddFulfillmentPlacesRequest request = AddFulfillmentPlacesRequest.newBuilder()
      .setProduct(productToUpdate.getName())
      .setType(fulfillmentInfoType)
      .addAllPlaceIds(placeIds)
      .setAddTime(Timestamps.fromMillis(System.currentTimeMillis()))
      .build();

  AddFulfillmentPlacesResponse response = productClient
      .addFulfillmentPlacesAsync(request).get();

  productClient.shutdownNow();
  productClient.awaitTermination(2, TimeUnit.SECONDS);

  return response;
}

Proto

  {
    product: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch/products/p123"
    type: "pickup-in-store"
    place_ids: "store0"
    place_ids: "store1"
    add_time: {
      seconds: 100
      nanos: 100
    }
    allow_missing: true
  }
  

此示例 AddFulfillmentPlacesRequest 添加了 fulfillment 类型 "pickup-in-store",以便为指定商品放置 ID "store0""store1"。由于 AddFulfillmentPlacesRequest.allow_missing 设置为 true,因此即使商品尚不存在,系统也会存储更新后的商品目录信息,以供最终创建商品时使用。更新带有 AddFulfillmentPlacesRequest.add_time 时间戳,以防止过时更新覆盖这些地点 ID 的 fulfillment 状态。以下各部分将更详细地讨论这些功能。

RemoveFulfillmentPlacesRequest 的行为完全相同,架构非常相似。

fulfillment_typesAddLocalInventoriesRemoveLocalInventories 更新时,它会反映每个地点 ID 与其支持的 fulfillment 类型列表之间的映射。当 fulfillment_infoAddFulfillmentPlacesRemoveFulfillmentPlaces 更新时,它会反映将每个具体执行方式类型映射到支持每个类型的地点 ID 列表。这两种 API 类型都会修改相同的底层执行信息,并且这两种 API 的效果会反映在 Product.fulfillment_info 中。

非增量更新

不能增量更新 price_infoavailabilityavailable_quantity,因为它们代表商品级商品目录,而不是地点级信息。此外,可能希望向 fulfillment_info 发出非增量更新,而不是仅增量更新。在这种情况下,建议使用 SetInventory 方法。

Java

如需了解如何安装和使用 Vertex AI Search for Retail 客户端库,请参阅 Vertex AI Search for Retail 客户端库。 如需了解详情,请参阅 Vertex AI Search for Retail Java API 参考文档

如需向 Vertex AI Search for Retail 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

public static SetInventoryResponse setInventoryWithMask(Product productToUpdate,
    FieldMask updateMask)
    throws IOException, ExecutionException, InterruptedException {
  ProductServiceClient productClient = getProductServiceClient();

  SetInventoryRequest request = SetInventoryRequest.newBuilder()
      .setInventory(productToUpdate)
      .setSetMask(updateMask)
      .setSetTime(Timestamps.fromMillis(System.currentTimeMillis()))
      .setAllowMissing(true)
      .build();

  SetInventoryResponse response = productClient.setInventoryAsync(request).get();

  productClient.shutdownNow();
  productClient.awaitTermination(2, TimeUnit.SECONDS);

  return response;
}

Proto

  {
    product: {
      name: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch/products/p123"
      availability: IN_STOCK
      fulfillment_info: {
        type: "pickup-in-store"
        place_ids: "store0"
        place_ids: "store1"
        place_ids: "store2"
        place_ids: "store3"
      }
      fulfillment_info: {
        type: "same-day-delivery"
      }
    }
    set_time: {
      seconds: 100
      nanos: 100
    }
    set_mask: {
      paths: "availability"
      paths: "fulfillment_info"
    }
    allow_missing: true
  }
  

在此特定请求中,SetInventoryRequest.product.fulfillment_info 字段是每个 fulfillment 类型符合条件的地点 ID 的完整说明,而不是增量规范。更新为 "same-day-delivery" 表示没有地点 ID 适用于此产品此 fulfillment 类型。所有其他 fulfillment 类型不会在此请求中更新。因此,此方法可用于仅替换部分 fulfillment 类型的地点 ID,而其他类型则保持不变。

默认情况下,如果 SetInventory.set_mask 未设置或为空,SetInventory 将更新所有库存字段。如果掩码不为空或 SetInventoryRequest.set_mask 中未明确列出库存字段,则系统会在更新请求中忽略该库存字段的任何指定值。

与增量更新一样,SetInventoryRequest.set_time 字段可用于设置更新时间,以与所有更新后的商品目录字段的上次记录更新时间相对比。

商品目录更新的时间戳保护措施

有几种不同的路径可以更新商品的库存字段,并防止出现无序更新,每个库存字段都与最新的更新时间相关联。

系统记录了 price_infoavailabilityavailable_quantity(fulfillment_info.place_ids, fulfillment_info.type) 的每一对的最新更新时间。

调用 AddFulfillmentPlacesRemoveFulfillmentPlacesSetInventory 方法可让调用方指定请求发出的更新时间。系统会将此更新时间与为相关商品目录字段记录的最新更新时间进行比较;当更新时间完全晚于最新更新时间时,才会提交更新。

例如,假设地点 ID "store1" 启用了 fulfillment 类型 "pickup-in- store",并且上次记录的更新时间设置为 T。如果 RemoveFulfillmentPlacesRequest.type = "pickup-in-store"RemoveFulfillmentPlacesRequest.place_ids 包含 "store1",则当且仅当 RemoveFulfillmentPlacesRequest.remove_time 晚于 T 时,请求才会从 "store1" 清除 "pickup-in-store"AddFulfillmentPlacesRequests 也是如此。

SetInventory 的运行方式与更新 price_infoavailabilityavailable_quantity 类似。更新 fulfillment_info 时,SetInventoryRequest 会隐式要求为给定 fulfillment 类型添加所有指定的地点 ID,并移除所有未指定的现有地点 ID。

因此,在处理 SetInventoryRequest 时,对于指定的每个 fulfillment 类型,fulfillment_info 更新都会隐式转换为 AddFulfillmentPlacesRequestRemoveFulfillmentPlacesRequest。也就是说,如果任何具有 fulfillment "pickup-in-store" 的现有地点 "store1" 的上次更新时间 T 晚于 SetInventoryRequest.set_time,将不会对 "store1""pickup-in-store" 应用隐式添加/移除。

预加载商品目录信息

每种库存更新方法都允许调用者在请求中设置 allow_missing。将 allow_missing 设置为 true 时,将按照方法规范处理对不存在的 Product 的库存更新,就像存在 Product 一样。如果未在此时间范围内通过 CreateProduct 创建相应的 Product,则商品目录信息将最多保留两天。

Java

public static SetInventoryResponse setInventory(Product productToUpdate)
    throws IOException, ExecutionException, InterruptedException {
  ProductServiceClient productClient = getProductServiceClient();

  SetInventoryRequest request = SetInventoryRequest.newBuilder()
      .setInventory(productToUpdate)
      .setSetTime(Timestamps.fromMillis(System.currentTimeMillis()))
      .setAllowMissing(true)
      .build();

  SetInventoryResponse response = productClient.setInventoryAsync(request).get();

  productClient.shutdownNow();
  productClient.awaitTermination(2, TimeUnit.SECONDS);

  return response;
}

何时使用 Product 方法

虽然可以使用产品 CRUD 方法更新商品目录字段,但调用者应该明确了解对现有或现有商品目录信息的影响。

它们是同步方法,这意味着用于商品目录方法的下游优化不适用,并且可能依赖于依靠这些方法频繁更新商品目录成本。尽可能使用被提及的商品目录更新方法。

CreateProduct

如果在设置任何广告资源字段的情况下调用 CreateProductCreateProductRequest.product 中提供的值将替换这些字段的预加载值。如果未设置库存字段,将自动使用任何预先存在的库存信息。

此外,已替换商品目录字段的最新更新时间将重置为方法调用的时间。

CreateProduct(包含预加载的商品目录)

PROTO

{
  parent: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch"
  product_id: "p123"
  product: {
    name: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch/products/p123"
    title: "some product"
    type: VARIANT
  }
}

在此示例中,创建的产品未设置任何库存字段,这意味着如果使用库存更新方法进行更新,系统将自动使用任何预加载的库存信息。这在库存更新与目录更新分离且您希望新创建的 Product 与任何现有库存信息同步时非常有用。

CreateProduct - 具有明确的商品目录

PROTO

{
  parent: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch"
  product_id: "p123"
  product: {
    name: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch/products/p123"
    title: "some product"
    type: VARIANT
    availability: OUT_OF_STOCK
    fulfillment_info: {
      type: "pickup-in-store"
    }
    fulfillment_info: {
      type: "same-day-delivery"
    }
  }
}

在此示例中,使用明确设置的库存字段创建 Product。这些字段将覆盖预先存在的任何值,并忽略相应字段的最新更新时间。因此,新创建的 Product 保证可用性设置为 OUT_OF_STOCK,并且没有任何地点 ID 支持 fulfillment 类型 "pickup-in-store""same-day-delivery"

如果不确定所有预加载的商品目录信息是否准确,并且宁愿在 Product 创建时显式设置商品目录以完全同步目录和商品目录,则带有商品目录信息的 CreateProduct 可能会有所帮助。

UpdateProduct

调用 UpdateProduct 且字段掩码 UpdateProductRequest.update_mask 包含任何库存字段时,UpdateProductRequest.product 中提供的值将替换任何预加载值。

此外,已替换商品目录字段的最新更新时间将重置为方法调用的时间。

PROTO

{
  product: {
    name: "projects/123/locations/global/catalogs/default_catalog/branches/default_branch/products/p123"
    availability: IN_STOCK
    fulfillment_info: {
      type: "pickup-in-store"
      place_ids: "store0"
      place_ids: "store1"
      place_ids: "store2"
      place_ids: "store3"
    }
    fulfillment_info: {
      type: "same-day-delivery"
    }
  }
  update_mask: {
    paths: "availability"
    paths: "fulfillment_info"
  }
}

此示例非常类似于 SetInventory 示例,不同之处在于无论每个商品目录字段的最新更新时间,系统保证始终应用更新。

如果在忽略时间戳保护的同时需要全面同步商品目录信息,则 UpdateProduct 能发挥作用。

虽然可以通过将 UpdateProductRequest.allow_missing 设置为 true 来执行 Product 插入,从而使用 UpdateProduct 预加载商品目录信息,但该方法需要设置特定目录字段,如 UpdateProductRequest.product.title。因此,建议使用商品目录更新方法预加载用例。

DeleteProduct

调用 DeleteProduct 时,将删除 DeleteProductRequest.name 中指定的产品的所有现有商品目录信息,包括每个商品目录字段的最新更新时间的所有记录。