ข้ามไปยังเนื้อหา

Object Store และ Keys

Object store คือสิ่งที่ IndexedDB ใช้แทนตารางใน database แต่ละ record ใน object store คือ JavaScript object ที่คุณเก็บเข้าไป — แต่ต่างจากตาราง SQL ตรงที่ไม่มีการบังคับโครงสร้าง คุณเก็บ object ที่มีรูปแบบต่างกันใน store เดียวกันได้ กฎโครงสร้างเพียงอย่างเดียวคือวิธีกำหนด key

เปรียบได้ดังนี้:

  • แถวในตาราง SQL มีชุดคอลัมน์ที่ตายตัว
  • record ใน object store คือ JS object ใดก็ได้ที่คุณส่งเข้าไป บวกกับ key ที่ระบุตัวตนของตัวเอง

คุณสร้าง object store โดยเรียก db.createObjectStore(name, options) บน instance ของ database method นี้ ต้อง เรียกใช้ภายใน event handler onupgradeneeded เท่านั้น — ใช้ได้เฉพาะในช่วงการอัปเกรดเวอร์ชัน

const request = indexedDB.open('my-db', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore('contacts', { keyPath: 'id' });
};

อาร์กิวเมนต์แรกคือชื่อ store อาร์กิวเมนต์ที่สองคือ object ตัวเลือกที่ควบคุมวิธีการทำงานของ key

เมื่อกำหนด keyPath store จะใช้ property บน object ที่เก็บ เป็น key ซึ่งเรียกว่า in-line key เพราะ key อยู่ภายใน object นั้นเอง

// ออบเจกต์ทุกชิ้นที่เก็บ ต้องมี property `id`
db.createObjectStore('contacts', { keyPath: 'id' });
// เรคคอร์ดที่ถูกต้อง:
store.add({ id: 1, name: 'Alice', email: '[email protected]' });
// ไม่ถูกต้อง — ไม่มีฟิลด์ `id` จะเกิด error:
store.add({ name: 'Bob' });

หากกำหนด autoIncrement: true ร่วมกับ keyPath browser จะสร้าง key ให้อัตโนมัติและเขียนลงใน object ที่ตำแหน่งที่ระบุ:

db.createObjectStore('contacts', { keyPath: 'id', autoIncrement: true });
// `id` จะถูกกรอกอัตโนมัติ:
store.add({ name: 'Alice' }); // เก็บเป็น { id: 1, name: 'Alice' }

เมื่อไม่กำหนด keyPath (หรือตั้งเป็น null) key จะ แยกออกจาก object เรียกว่า out-of-line key คุณต้องส่ง key เองเป็นอาร์กิวเมนต์ที่สองของ add() หรือ put()

db.createObjectStore('raw-blobs', { keyPath: null });
// คุณต้องระบุ key เอง:
store.add({ data: '...' }, 'blob-001');

ผสมกับ autoIncrement: true เพื่อให้ browser สร้าง key เป็นจำนวนเต็มโดยไม่แตะ object ที่เก็บ:

db.createObjectStore('logs', { autoIncrement: true });
// ไม่ต้องใส่ key — เบราว์เซอร์กำหนดเป็น 1, 2, 3, ...
store.add({ level: 'info', message: 'App started' });
โหมดkeyPathautoIncrementแหล่งที่มาของ Key
In-line (กำหนดเอง)'id'falseฟิลด์ id ของ object
In-line (อัตโนมัติ)'id'trueสร้างอัตโนมัติ เก็บไว้ใน id
Out-of-line (กำหนดเอง)(ไม่มี)falseผู้เรียกส่ง key มาเอง
Out-of-line (อัตโนมัติ)(ไม่มี)trueสร้างอัตโนมัติ ไม่อยู่ใน object

ตัวอย่างด้านล่างเปิด database ที่เวอร์ชัน 1 และสร้าง store สองอัน:

  • contacts — in-line key โดยใช้ property id
  • logs — out-of-line key แบบ auto-increment

รันดูเพื่อดูชื่อ store ทั้งสองถูกพิมพ์ออกมาใน console

Browser Storage
ตัวเลือกBenefitCost
Schemaless object storeเก็บ object รูปแบบต่างกันใน store เดียวได้ ปรับโครงสร้างข้อมูลได้ยืดหยุ่นโดยไม่ต้อง migrate schemaไม่มีการบังคับ validate รูปแบบ record ผิดพลาดเรื่อง field/type จะไปโผล่ตอน runtime แทน
In-line key (keyPath)key อยู่ใน object เดียวกัน อ่าน/debug ง่าย ไม่ต้องส่ง key แยกobject ทุกชิ้นต้องมี field ตาม keyPath เสมอ ไม่งั้น add/put error
Out-of-line keyแยก key ออกจาก object เก็บ object ที่ไม่มี field สำหรับ identity ได้ (เช่น blob)ต้องส่ง key เองทุกครั้งที่เรียก add/put เพิ่ม boilerplate
  • เรียก createObjectStore() นอก onupgradeneeded — method นี้ throw DOMException ทันทีถ้าเรียกนอก upgrade transaction เพราะการเปลี่ยน schema ทำได้เฉพาะช่วง version upgrade เท่านั้น
  • ไม่เช็ค objectStoreNames.contains() ก่อนสร้าง store ซ้ำ — เมื่อเพิ่มเวอร์ชันแล้ว onupgradeneeded รันโค้ด migrate เดิมซ้ำ การเรียก createObjectStore ด้วยชื่อที่มีอยู่แล้วจะ throw error
  • สับสนว่า out-of-line key ถูกเก็บใน object — เมื่อไม่กำหนด keyPath key จะไม่ปรากฏใน object ที่ได้จาก get()/getAll() เลย ต้องดูจาก IDBCursor.key หรือ argument ที่ส่งตอน add() แทน

💡 ตัวอย่างจากของจริง

Notion — แยก object store ตามประเภทข้อมูล เช่น pages กับ blocks โดยใช้ in-line key id เพื่อให้ sync กับ record บน server ตรงกันโดยตรง

Google Docs — เก็บ edit history เป็น out-of-line key แบบ auto-increment ในแต่ละ document เพื่อรักษาลำดับเวลาของการแก้ไขโดยไม่ต้องผูก timestamp ไว้ใน object เอง

ต้องส่ง option ใดเข้า createObjectStore เพื่อให้ใช้ property บนออบเจกต์เป็น key?
createObjectStore() สามารถเรียกใช้ได้ในสถานการณ์ใด?
คุณสร้างสโตร์ด้วย { autoIncrement: true } และไม่มี keyPath — key ที่สร้างขึ้นอัตโนมัติถูกเก็บไว้ที่ไหน?