การอัปเดตแบบเรียลไทม์

ส่วนนี้จะอธิบายวิธีส่งการอัปเดตข้อมูลสินค้าคงคลังที่มีความละเอียดอ่อนตามเวลาไปยัง Google Real-time updates API ช่วยให้คุณพุชข้อมูลอัปเดตและลบเอนทิตีในซานด์บ็อกซ์หรือพื้นที่โฆษณาเวอร์ชันที่ใช้งานจริงได้เกือบแบบเรียลไทม์

ฟังก์ชันการทำงานนี้มีไว้สำหรับอัปเดตที่คุณไม่สามารถคาดการณ์ได้เป็นหลัก เช่น การปิดร้านกะทันหัน การนำรายการออกจากเมนู หรืออัปเดตราคาของรายการเมนู ซึ่งต้องแสดงใน UI ของ Google อย่างรวดเร็ว หากไม่จําเป็นต้องแสดงการเปลี่ยนแปลงในทันที คุณสามารถใช้การส่งผ่านข้อมูลแบบเป็นกลุ่มแทน การอัปเดตแบบเรียลไทม์จะประมวลผลภายในเวลาไม่เกิน 5 นาที

ข้อกำหนดเบื้องต้น

คุณต้องมีสิ่งต่อไปนี้ก่อนใช้การอัปเดตแบบเรียลไทม์

  1. เปิดใช้ Maps Booking API แล้ว
    • ใน GCP ให้ไปที่ API และบริการ > ไลบรารี
    • ค้นหา "Google Maps Booking API"
      ค้นหา Google Maps Booking API
    • ค้นหาอินสแตนซ์ Sandbox ("Google Maps Booking API (Dev)") แล้วคลิกเปิดใช้
    • ค้นหาอินสแตนซ์เวอร์ชันที่ใช้งานจริง ("Google Maps Booking API") แล้วคลิกเปิดใช้
      เปิดใช้ Google Maps Booking API
  2. ระบบจะสร้างบัญชีบริการที่มีบทบาทผู้แก้ไขในโปรเจ็กต์ GCP โปรดดูรายละเอียดเพิ่มเติมที่หัวข้อการตั้งค่าบัญชี
  3. ฟีดข้อมูลเวอร์ชันที่ใช้งานจริงหรือแซนด์บ็อกซ์ได้รับการโฮสต์และส่งผ่านข้อมูล ดูรายละเอียดเพิ่มเติมได้ที่การส่งผ่านข้อมูลแบบเป็นกลุ่ม
  4. สําหรับการตรวจสอบสิทธิ์ API เราขอแนะนําให้ติดตั้งไลบรารีไคลเอ็นต์ Google ในภาษาที่ต้องการ ใช้ "https://www.googleapis.com/auth/mapsbooking" เป็นขอบเขต OAuth ตัวอย่างโค้ดที่รวมไว้ด้านล่างใช้ไลบรารีเหล่านี้ มิเช่นนั้น คุณจะต้องจัดการการแลกเปลี่ยนโทเค็นด้วยตนเองตามที่อธิบายไว้ในการใช้ OAuth 2.0 เพื่อเข้าถึง Google API

ภาพรวม

Real-time updates API รองรับการดำเนินการ 2 ประเภท การดำเนินการแรกคือการอัปเดตรายการที่มีอยู่ การดำเนินการที่ 2 คือลบเพื่อนำเอนทิตีออกจากคลัง ระบบจะดําเนินการทั้ง 2 รายการกับเอนทิตีต่างๆ ที่แสดงอยู่ในเนื้อหาคําขอ คุณสามารถอัปเดตเอนทิตีได้สูงสุด 1,000 รายการในการเรียก API ครั้งเดียว API จะยอมรับคำขอขาเข้าทั้งหมดและจัดคิวไว้เพื่อดำเนินการต่อ ดังนั้น ระบบจะประมวลผลคำขอ RTU แบบอะซิงโครนัส

การอัปเดตแบบเรียลไทม์ของ API ทำงานใน 2 สภาพแวดล้อม ได้แก่ แซนด์บ็อกซ์และเวอร์ชันที่ใช้งานจริง สภาพแวดล้อม Sandbox ใช้สำหรับการทดสอบคำขอ API และสภาพแวดล้อมเวอร์ชันที่ใช้งานจริงเพื่ออัปเดตเนื้อหาที่แสดงต่อผู้ใช้การสั่งซื้อจากต้นทางถึงปลายทาง ชื่อโฮสต์ของทั้ง 2 สภาพแวดล้อม

  • แซนด์บ็อกซ์ - partnerdev-mapsbooking.googleapis.com
  • เวอร์ชันที่ใช้งานจริง - mapsbooking.googleapis.com

ปลายทาง

การอัปเดตแบบเรียลไทม์ API จะแสดงปลายทาง 2 รายการเพื่อจัดการคำขอขาเข้าสำหรับการอัปเดตพื้นที่โฆษณา ดังนี้

  • UPSERT - /v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
  • ลบ - /v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete

พารามิเตอร์ PARTNER_ID จะอยู่ใน Actions Center ที่แสดงเป็นรหัสพาร์ทเนอร์ในหน้าบัญชีและผู้ใช้ ดังที่แสดงในภาพหน้าจอด้านล่าง

รหัสพาร์ทเนอร์ในพอร์ทัลของพาร์ทเนอร์

โดยใช้ 10000001 เป็นค่าของ PARTNER_ID จากตัวอย่างภาพหน้าจอด้านบน URL แบบเต็มสําหรับการส่งคําขอ API ใน Sandbox และเวอร์ชันที่ใช้งานจริงจะมีลักษณะดังตัวอย่างด้านล่าง

# Sandbox UPSERT
https://partnerdev-mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchPush
# Sandbox DELETE
https://partnerdev-mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchDelete
# Production UPSERT
https://mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchPush
# Production DELETE
https://mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchDelete

การอัปเดตเอนทิตี

หากต้องการอัปเดตเอนทิตีในพื้นที่โฆษณา ให้ใช้ปลายทาง UPSERT และส่งคําขอ HTTP POST คำขอ POST แต่ละรายการต้องมีพารามิเตอร์ PARTNER_ID พร้อมกับเพย์โหลด JSON ที่มี Structured Data ของประเภทเอนทิตีที่แสดงอยู่ในสคีมาสินค้าคงคลัง

เพย์โหลดคำขออัปเดต

เนื้อความของคำขอคือออบเจ็กต์ JSON ที่มีรายการระเบียน ระเบียนแต่ละรายการจะสอดคล้องกับเอนทิตีที่อัปเดต ประกอบด้วยช่อง data_record ที่มีเพย์โหลดเอนทิตีที่เข้ารหัส Base64 และ generation_timestamp ที่ระบุเวลาของการอัปเดตเอนทิตี

  {
    "records": [
      {
        "data_record":"BASE_64_ENCODED_ENTITY",
        "generation_timestamp":"UPDATE_TIMESTAMP"
      }
    ]
  }

ในเพย์โหลดด้านบน ให้แทนที่ข้อมูลต่อไปนี้

  • BASE_64_ENCODED_ENTITY: สตริง JSON ที่เข้ารหัส Base64 ของเอนทิตี JSON ของเอนทิตีที่ถอดรหัสแล้วควรมีโครงสร้างเหมือนกับในข้อกำหนดของฟีด เช่น

    {"@type":"MenuSection","name":"My Updated Menu Section","menuId":{"@id":"10824","displayOrder":1},"@id":"853705"}
  • UPDATE_TIMESTAMP: อย่าลืมใส่การประทับเวลาที่สร้างเอนทิตีในระบบแบ็กเอนด์ การประทับเวลานี้ใช้เพื่อตรวจสอบว่ามีการอัปเดตสินค้าคงคลังตามลำดับที่ถูกต้อง หากไม่ได้ระบุช่องนี้ ระบบจะตั้งค่าเป็นเวลาที่ Google ได้รับคำขอ เมื่ออัปเดตเอนทิตีผ่านคำขอ batchPush ระบบจะใช้ช่อง generation_timestamp สำหรับการกำหนดเวอร์ชันเอนทิตี ดูรูปแบบค่าเวลาที่คาดไว้ในสคีมาพื้นที่โฆษณาเชิงสัมพันธ์

คำขออัปเดตแบบเรียลไทม์ทุกรายการต้องเป็นไปตามเงื่อนไขต่อไปนี้

  • เนื้อหาของเพย์โหลดต้องมีขนาดไม่เกิน 5 MB ในทำนองเดียวกับฟีดกลุ่ม เราขอแนะนำให้คุณลบเว้นวรรคเพื่อใส่ข้อมูลได้มากขึ้น
  • คำขอ batchPush หนึ่งๆ อาจมีเอนทิตีได้สูงสุด 1,000 รายการ

ตัวอย่าง

ตัวอย่างที่ 1: การอัปเดตร้านอาหาร

สมมติว่าคุณต้องอัปเดตหมายเลขโทรศัพท์ของร้านอาหารอย่างเร่งด่วน การอัปเดตของคุณมี JSON ของทั้งร้านอาหาร

ลองดูฟีดกลุ่มที่มีลักษณะดังต่อไปนี้

{
  "@type": "Restaurant",
  "@id": "restaurant12345",
  "name": "Some Restaurant",
  "url": "https://www.provider.com/somerestaurant",
  "telephone": "+16501234570",
  "streetAddress": "345 Spear St",
  "addressLocality": "San Francisco",
  "addressRegion": "CA",
  "postalCode": "94105",
  "addressCountry": "US",
  "latitude": 37.472842,
  "longitude": -122.217144
}
  

การอัปเดตแบบเรียลไทม์ด้วย HTTP POST จะมีลักษณะดังนี้

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant12345",
        "name": "Some Restaurant",
        "url": "https://www.provider.com/somerestaurant",
        "telephone": "+16501234570",
        "streetAddress": "345 Spear St",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94105",
        "addressCountry": "US",
        "latitude": 37.472842,
        "longitude": -122.217144
      }
      "generation_timestamp": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Base64

ตัวอย่างเดียวกันที่มีเพย์โหลดที่เข้ารหัส Base64

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzNDUiLCJuYW1lIjoiU29tZSBSZXN0YXVyYW50IiwidXJsIjoiaHR0cHM6Ly93d3cucHJvdmlkZXIuY29tL3NvbWVyZXN0YXVyYW50IiwidGVsZXBob25lIjoiKzE2NTAxMjM0NTcwIiwic3RyZWV0QWRkcmVzcyI6IjM0NSBTcGVhciBTdCIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMDUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIiwibGF0aXR1ZGUiOjM3LjQ3Mjg0MiwibG9uZ2l0dWRlIjotMTIyLjIxNzE0NH0="
      "generation_timestamp": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

ตัวอย่างที่ 2: การอัปเดตร้านอาหารหลายแห่ง

หากต้องการอัปเดตเอนทิตีร้านอาหาร 2 รายการในการเรียก API ครั้งเดียว คำขอ HTTP POST จะเป็นดังนี้

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant12345",
        "name": "Some Restaurant",
        "url": "https://www.provider.com/somerestaurant",
        "telephone": "+16501235555",
        "streetAddress": "345 Spear St",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94105",
        "addressCountry": "US",
        "latitude": 37.472842,
        "longitude": -122.217144
      },
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    },
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant123",
        "name": "Some Other Restaurant",
        "url": "https://www.provider.com/someotherrestaurant",
        "telephone": "+16501231235",
        "streetAddress": "385 Spear St",
        "addressLocality": "San Mateo",
        "addressRegion": "CA",
        "postalCode": "94115",
        "addressCountry": "US"
      },
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    }
  ]
}
    

Base64

ตัวอย่างเดียวกันที่มีเพย์โหลดที่เข้ารหัส Base64

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzNDUiLCJuYW1lIjoiU29tZSBSZXN0YXVyYW50IiwidXJsIjoiaHR0cHM6Ly93d3cucHJvdmlkZXIuY29tL3NvbWVyZXN0YXVyYW50IiwidGVsZXBob25lIjoiKzE2NTAxMjM1NTU1Iiwic3RyZWV0QWRkcmVzcyI6IjM0NSBTcGVhciBTdCIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMDUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIiwibGF0aXR1ZGUiOjM3LjQ3Mjg0MiwibG9uZ2l0dWRlIjotMTIyLjIxNzE0NH0=",
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    },
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzIiwibmFtZSI6IlNvbWUgT3RoZXIgUmVzdGF1cmFudCIsInVybCI6Imh0dHBzOi8vd3d3LnByb3ZpZGVyLmNvbS9zb21lcmVzdGF1cmFudCIsInRlbGVwaG9uZSI6IisxNjUwMTIzMTIzNSIsInN0cmVldEFkZHJlc3MiOiIzODUgU3BlYXIgU3QiLCJhZGRyZXNzTG9jYWxpdHkiOiJTYW4gTWF0ZW8iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMTUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIn0=",
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    }
  ]
}
    

ตัวอย่างที่ 3: การอัปเดตราคาของรายการเมนู

สมมติว่าคุณต้องเปลี่ยนราคาของรายการในเมนู

ลองดูฟีดกลุ่มที่มีลักษณะดังต่อไปนี้

{
  "@type": "MenuItemOffer",
  "@id": "menuitemoffer6680262",
  "sku": "offer-cola",
  "menuItemId": "menuitem896532",
  "price": 2,
  "priceCurrency": "USD"
}

จากนั้นการอัปเดตแบบเรียลไทม์ผ่าน POST จะเป็นดังนี้

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "MenuItemOffer",
        "@id": "menuitemoffer6680262",
        "sku": "offer-cola",
        "menuItemId": "menuitem896532",
        "price": 2,
        "priceCurrency": "USD"
      },
      "generation_timestamp": "2022-08-19T17:20:10Z"
    }
  ]
}
    

Base64

ตัวอย่างเดียวกันที่มีเพย์โหลดที่เข้ารหัส Base64

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtT2ZmZXIiLCJAaWQiOiJtZW51aXRlbW9mZmVyNjY4MDI2MiIsInNrdSI6Im9mZmVyLWNvbGEiLCJtZW51SXRlbUlkIjoibWVudWl0ZW04OTY1MzIiLCJwcmljZSI6MiwicHJpY2VDdXJyZW5jeSI6IlVTRCJ9",
      "generation_timestamp": "2022-08-19T17:20:10Z"
    }
  ]
}

    

การเพิ่มเอนทิตี

อย่าใช้การอัปเดตแบบเรียลไทม์เพื่อเพิ่มเอนทิตีใหม่ เนื่องจากอาจส่งผลให้ข้อมูลไม่สอดคล้องกัน แต่ให้ใช้กระบวนการฟีดกลุ่มตามที่อธิบายไว้สําหรับการส่งผ่านข้อมูลแบบเป็นกลุ่ม

การลบเอนทิตี

หากต้องการลบเอนทิตีออกจากคลัง ให้ใช้ปลายทาง DELETE และส่งคําขอ HTTP POST คำขอ POST แต่ละรายการต้องมีพารามิเตอร์ PARTNER_ID พร้อมกับเพย์โหลด JSON ซึ่งมีตัวระบุของเอนทิตีในคลัง

ลบเพย์โหลดคำขอ

เนื้อความของคำขอให้ลบมีโครงสร้างคล้ายกับคำขออัปเดต รวมถึงมีรายการระเบียนที่มีฟิลด์ data_record และ delete_time ดังนี้

  {
    "records": [
      {
        "data_record":"BASE_64_ENCODED_REFERENCE",
        "delete_time": "DELETE_TIMESTAMP"
      }
    ]
  }

ในเพย์โหลดด้านบน ให้แทนที่ข้อมูลต่อไปนี้

  • BASE_64_ENCODED_REFERENCE: สตริง JSON ที่เข้ารหัส Base64 ของการอ้างอิงถึงเอนทิตีที่จะนําออก การอ้างอิงประกอบด้วยประเภทเอนทิตีและตัวระบุเท่านั้น เช่น การแสดง JSON ของการอ้างอิง MenuSection

    {"@type":"MenuSection","@id":"853705"}
  • DELETE_TIMESTAMP: อย่าลืมระบุการประทับเวลาที่ระบบแบ็กเอนด์ลบเอนทิตี การประทับเวลานี้ใช้เพื่อกำหนดลำดับการลบพื้นที่โฆษณา

คำขอ batchDelete หนึ่งๆ อาจมีเอนทิตีได้สูงสุด 1,000 รายการ

ตัวอย่าง

ตัวอย่างที่ 1: การนำเอนทิตี MenuItem 2 รายการออก

หากต้องการนำรายการเมนู 2 รายการออกในการเรียก API ครั้งเดียว คำขอ HTTP POST จะเป็นดังนี้

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "MenuItem",
        "@id": "item_1234"
      },
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
    {
      "data_record": {
        "@type": "MenuItem",
        "@id": "item_5678"
      },
      "delete_time": "2022-08-21T15:23:00.000Z"
    }
  ]
}
    

Base64

ตัวอย่างเดียวกันที่มีเพย์โหลดที่เข้ารหัส Base64

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtIiwiQGlkIjoiaXRlbV8xMjM0In0="
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtIiwiQGlkIjoiaXRlbV81Njc4In0="
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
  ]
}
    

ตัวอย่างที่ 2: การลบเอนทิตี Restaurant

ลองนึกถึงสถานการณ์ที่คุณต้องการลบร้านอาหารในฟีดกลุ่ม คุณต้องลบเฉพาะเอนทิตีร้านอาหาร อย่าลบเอนทิตีย่อย เช่น บริการและเมนู เนื่องจากระบบจะนำออกโดยอัตโนมัติ

ตัวอย่างคำขอลบเอนทิตีร้านอาหารที่มีรหัส https://www.provider.com/restaurant/12345

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "https://www.provider.com/restaurant/12345"
      },
      "delete_time": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Base64

ตัวอย่างเดียวกันที่มีเพย์โหลดที่เข้ารหัส Base64

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "ewogICJAdHlwZSI6ICJSZXN0YXVyYW50IiwKICAiQGlkIjogImh0dHBzOi8vd3d3LnByb3ZpZGVyLmNvbS9yZXN0YXVyYW50LzEyMzQ1Igp9"
      "delete_time": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

การตรวจสอบและรหัสการตอบกลับของ API

การตรวจสอบมี 2 ประเภทที่ดำเนินการกับการเรียก API การอัปเดตแบบเรียลไทม์ ได้แก่

  • ระดับคำขอ - การตรวจสอบเหล่านี้จะตรวจสอบว่าเพย์โหลดเป็นไปตามสคีมา upsert หรือ delete และ data_record ทุกรายการมีทั้งช่อง @id และ @type การตรวจสอบเหล่านี้เป็นแบบซิงค์และระบบจะแสดงผลลัพธ์ในส่วนเนื้อหาการตอบกลับของ API รหัสการตอบกลับ 200 และเนื้อหา JSON ว่าง {} หมายความว่าการตรวจสอบเหล่านี้ผ่านและระบบได้จัดคิวเอนทิตีในคำขอนั้นเพื่อประมวลผล รหัสการตอบกลับที่ไม่ใช่ 200 หมายความว่าการตรวจสอบอย่างน้อย 1 รายการไม่สำเร็จและคำขอทั้งหมดถูกปฏิเสธ (รวมถึงเอนทิตีทั้งหมดในเพย์โหลด) ตัวอย่างเช่น หาก data_record ไม่มี @type ระบบจะแสดงการตอบกลับข้อผิดพลาดต่อไปนี้

    {
      "error": {
        "code": 400,
        "message": "Record:{\"@id\":\"2717/86853/DELIVERY\",\"applicableServiceType\":[\"DELIVERY\",\"TAKEOUT\"],\"menuId\":[{\"@id\":\"2717/DELIVERY\",\"displayOrder\":1},{\"@id\":\"2717/TAKEOUT\",\"displayOrder\":2}],\"name\":\"Salad\",\"offeredById\":[\"2717\"]} has following errors: \nThe entity type could not be extracted from the entity value.\n",
        "status": "INVALID_ARGUMENT",
        "details": [
          {
            "@type": "type.googleapis.com/google.rpc.DebugInfo",
            "detail": "[ORIGINAL ERROR] generic::invalid_argument: Failed to parse one or more rtu records. Record:{\"@id\":\"2717/86853/DELIVERY\",\"applicableServiceType\":[\"DELIVERY\",\"TAKEOUT\"],\"menuId\":[{\"@id\":\"2717/DELIVERY\",\"displayOrder\":1},{\"@id\":\"2717/TAKEOUT\",\"displayOrder\":2}],\"name\":\"Salad\",\"offeredById\":[\"2717\"]} has following errors: \nThe entity type could not be extracted from the entity value.\n [google.rpc.error_details_ext] { message: \"Record:{\\\"@id\\\":\\\"2717/86853/DELIVERY\\\",\\\"applicableServiceType\\\":[\\\"DELIVERY\\\",\\\"TAKEOUT\\\"],\\\"menuId\\\":[{\\\"@id\\\":\\\"2717/DELIVERY\\\",\\\"displayOrder\\\":1},{\\\"@id\\\":\\\"2717/TAKEOUT\\\",\\\"displayOrder\\\":2}],\\\"name\\\":\\\"Salad\\\",\\\"offeredById\\\":[\\\"2717\\\"]} has following errors: \\nThe entity type could not be extracted from the entity value.\\n\" }"
          }
        ]
      }
    }
  • ระดับเอนทิตี - ระบบจะตรวจสอบเอนทิตีแต่ละรายการในเพย์โหลดเทียบกับสคีมาเชิงสัมพันธ์ ระบบจะไม่รายงานปัญหาที่พบในระยะการตรวจสอบนี้ในคำตอบของ API โดยระบบจะรายงานในแดชบอร์ดการรายงาน RTU เท่านั้น

โควต้า API

การอัปเดต API แบบเรียลไทม์มีโควต้า 1,500 คำขอทุก 60 วินาที หรือ 25 คำขอต่อวินาทีโดยเฉลี่ย เมื่อใช้โควต้าเกิน Google จะตอบกลับด้วยข้อความแสดงข้อผิดพลาดต่อไปนี้

{
  "error": {
    "code": 429,
    "message": "Insufficient tokens for quota ...",
    "status": "RESOURCE_EXHAUSTED",
    "details": [...]
  }
}

วิธีแก้ปัญหานี้คือลองเรียกใช้อีกครั้งโดยเว้นระยะเวลาห่างมากขึ้นเรื่อยๆ จนกว่าจะสำเร็จ หากคุณใช้โควต้าจนหมดเป็นประจำ ให้พิจารณารวมเอนทิตีเพิ่มเติมในคําขอ API 1 รายการ คุณรวมเอนทิตีได้สูงสุด 1,000 รายการในการเรียก API ครั้งเดียว

ตัวอย่างโค้ด

ด้านล่างนี้คือตัวอย่างวิธีใช้ Real-Time Update API ในภาษาต่างๆ ตัวอย่างเหล่านี้ใช้ไลบรารี Google Auth เพื่อตรวจสอบสิทธิ์โดยใช้ไฟล์คีย์ของบัญชีบริการที่สร้างขึ้นระหว่างการตั้งค่าบัญชี ดูวิธีแก้ปัญหาอื่นๆ ได้ที่การใช้ OAuth 2.0 สำหรับแอปพลิเคชันที่มีการโต้ตอบระหว่างเซิร์ฟเวอร์กับเซิร์ฟเวอร์ พิจารณาใช้สคีมาที่พร้อมใช้งานในสร้างคลังไลบรารีไคลเอ็นต์เพื่อสร้างซอร์สโค้ดสําหรับพื้นที่โฆษณาและประเภทออบเจ็กต์การอัปเดตแบบเรียลไทม์

การอัปเดตเอนทิตี

Node.js

โค้ดนี้ใช้ไลบรารีการให้สิทธิ์ของ Google สําหรับ Node.js

/* Sample code for Real-time update batchPush implementation.
 *
 * Required libraries:
 * - google-auth-library
 */

const {JWT} = require('google-auth-library');

// ACTION REQUIRED: Change this to the path of the service account client secret
// file downloaded from the Google Cloud Console.
const serviceAccountJson = require('./service-account.json');

// ACTION REQUIRED: Change this to your Partner ID received from Google.
// The Partner ID is available on the Partner Portal.
const PARTNER_ID = 1234;

const HOST = {
  prod: 'https://mapsbooking.googleapis.com',
  sandbox: 'https://partnerdev-mapsbooking.googleapis.com'
};

// ACTION REQUIRED: Change to 'prod' for production
const ENV = 'sandbox';

// Feed name for Order with Google including the version.
const FEED_NAME = 'owg.v2';

// Endpoint url
const url = `${HOST[ENV]}/v1alpha/inventory/partners/${PARTNER_ID}/feeds/${
    FEED_NAME}/record:batchPush`;

/**
 * Send a Real-time update request to update/insert entities
 */
async function batchUpsert(entities) {
  /**
   * Sign JWT token using private key from service account secret file
   * provided. The client can be created without providing a service account
   * secret file by implementing Application Default Credentials.
   * https://github.com/googleapis/google-auth-library-nodejs
   */
  const client = new JWT({
    email: serviceAccountJson.client_email,
    key: serviceAccountJson.private_key,
    scopes: ['https://www.googleapis.com/auth/mapsbooking'],
  });
  const request = {records: toPushRecords(entities)};
  const body = JSON.stringify(request);
  try {
    const response = await client.request({
      method: 'POST',
      url,
      data: body,
      headers: {'Content-Type': 'application/json'}
    });
    console.log('request body:', body);
    console.log('response status:', response.status);
    console.log(
        'response data:', response.data);  // successful response returns '{}'
  } catch (error) {
    console.log('error:', error);
  }
}

/**
 * Maps array of entities to records for batch push requests
 */
const toPushRecords = (entities) => {
  return entities.map((entity) => {
    // Using dateModified to set generation_timestamp. Defaulting to the
    // current timestamp for records that do not have dateModified.
    const generation_timestamp =
        entity.dateModified ? entity.dateModified : new Date().toISOString();
    return {data_record: btoa(JSON.stringify(entity)), generation_timestamp};
  });
};

// Call batchUpsert with example entities. dateModified is optional and is
// used to hold the actual timestamp when the entity was updated/created.
batchUpsert([
  {
    '@type': 'MenuItemOffer',
    '@id': '6680261',
    'menuItemId': '18931508',
    'price': 15.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
  },
  {
    '@type': 'MenuItemOffer',
    '@id': '6680262',
    'menuItemId': '18931509',
    'price': 25.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
  }
]);

Python

โค้ดนี้ใช้ไลบรารีการให้สิทธิ์ของ Google สําหรับ Python

"""Sample code for the Real-time update batchPush implementation."""

# Required libraries:
# - google-auth

import base64
import datetime
import json
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

# ACTION REQUIRED: Change this to the Partner ID received from Google.
# Partner ID is available on the Partner Portal.
# https://partnerdash.google.com/apps/reservewithgoogle
_PARTNER_ID = '1234'

# ACTION REQUIRED: Change this to the path of the service account client secret
# file downloaded from the Google Cloud Console.
_SERVICE_ACCOUNT_KEY_JSON_FILE = 'service-account-creds.json'

_HOST_MAP = {
    'sandbox': 'https://partnerdev-mapsbooking.googleapis.com',
    'prod': 'https://mapsbooking.googleapis.com'
}

# ACTION REQUIRED: Change to 'prod' for production
_ENV = 'sandbox'

# Feed name for Order with Google including the version.
_FEED_NAME = 'owg.v2'

_ENDPOINT = '{}/v1alpha/inventory/partners/{}/feeds/{}/record:batchPush'.format(
    _HOST_MAP[_ENV], _PARTNER_ID, _FEED_NAME)


def batch_upsert(entities):
  """Makes a batchPush request using the Real-time updates REST service.

  Args:
      entities: The list of entity objects to update or add.
  """

  # Creates credentials by providing a json file. Credentials can also be
  # provided by implementing Application Default Credentials.
  # https://googleapis.dev/python/google-auth/latest/user-guide.html
  credentials = service_account.Credentials.from_service_account_file(
      _SERVICE_ACCOUNT_KEY_JSON_FILE,
      scopes=['https://www.googleapis.com/auth/mapsbooking'])
  authorized_session = AuthorizedSession(credentials)

  # JSON request object
  batch_request = {'records': [create_push_record(x) for x in entities]}
  response = authorized_session.post(_ENDPOINT, json=batch_request)
  print('request body:', json.dumps(batch_request))
  print('response status:', response.status_code)
  print('response data:', response.text)  # successful response returns '{}'


def create_push_record(entity):
  """Creates a record from an entity for batchPush requests.

  Args:
      entity: The entity object to create the record from.

  Returns:
      The constructed record for the batchPush request payload.
  """
  data_bytes = json.dumps(entity).encode('utf-8')
  base64_bytes = base64.b64encode(data_bytes)
  # Using dateModified to set generation_timestamp. Defaulting to the
  # current timestamp for records that do not have dateModified.
  generation_timestamp = entity.dateModified if 'dateModified' in entity else datetime.datetime.now(
  ).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
  return {
      'generation_timestamp': generation_timestamp,
      'data_record': base64_bytes.decode('utf-8')
  }


# Call batch_upsert with example entities. dateModified is optional and is
# used to hold the actual timestamp when the entity was updated/created.
batch_upsert([{
    '@type': 'MenuItemOffer',
    '@id': '6680261',
    'menuItemId': '18931508',
    'price': 15.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
}, {
    '@type': 'MenuItemOffer',
    '@id': '6680262',
    'menuItemId': '18931509',
    'price': 25.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
}])

Java

โค้ดนี้ใช้ไลบรารีการให้สิทธิ์ของ Google สําหรับ Java

โมเดลซอร์สโค้ดไคลเอ็นต์ในแพ็กเกจ rtusamples.inventory และ rtusamples.realtime สร้างโดยทำตามขั้นตอนในหัวข้อสร้างไลบรารีไคลเอ็นต์

/*
 * Required Libraries:
 * - JDK >= 11
 * - google-auth-library-oauth2-http
 */
package rtusamples;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import rtusamples.inventory.MenuItemOffer;
import rtusamples.inventory.MenuItemOfferType;
import rtusamples.inventory.ServiceTypeElement;
import rtusamples.realtime.BatchPushGenericRecordRequest;
import rtusamples.realtime.GenericRecord;

/** Sample code for Real-time update batchPush implementation. */
public final class BasicPush {
  // ACTION REQUIRED: Change this to your Partner ID received from Google. The Partner ID is
  // available on the Partner Portal.
  private static final long PARTNER_ID = 12345678;

  // ACTION REQUIRED: Change this to the path of the service account client secret file downloaded
  // from the Google Cloud Console.
  private static final String JSON_KEY_FULL_PATH =
      "<path to your JSON credentials>/credentials.json";

  // ACTION REQUIRED: Change this to the endpoint that is needed.
  private static final String ENDPOINT =
      //    "https://partnerdev-mapsbooking.googleapis.com"; // for sandbox
      "https://mapsbooking.googleapis.com"; // for prod

  // Feed name for Order with Google including the version.
  private static final String FEED_NAME = "owg.v2";

  private static final ObjectMapper objectMapper = new ObjectMapper();

  private static final DateTimeFormatter TIMESTAMP_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

  private static final Charset UTF_8 = Charset.forName("UTF-8");

  public static void main(String[] args) throws Exception {

    /**
     * Create credentials from service account secret file. Alternatively, the credentials can be
     * created by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-java
     */
    // GoogleCredentials sourceCredentials =
    //     GoogleCredentials.getApplicationDefault()
    //         .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // ImpersonatedCredentials credentials =
    //     ImpersonatedCredentials.create(
    //         sourceCredentials,
    //         "fo-test@projectname.iam.gserviceaccount.com",
    //         null,
    //         Arrays.asList("https://www.googleapis.com/auth/mapsbooking"),
    //         300);

    GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream(JSON_KEY_FULL_PATH))
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // Create example MenuItemOffer entities, dateModified is optional and is used to hold
    // the actual timestamp when the entity was updated/created.
    MenuItemOffer menuItemOfferPizza = new MenuItemOffer();
    menuItemOfferPizza.setID("6680261");
    menuItemOfferPizza.setType(MenuItemOfferType.MENU_ITEM_OFFER);
    menuItemOfferPizza.setMenuItemID("18931508");
    menuItemOfferPizza.setPrice(15.5);
    menuItemOfferPizza.setPriceCurrency("USD");
    menuItemOfferPizza.setApplicableServiceType(
        new ServiceTypeElement[] {ServiceTypeElement.TAKEOUT, ServiceTypeElement.DELIVERY});
    menuItemOfferPizza.setInventoryLevel(0.0);
    menuItemOfferPizza.setDateModified("2022-10-07T13:00:00.000Z");

    MenuItemOffer menuItemOfferSalad = new MenuItemOffer();
    menuItemOfferSalad.setID("6680262");
    menuItemOfferSalad.setType(MenuItemOfferType.MENU_ITEM_OFFER);
    menuItemOfferSalad.setMenuItemID("18931509");
    menuItemOfferSalad.setPrice(25.5);
    menuItemOfferSalad.setPriceCurrency("USD");
    menuItemOfferSalad.setApplicableServiceType(
        new ServiceTypeElement[] {ServiceTypeElement.TAKEOUT, ServiceTypeElement.DELIVERY});
    menuItemOfferSalad.setInventoryLevel(0.0);
    menuItemOfferSalad.setDateModified("2022-10-07T13:00:00.000Z");

    // Example array of MenuItemOffer entities to update.
    List<MenuItemOffer> menuItemOffers = Arrays.asList(menuItemOfferPizza, menuItemOfferSalad);

    // Create list of GenericRecord from menuItemOffers.
    List<GenericRecord> menuItemOfferGenericRecords =
        menuItemOffers.stream()
            .map(
                (menuItemOffer) ->
                    toBatchPushRecord(menuItemOffer, menuItemOffer.getDateModified()))
            .collect(Collectors.toList());

    // List of records to be updated/created.
    List<GenericRecord> recordsToBeUpdated = new ArrayList<>();

    // Add list of menuItemOffer generic records.
    recordsToBeUpdated.addAll(menuItemOfferGenericRecords);

    // Request object that contains all records.
    BatchPushGenericRecordRequest batchPushRequest = new BatchPushGenericRecordRequest();
    batchPushRequest.setRecords(recordsToBeUpdated.toArray(new GenericRecord[0]));

    // Execute batchPush request.
    BasicPush basicPush = new BasicPush();
    basicPush.batchPush(batchPushRequest, credentials);
  }

  public void batchPush(
      BatchPushGenericRecordRequest batchPushRequest, GoogleCredentials credentials)
      throws IOException {

    credentials.refreshIfExpired();
    AccessToken token = credentials.getAccessToken();

    String requestBody = objectMapper.writeValueAsString(batchPushRequest);
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(
                URI.create(
                    String.format(
                        "%s/v1alpha/inventory/partners/%s/feeds/%s/record:batchPush",
                        ENDPOINT, PARTNER_ID, FEED_NAME)))
            .header("Content-Type", "application/json")
            .header("Authorization", String.format("Bearer %s", token.getTokenValue()))
            .POST(BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = null;
    try {
      response = client.send(request, BodyHandlers.ofString());
      System.out.println("Request body:" + requestBody);
      System.out.println("Response status:" + response.statusCode());
      System.out.println("Response body:" + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static <T> GenericRecord toBatchPushRecord(T entity, String dateModified) {
    GenericRecord genericRecord = new GenericRecord();
    try {
      String json = objectMapper.writeValueAsString(entity);
      genericRecord.setDataRecord(Base64.getEncoder().encodeToString(json.getBytes(UTF_8)));
      // Using dateModified to set generation_timestamp. Defaulting to the
      // current timestamp for records that do not have dateModified.
      String generationTimestamp =
          Optional.ofNullable(dateModified)
              .orElse(OffsetDateTime.now(Clock.systemUTC()).format(TIMESTAMP_FORMATTER));
      genericRecord.setGenerationTimestamp(generationTimestamp);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
    }
    return genericRecord;
  }
}

การนำเอนทิตีออก

Node.js

โค้ดนี้ใช้ไลบรารีการให้สิทธิ์ของ Google สําหรับ Node.js

/* Sample code for Real-time update batchDelete implementation.
 *
 * Required libraries:
 * - google-auth-library
 */

const {JWT} = require('google-auth-library');

// ACTION REQUIRED: Change this to the path of the service account client secret
// file downloaded from the Google Cloud Console.
const serviceAccountJson = require('./service-account.json');

// ACTION REQUIRED: Change this to your Partner ID received from Google.
// The Partner ID is available on the Partner Portal.
const PARTNER_ID = 1234;

const HOST = {
  prod: 'https://mapsbooking.googleapis.com',
  sandbox: 'https://partnerdev-mapsbooking.googleapis.com'
};

// ACTION REQUIRED: Change to 'prod' for production
const ENV = 'sandbox';

// Feed name for Order with Google including the version.
const FEED_NAME = 'owg.v2';

// Endpoint url
const url = `${HOST[ENV]}/v1alpha/inventory/partners/${PARTNER_ID}/feeds/${
    FEED_NAME}/record:batchDelete`;


/**
 * Send a Real-time update request to delete entities
 */
async function batchDelete(entities) {
  try {
    /**
     * Sign JWT token using private key from service account secret file
     * provided. The client can be created without providing a service account
     * secret file by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-nodejs
     */
    const client = new JWT({
      email: serviceAccountJson.client_email,
      key: serviceAccountJson.private_key,
      scopes: ['https://www.googleapis.com/auth/mapsbooking'],
    });
    const request = {
      records: toDeleteRecords(entities)
    };
    const body = JSON.stringify(request);
    try {
      const response = await client.request({
        method: 'POST',
        url,
        data: body,
        headers: {'Content-Type': 'application/json'}
      });
      console.log('request body:', body);
      console.log('response status:', response.status);
      console.log('response data:', response.data);  // successful response returns '{}'
    } catch (error) {
      console.log('error:', error);
    }
  }

  /**
   * Maps array of entities to records for batch delete requests
   */
  const toDeleteRecords = (entities) => {
    return entities.map((entity) => {
      // Using dateModified to set delete_time. Defaulting to the current
      // timestamp for records that do not have dateModified.
      const delete_time =
          entity.dateModified ? entity.dateModified : new Date().toISOString();
      return {data_record: btoa(JSON.stringify(entity)), delete_time};
    });
  };

  // Call batchDelete with example entities. dateModified is optional and is
  // used to hold the actual timestamp when the entity was deleted.
  batchDelete([
    {
      '@type': 'Menu',
      '@id': '853706',
      'dateModified': '2022-06-19T15:43:50.970Z'
    },
    {
      '@type': 'Menu',
      '@id': '853705',
      'dateModified': '2022-06-19T15:13:00.280Z'
    }
  ]);

Python

โค้ดนี้ใช้ไลบรารีการให้สิทธิ์ของ Google สําหรับ Python

"""Sample code for the Real-time update batchDelete implementation."""

# Required libraries:
# - google-auth

import base64
import datetime
import json
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

# ACTION REQUIRED: Change this to the Partner ID received from Google.
# Partner ID is available on the Partner Portal.
# https://partnerdash.google.com/apps/reservewithgoogle
_PARTNER_ID = '1234'

# ACTION REQUIRED: Change this to the path of the service account client secret
# file downloaded from the Google Cloud Console.
_SERVICE_ACCOUNT_KEY_JSON_FILE = 'service-account-creds.json'

_HOST_MAP = {
    'sandbox': 'https://partnerdev-mapsbooking.googleapis.com',
    'prod': 'https://mapsbooking.googleapis.com'
}

# ACTION REQUIRED: Change to 'prod' for production
_ENV = 'sandbox'

# Feed name for Order with Google including the version.
_FEED_NAME = 'owg.v2'

_ENDPOINT = '{}/v1alpha/inventory/partners/{}/feeds/{}/record:batchDelete'.format(
    _HOST_MAP[_ENV], _PARTNER_ID, _FEED_NAME)


def batch_delete(entities):
  """Makes a batch delete request using the Real-time updates REST service.

  Args:
      entities: The list of entity objects to delete.
  """

  # Creates credentials by providing a json file. Credentials can also be
  # provided by implementing Application Default Credentials.
  # https://googleapis.dev/python/google-auth/latest/user-guide.html
  credentials = service_account.Credentials.from_service_account_file(
      _SERVICE_ACCOUNT_KEY_JSON_FILE,
      scopes=['https://www.googleapis.com/auth/mapsbooking'])
  authorized_session = AuthorizedSession(credentials)

  # JSON request object
  batch_request = {'records': [create_delete_record(x) for x in entities]}
  response = authorized_session.post(_ENDPOINT, json=batch_request)
  print('request body:', json.dumps(batch_request))
  print('response status:', response.status_code)
  print('response data:', response.text)  # successful response returns '{}'


def create_delete_record(entity):
  """Creates a record from an entity for batchDelete requests.

  Args:
    entity: The entity object to create the record from.

  Returns:
    The constructed record for the batchDelete request payload.
  """
  data_bytes = json.dumps(entity).encode('utf-8')
  base64_bytes = base64.b64encode(data_bytes)
  # Using dateModified to set delete_time. Defaulting to the current
  # timestamp for records that do not have dateModified.
  delete_time = entity.dateModified if 'dateModified' in entity else datetime.datetime.now(
  ).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
  return {
      'delete_time': delete_time,
      'data_record': base64_bytes.decode('utf-8')
  }


# Call batch_delete with example entities. dateModified is optional and is
# used to hold the actual timestamp when the entity was deleted.
batch_delete([{
    '@type': 'Menu',
    '@id': '853706',
    'dateModified': '2022-06-19T13:10:00.000Z'
}, {
    '@type': 'Menu',
    '@id': '853705',
    'dateModified': '2022-06-19T13:30:10.000Z'
}])

Java

โค้ดนี้ใช้ไลบรารีการให้สิทธิ์ของ Google สําหรับ Java

โมเดลซอร์สโค้ดไคลเอ็นต์ในแพ็กเกจ rtusamples.inventory และ rtusamples.realtime สร้างโดยทำตามขั้นตอนในหัวข้อสร้างไลบรารีไคลเอ็นต์

/*
 * Required Libraries:
 * - JDK >= 11
 * - google-auth-library-oauth2-http
 */
package rtusamples;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import rtusamples.inventory.Menu;
import rtusamples.inventory.MenuType;
import rtusamples.realtime.BatchDeleteGenericRecordsRequest;
import rtusamples.realtime.GenericDeleteRecord;

/** Sample code for the Real-time update batchDelete implementation. */
public final class BasicDelete {
  // ACTION REQUIRED: Change this to your Partner ID received from Google. The Partner ID is
  // available on the Partner Portal.
  private static final long PARTNER_ID = 123456789;

  // ACTION REQUIRED: Change this to the path of the service account client secret file downloaded
  // from the Google Cloud Console.
  private static final String JSON_KEY_FULL_PATH =
      "<path to your JSON credentials>/credentials.json";

  // ACTION REQUIRED: Change this to the endpoint that is needed.
  private static final String ENDPOINT =
      "https://partnerdev-mapsbooking.googleapis.com"; // for sandbox
  // "https://mapsbooking.googleapis.com" // for prod

  // Feed name for Order with Google including the version.
  private static final String FEED_NAME = "owg.v2";

  private static final ObjectMapper objectMapper = new ObjectMapper();

  private static final DateTimeFormatter TIMESTAMP_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

  private static final Charset UTF_8 = Charset.forName("UTF-8");

  public static void main(String[] args) throws Exception {

    /**
     * Create credentials from service account secret file. Alternatively, the credentials can be
     * created by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-java
     */
    // GoogleCredentials sourceCredentials =
    //     GoogleCredentials.getApplicationDefault()
    //         .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // ImpersonatedCredentials credentials =
    //     ImpersonatedCredentials.create(
    //         sourceCredentials,
    //         "fo-test@projectname.iam.gserviceaccount.com",
    //         null,
    //         Arrays.asList("https://www.googleapis.com/auth/mapsbooking"),
    //         300);

    GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream(JSON_KEY_FULL_PATH))
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // Create example Menu entities, dateModified is optional and is used to hold
    // the actual timestamp when the entity was deleted.
    Menu menuLunch = new Menu();
    menuLunch.setID("853705");
    menuLunch.setType(MenuType.MENU);
    menuLunch.setDateModified("2022-09-19T13:10:00.000Z");

    Menu menuDinner = new Menu();
    menuDinner.setID("853706");
    menuDinner.setType(MenuType.MENU);
    menuDinner.setDateModified("2022-09-19T13:13:10.000Z");

    // Example array of Menu entities to update.
    List<Menu> menus = Arrays.asList(menuLunch, menuDinner);

    // Create list of GenericDeleteRecord from menus.
    List<GenericDeleteRecord> menuGenericDeleteRecords =
        menus.stream()
            .map((menu) -> toBatchDeleteRecord(menu, menu.getDateModified()))
            .collect(Collectors.toList());

    // List of records to be deleted.
    List<GenericDeleteRecord> recordsToBeDeleted = new ArrayList<>();

    // Add list of menu generic records.
    recordsToBeDeleted.addAll(menuGenericDeleteRecords);

    // Request object that contains all records.
    BatchDeleteGenericRecordsRequest batchDeleteRequest = new BatchDeleteGenericRecordsRequest();
    batchDeleteRequest.setRecords(recordsToBeDeleted.toArray(new GenericDeleteRecord[0]));

    // Execute batchDelete request.
    BasicDelete basicDelete = new BasicDelete();
    basicDelete.batchDelete(batchDeleteRequest, credentials);
  }

  public void batchDelete(
      BatchDeleteGenericRecordsRequest batchDeleteRequest, GoogleCredentials credentials)
      throws IOException {

    credentials.refreshIfExpired();
    AccessToken token = credentials.getAccessToken();

    String requestBody = objectMapper.writeValueAsString(batchDeleteRequest);
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(
                URI.create(
                    String.format(
                        "%s/v1alpha/inventory/partners/%s/feeds/%s/record:batchDelete",
                        ENDPOINT, PARTNER_ID, FEED_NAME)))
            .header("Content-Type", "application/json")
            .header("Authorization", String.format("Bearer %s", token.getTokenValue()))
            .POST(BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = null;
    try {
      response = client.send(request, BodyHandlers.ofString());
      System.out.println("Request body:" + requestBody);
      System.out.println("Response status:" + response.statusCode());
      System.out.println("Response body:" + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static <T> GenericDeleteRecord toBatchDeleteRecord(T entity, String dateModified) {
    GenericDeleteRecord genericRecord = new GenericDeleteRecord();
    try {
      String json = objectMapper.writeValueAsString(entity);
      genericRecord.setDataRecord(Base64.getEncoder().encodeToString(json.getBytes(UTF_8)));
      // Using dateModified to set delete_time. Defaulting to the current
      // timestamp for records that do not have dateModified.
      String deleteTime =
          Optional.ofNullable(dateModified)
              .orElse(OffsetDateTime.now(Clock.systemUTC()).format(TIMESTAMP_FORMATTER));
      genericRecord.setDeleteTime(deleteTime);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
    }
    return genericRecord;
  }
}

กรณีการใช้งาน

ตัวอย่างกรณีการใช้งานต่อไปนี้เป็นการอัปเดตแบบเรียลไทม์ การอัปเดตฟีดหลายรายการพร้อมกัน และเนื้อหาในระดับสูงในการเรียก API

สถานการณ์ เอนทิตีที่จะอัปเดต คำอธิบายและผล
การปิดใช้บริการ Service

คุณต้องปิดใช้บริการเนื่องจากเหตุผลที่ไม่คาดคิด

การอัปเดตแบบเรียลไทม์: อัปเดตเอนทิตี Service ที่เป็นปัญหาโดยตั้งค่าพร็อพเพอร์ตี้ isDisabled เป็น true แต่ให้พร็อพเพอร์ตี้อื่นๆ เหมือนเดิม

ฟีดแบบสมบูรณ์: โปรดอัปเดตเอนทิตีจากฟีดแบบสมบูรณ์เพื่อตั้งค่า isDisabled เป็น true ก่อนการดึงข้อมูลครั้งถัดไปของ Google ไม่เช่นนั้นระบบจะเปิดใช้เอนทิตีอีกครั้ง

สินค้าบางรายการหมด MenuItemOffer การอัปเดตแบบเรียลไทม์: ส่งเอนทิตี MenuItemOffer ที่รวมอยู่โดยตั้งค่า inventoryLevel เป็น 0 สำหรับ MenuItem ที่ระบุ และข้อมูลอื่นๆ ทั้งหมดไม่เปลี่ยนแปลง
การเปลี่ยนแปลงราคารายการในเมนู MenuItemOffer การอัปเดตแบบเรียลไทม์: ส่งเอนทิตี MenuItemOffer ที่รวมอยู่โดยตั้งค่า price เป็นราคาที่อัปเดตแล้วสำหรับ MenuItem ที่ระบุ และข้อมูลอื่นๆ ทั้งหมดไม่มีการเปลี่ยนแปลง

เพิ่มเอนทิตีระดับบนสุดใหม่

ใช้กับเอนทิตีประเภท Menu, Restaurant และ Service เท่านั้น

Menu, Restaurant, Service

เช่น คุณต้องเพิ่มเมนูใหม่ลงในร้านอาหาร

ฟีดแบบสมบูรณ์: เพิ่มเอนทิตีในฟีดข้อมูลและรอการส่งผ่านข้อมูลเป็นกลุ่ม

ลบเอนทิตีระดับบนสุดอย่างถาวร

ใช้กับเอนทิตีประเภท Menu, Restaurant และ Service เท่านั้น

Menu, Restaurant, Service

การอัปเดตแบบเรียลไทม์: ส่งการลบอย่างชัดแจ้ง

ฟีดแบบสมบูรณ์: โปรดนำเอนทิตีออกจากฟีดแบบสมบูรณ์ก่อนการดึงข้อมูลครั้งถัดไปของ Google มิเช่นนั้นระบบจะเพิ่มเอนทิตีนั้นอีกครั้ง

เพิ่มพื้นที่นำส่งใหม่ในServiceที่เฉพาะเจาะจง ServiceArea ฟีดกลุ่ม: ส่งเอนทิตี ServiceArea ที่เป็นปัญหาโดยเก็บฟิลด์ทั้งหมดไว้เหมือนเดิม เช่นเดียวกับที่คุณทำในฟีดแบบสมบูรณ์ โดยระบุพื้นที่การแสดงโฆษณาใหม่ภายใน polygon, geoRadius หรือ postalCode
อัปเดตเวลาถึงโดยประมาณของการนำส่งใน Service ServiceHours ฟีดกลุ่ม: ส่ง ServiceHours เหมือนกับในฟีด ยกเว้น leadTimeMin ที่จะอัปเดตตาม
อัปเดตราคาการนำส่งใน Service Fee ฟีดกลุ่ม: ส่งการส่งโฆษณา Fee แบบสมบูรณ์โดยอัปเดต price
อัปเดตเวลานำส่งหรือเวลาให้บริการอาหารสำหรับซื้อกลับบ้านใน Service ServiceHours ฟีดกลุ่ม: ส่ง ServiceHours เหมือนกับในฟีด ยกเว้นพร็อพเพอร์ตี้ opens และ closes ที่จะอัปเดตตาม
Service (เปลี่ยนจำนวนเงินขั้นต่ำในการสั่งซื้อ) Fee ฟีดแบบเป็นกลุ่ม: ส่ง Fee แบบสมบูรณ์ที่มี minPrice อัปเดตแล้ว
ลบ MenuItem ถาวร Menu ฟีดกลุ่ม: ส่ง MenuItem เหมือนกับในฟีด แต่ให้ parentMenuSectionId ว่างเปล่า

เวลาในการประมวลผลงานแบบเป็นกลุ่มและการอัปเดตแบบเรียลไทม์

ระบบจะประมวลผลเอนทิตีที่อัปเดตหรือลบผ่านฟีดกลุ่มภายใน 2 ชั่วโมง ส่วนเอนทิตีที่อัปเดตผ่านการอัปเดตแบบเรียลไทม์จะได้รับการประมวลผลภายใน 5 นาที ระบบจะลบเอนทิตีที่ล้าสมัยใน 14 วัน

คุณส่งข้อมูลต่อไปนี้ให้ Google ได้

  • งานกลุ่มหลายรายการต่อวันเพื่อให้สินค้าคงคลังเป็นข้อมูลล่าสุด หรือ
  • การส่งงานเป็นกลุ่มวันละครั้งและการอัปเดตแบบเรียลไทม์เพื่อให้สินค้าคงคลังเป็นปัจจุบันอยู่เสมอ