이 문서는 Recommendations AI, Retail Search, 새로운 Retail 콘솔에 대한 문서입니다. 제한된 GA 단계에서 Retail Search를 사용하려면 Cloud 영업팀에 문의하세요.

Recommendations AI만 사용하는 경우 Recommendations 콘솔에서 Recommendations AI 문서를 참조하세요.

Retail Search용 인벤토리 업데이트

Product 생성, 읽기, 업데이트 및 삭제(CRUD) 메서드는 Product의 속성을 광범위하게 수정하는 데 사용되지만, 다양한 수준의 세부사항으로 인벤토리별 필드를 업데이트하는 데 사용할 수 있는 Product 메서드 세트가 있습니다. 다음 Product 필드는 인벤토리 필드로 간주됩니다.

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

인벤토리 업데이트 방법

제품의 인벤토리 정보 변경은 카탈로그 정보를 변경하는 것보다 훨씬 더 자주 발생할 수 있습니다. 따라서 대량의 인벤토리별 업데이트를 처리하기 위한 특수 메서드 세트가 제공됩니다. 이러한 메서드는 성능 저하 없이 제품당 수백 개의 동시 업데이트를 지원하는 다운스트림 최적화로 인해 비동기식입니다.

증분 업데이트

fulfillment_info는 종종 Product의 장소 수준 fulfillment 가용성을 인코딩하는 데 사용됩니다. 경우에 따라 특정 장소에 대한 fulfillment 가용성이 변경될 수 있으며, 전체 제품의 fulfillment 정보를 다시 지정하기 위해 UpdateProduct 메서드를 사용하는 대신 이 변경사항을 설명하는 업데이트를 실행할 수 있습니다.

이러한 경우 AddFulfillmentPlacesRemoveFulfillmentPlaces 메서드를 사용하여 지정된 fulfillment 유형에 추가되거나 삭제되는 장소 ID를 기반으로 제품 fulfillment 변경사항을 증분 방식으로 업데이트할 수 있습니다.

자바

Retail용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Retail 클라이언트 라이브러리를 참조하세요. 자세한 내용은 Retail 자바 API 참조 문서를 확인하세요.

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로 설정되므로 제품이 아직 존재하지 않더라도 최종적으로 제품이 생성될 때 업데이트된 인벤토리 정보가 저장됩니다. 비활성 업데이트가 장소 ID의 fulfillment 상태를 재정의하지 못하도록 이 업데이트에는 AddFulfillmentPlacesRequest.add_time으로 타임스탬프가 지정됩니다. 이러한 기능은 다음 섹션에서 보다 자세히 설명합니다.

이 동작은 RemoveFulfillmentPlacesRequest와 동일하며 스키마는 매우 유사합니다.

비증분 업데이트

price_info, availability, available_quantity는 장소 수준 정보가 아닌 제품 수준 인벤토리를 나타내기 때문에 증분 방식으로 업데이트할 수 없습니다. 또한 증분 변경 대신 fulfillment_info로 비증분 업데이트를 실행하는 것이 좋습니다. 이러한 경우 SetInventory 메서드를 사용하는 것이 좋습니다.

자바

Retail용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Retail 클라이언트 라이브러리를 참조하세요. 자세한 내용은 Retail 자바 API 참조 문서를 확인하세요.

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"에 대한 업데이트는 이 제품의 fulfillment 유형에 적합한 장소 ID가 없음을 나타냅니다. 다른 모든 fulfillment 유형은 이 요청에서 업데이트되지 않습니다.

기본적으로 SetInventory.set_mask가 설정되지 않거나 비어 있는 경우 SetInventory는 모든 인벤토리 필드를 업데이트합니다. 마스크가 비어 있지 않거나 SetInventoryRequest.set_mask에 인벤토리 필드가 명시적으로 나열되지 않으면 업데이트 요청에서 해당 인벤토리 필드에 지정된 값이 무시됩니다.

증분 업데이트와 마찬가지로 SetInventoryRequest.set_time 필드를 사용하여 업데이트된 모든 인벤토리 필드의 마지막으로 기록된 업데이트 시간을 기준으로 업데이트 시간을 설정할 수 있습니다.

인벤토리 업데이트를 위한 타임스탬프 보호

제품의 인벤토리 필드를 업데이트하고 잘못된 순서의 업데이트를 방지하기 위한 여러 경로가 있으며, 각 인벤토리 필드는 최신 업데이트 시간과 연결됩니다.

price_info, availability, available_quantity 및 각 쌍(fulfillment_info.place_ids, fulfillment_info.type)의 최신 업데이트 시간이 기록됩니다.

AddFulfillmentPlaces, RemoveFulfillmentPlaces, SetInventory 메서드를 사용하면 호출자가 요청이 실행되는 시점에 대한 업데이트 시간을 지정할 수 있습니다. 이 업데이트 시간은 관련 인벤토리 필드에 기록된 최신 업데이트 시간과 비교되며, 업데이트 시간이 최신 업데이트 시간 이후인 경우에만 업데이트가 커밋됩니다.

예를 들어 장소 ID "store1"에 fulfillment 유형 "pickup-in- store"가 사용 설정되어 마지막으로 기록된 업데이트 시간이 T로 설정되었다고 가정해 보겠습니다. RemoveFulfillmentPlacesRequest.type = "pickup-in-store"RemoveFulfillmentPlacesRequest.place_ids"store1"이 포함된 경우 요청은 RemoveFulfillmentPlacesRequest.remove_timeT 이후인 경우에만 "store1"에서 "pickup-in-store"를 삭제합니다. 이는 AddFulfillmentPlacesRequests에 대해서도 마찬가지입니다.

SetInventoryprice_info, availability, available_quantity를 업데이트하는 것과 유사한 방식으로 작동합니다. fulfillment_info를 업데이트할 때 SetInventoryRequest는 해당 fulfillment 유형에 지정된 모든 장소 ID를 추가하고 지정되지 않은 모든 기존 장소 ID를 삭제하도록 암시적으로 요청합니다.

따라서 SetInventoryRequest가 처리될 때 fulfillment_info 업데이트는 지정된 각 fulfillment 유형에 대해 AddFulfillmentPlacesRequestRemoveFulfillmentPlacesRequest로 암시적으로 변환됩니다. 즉, fulfillment가 "pickup-in-store"인 기존 장소 "store1"의 최종 업데이트 시간이 SetInventoryRequest.set_time보다 이후인 T인 경우 "store1""pickup-in-store"에 대한 암시적 추가/삭제가 적용되지 않습니다.

인벤토리 정보 미리 로드

각 인벤토리 업데이트 메서드를 통해 호출자는 요청에서 allow_missing을 설정할 수 있습니다. allow_missing이 true로 설정되면 존재하지 않는 Product에 대한 인벤토리 업데이트는 메서드 사양에 따라 Product가 존재하는 것처럼 처리됩니다. 인벤토리 정보는 이 기간 내에 CreateProduct를 통해 해당 Product가 생성되지 않을 경우 최대 2일 동안 보관됩니다.

자바

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

인벤토리 필드가 설정된 상태에서 CreateProduct를 호출하면 CreateProductRequest.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로 설정되며, fulfillment 유형 "pickup-in-store""same-day-delivery"를 지원하는 장소 ID는 없습니다.

인벤토리 정보가 있는 CreateProduct는 미리 로드된 모든 인벤토리 정보가 정확한지 확실하지 않고 Product 생성 시점에 인벤토리를 명시적으로 설정하여 카탈로그와 인벤토리를 완전히 동기화하려는 경우 유용할 수 있습니다.

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_missingtrue로 설정해 Product upsert를 수행하면 UpdateProduct를 사용하여 인벤토리 정보를 미리 로드할 수 있지만, 이 메서드를 사용하려면 UpdateProductRequest.product.title과 같은 특정 카탈로그 필드를 설정해야 합니다. 따라서 사용 사례를 미리 로드하는 데 인벤토리 업데이트 메서드를 사용하는 것이 좋습니다.

DeleteProduct

DeleteProduct가 호출되면 각 인벤토리 필드에 대한 최신 업데이트 시간의 모든 기록을 포함하여 DeleteProductRequest.name에 지정된 제품에 대한 기존 인벤토리 정보가 모두 삭제됩니다.