IndexedDB के साथ काम करना

इस गाइड में IndexedDB API की बुनियादी बातों के बारे में बताया गया है. हम जेक आर्चिबाल्ड की IndexedDB Promised लाइब्रेरी का इस्तेमाल कर रहे हैं. यह IndexedDB API से काफ़ी मिलती-जुलती है. हालांकि, इसमें प्रॉमिस का इस्तेमाल किया जाता है. छोटे सिंटैक्स के लिए, await की मदद ली जा सकती है. यह एपीआई के स्ट्रक्चर को बनाए रखते हुए, एपीआई को आसान बनाता है.

IndexedDB क्या है?

IndexedDB एक बड़े लेवल का NoSQL स्टोरेज सिस्टम है. यह उपयोगकर्ता के ब्राउज़र में किसी भी चीज़ को सेव करने की अनुमति देता है. सामान्य खोज, पाएं, और पुट कार्रवाइयों के अलावा, IndexedDB लेन-देन की भी सुविधा देता है. साथ ही, यह बड़ी मात्रा में स्ट्रक्चर्ड डेटा सेव करने के लिए भी अच्छा विकल्प है.

हर IndexedDB डेटाबेस, किसी ऑरिजिन के लिए यूनीक होता है. आम तौर पर, यह साइट डोमेन या सबडोमेन के लिए होता है. इसका मतलब है कि इसे किसी दूसरे ऑरिजिन से न तो ऐक्सेस किया जा सकता है और न ही इसे ऐक्सेस किया जा सकता है. अगर डेटा सेव करने की सीमाएं पूरी तरह से मौजूद होती हैं, तो ये आम तौर पर बड़ी होती हैं. हालांकि, अलग-अलग ब्राउज़र सीमाओं और डेटा को हटाने के तरीके को अलग-अलग तरीके से संभालते हैं. ज़्यादा जानकारी के लिए, आगे पढ़ना सेक्शन देखें.

IndexedDB के शब्द

डेटाबेस
IndexedDB का सबसे ऊंचा लेवल. इसमें ऑब्जेक्ट स्टोर होता है, जिसमें वह डेटा होता है जिसे आपको बनाए रखना है. आप जो भी नाम चुनें, उसके साथ कई डेटाबेस बना सकते हैं.
ऑब्जेक्ट स्टोर
डेटा सेव करने के लिए एक बकेट, जो रिलेशनल डेटाबेस की टेबल की तरह है. आम तौर पर, सेव किए जा रहे डेटा के हर टाइप (JavaScript डेटा टाइप नहीं) के लिए एक ऑब्जेक्ट स्टोर होता है. डेटाबेस टेबल से उलट, स्टोर में JavaScript डेटा टाइप का एक जैसा होना ज़रूरी नहीं है. उदाहरण के लिए, अगर किसी ऐप्लिकेशन में people का ऑब्जेक्ट स्टोर है, जिसमें तीन लोगों की जानकारी है, तो लोगों की उम्र वाली प्रॉपर्टी 53, 'twenty-five', और unknown हो सकती हैं.
इंडेक्स
यह एक तरह का ऑब्जेक्ट स्टोर है, जिसमें डेटा की अलग-अलग प्रॉपर्टी के हिसाब से किसी दूसरे ऑब्जेक्ट स्टोर (जिसे रेफ़रंस ऑब्जेक्ट स्टोर कहा जाता है) में डेटा को व्यवस्थित किया जाता है. इंडेक्स का इस्तेमाल, इस प्रॉपर्टी से ऑब्जेक्ट स्टोर में रिकॉर्ड वापस पाने के लिए किया जाता है. उदाहरण के लिए, अगर आपको लोगों को स्टोर करना है, तो बाद में उन्हें उनके नाम, उम्र या पसंदीदा जानवर के नाम से फ़ेच करें.
कार्रवाई
डेटाबेस के साथ इंटरैक्शन.
लेन-देन
किसी कार्रवाई या ऑपरेशन के ग्रुप के चारों ओर एक रैपर, जो डेटाबेस की इंटिग्रिटी पक्का करता है. अगर लेन-देन की कोई भी कार्रवाई काम नहीं करती, तो कोई भी कार्रवाई लागू नहीं होती. साथ ही, डेटाबेस उस स्थिति में वापस आ जाता है जैसा लेन-देन शुरू होने से पहले था. IndexedDB में मौजूद सभी पढ़ने या लिखने की कार्रवाइयां, लेन-देन का हिस्सा होनी चाहिए. इसकी मदद से, डेटाबेस पर एक ही समय में काम करने वाले दूसरे थ्रेड के साथ टकराव का जोखिम पैदा नहीं होता.
कर्सर
किसी डेटाबेस में एक से ज़्यादा रिकॉर्ड पर फिर से लागू करने का तरीका.

IndexedDB के इस्तेमाल की जांच करने का तरीका

IndexedDB करीब-करीब सभी जगहों पर काम करता है. हालांकि, अगर पुराने ब्राउज़र का इस्तेमाल किया जा रहा है, तो ऐसी स्थिति में सुविधा की पहचान करना कोई बुरा विचार नहीं है. सबसे आसान तरीका है, window ऑब्जेक्ट की जांच करना:

function indexedDBStuff () {
  // Check for IndexedDB support:
  if (!('indexedDB' in window)) {
    // Can't use IndexedDB
    console.log("This browser doesn't support IndexedDB");
    return;
  } else {
    // Do IndexedDB stuff here:
    // ...
  }
}

// Run IndexedDB code:
indexedDBStuff();

डेटाबेस खोलने का तरीका

IndexedDB से, आपके पास चुने गए किसी भी नाम के साथ कई डेटाबेस बनाने का विकल्प होगा. अगर किसी डेटाबेस को खोलने की कोशिश करते समय वह मौजूद नहीं होता है, तो वह अपने-आप बन जाता है. डेटाबेस खोलने के लिए, idb लाइब्रेरी में मौजूद openDB() तरीके का इस्तेमाल करें:

import {openDB} from 'idb';

async function useDB () {
  // Returns a promise, which makes `idb` usable with async-await.
  const dbPromise = await openDB('example-database', version, events);
}

useDB();

यह तरीका ऐसा प्रॉमिस देता है जो डेटाबेस ऑब्जेक्ट तक पहुंचाता है. openDB() तरीके का इस्तेमाल करते समय, डेटाबेस सेट अप करने के लिए नाम, वर्शन नंबर, और इवेंट ऑब्जेक्ट दें.

यहां, कॉन्टेक्स्ट के हिसाब से openDB() तरीके का एक उदाहरण दिया गया है:

import {openDB} from 'idb';

async function useDB () {
  // Opens the first version of the 'test-db1' database.
  // If the database does not exist, it will be created.
  const dbPromise = await openDB('test-db1', 1);
}

useDB();

पहचान छिपाने वाले फ़ंक्शन के सबसे ऊपर, IndexedDB सपोर्ट की जांच करें. अगर ब्राउज़र IndexedDB के साथ काम नहीं करता है, तो इससे फ़ंक्शन से बाहर निकल जाता है. अगर फ़ंक्शन काम करना जारी रखता है, तो वह 'test-db1' नाम का डेटाबेस खोलने के लिए, openDB() तरीके को कॉल करता है. इस उदाहरण में, चीज़ों को आसान बनाए रखने के लिए वैकल्पिक इवेंट ऑब्जेक्ट को छोड़ दिया गया है, लेकिन IndexedDB के साथ कोई सही काम करने के लिए आपको इसे तय करना होगा.

ऑब्जेक्ट स्टोर के साथ काम करने का तरीका

IndexedDB डेटाबेस में एक या उससे ज़्यादा ऑब्जेक्ट स्टोर होते हैं. इनमें से हर स्टोर में, कुंजी के लिए एक कॉलम और उस कुंजी से जुड़े डेटा के लिए दूसरा कॉलम होता है.

ऑब्जेक्ट स्टोर बनाएं

अच्छी तरह से स्ट्रक्चर किए गए IndexedDB डेटाबेस में हर उस तरह के डेटा के लिए एक ऑब्जेक्ट स्टोर होना चाहिए जिसे बनाए रखने की ज़रूरत है. उदाहरण के लिए, अगर किसी साइट पर उपयोगकर्ता की प्रोफ़ाइल और नोट बने रहते हैं, तो हो सकता है कि उस साइट में person ऑब्जेक्ट वाला people ऑब्जेक्ट स्टोर और note ऑब्जेक्ट वाला notes ऑब्जेक्ट स्टोर हो.

डेटाबेस को पूरी सुरक्षा देने के लिए, openDB() कॉल के इवेंट ऑब्जेक्ट में सिर्फ़ ऑब्जेक्ट स्टोर बनाए या हटाए जा सकते हैं. इवेंट ऑब्जेक्ट, upgrade() तरीके को दिखाता है, जिससे आप ऑब्जेक्ट स्टोर बना सकते हैं. ऑब्जेक्ट स्टोर बनाने के लिए, upgrade() तरीके में मौजूद createObjectStore() तरीके को कॉल करें:

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('example-database', 1, {
    upgrade (db) {
      // Creates an object store:
      db.createObjectStore('storeName', options);
    }
  });
}

createStoreInDB();

इस तरीके में ऑब्जेक्ट स्टोर का नाम और वैकल्पिक कॉन्फ़िगरेशन ऑब्जेक्ट का इस्तेमाल किया जाता है. इससे ऑब्जेक्ट स्टोर के लिए अलग-अलग प्रॉपर्टी तय की जा सकती हैं.

यहां createObjectStore() को इस्तेमाल करने का एक उदाहरण दिया गया है:

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db1', 1, {
    upgrade (db) {
      console.log('Creating a new object store...');

      // Checks if the object store exists:
      if (!db.objectStoreNames.contains('people')) {
        // If the object store does not exist, create it:
        db.createObjectStore('people');
      }
    }
  });
}

createStoreInDB();

इस उदाहरण में, ऑब्जेक्ट स्टोर बनाने के लिए, openDB() तरीके में एक इवेंट ऑब्जेक्ट पास किया जाता है. साथ ही, पहले की तरह ही, ऑब्जेक्ट स्टोर बनाने का काम, इवेंट ऑब्जेक्ट के upgrade() तरीके में ही किया जाता है. हालांकि, अगर पहले से मौजूद कोई ऑब्जेक्ट स्टोर बनाने की कोशिश की जाती है, तो ब्राउज़र गड़बड़ी का मैसेज दिखाता है. इसलिए, हमारा सुझाव है कि आप createObjectStore() तरीके को if स्टेटमेंट में रैप करें. इससे यह पता चलता है कि ऑब्जेक्ट स्टोर मौजूद है या नहीं. if ब्लॉक के अंदर, 'firstOS' नाम का ऑब्जेक्ट स्टोर बनाने के लिए, createObjectStore() को कॉल करें.

मुख्य कुंजियां तय करने का तरीका

ऑब्जेक्ट स्टोर को तय करते समय, यह तय किया जा सकता है कि स्टोर में मुख्य कुंजी का इस्तेमाल करके डेटा की पहचान कैसे की जाए. आप कुंजी का पाथ बताकर या कुंजी जनरेटर का इस्तेमाल करके मुख्य कुंजी तय कर सकते हैं.

मुख्य पाथ ऐसी प्रॉपर्टी है जो हमेशा मौजूद रहती है और उसमें यूनीक वैल्यू होती है. उदाहरण के लिए, people ऑब्जेक्ट स्टोर के मामले में, आपके पास ईमेल पते को मुख्य पाथ के तौर पर चुनने का विकल्प है:

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('people')) {
        db.createObjectStore('people', { keyPath: 'email' });
      }
    }
  });
}

createStoreInDB();

इस उदाहरण में, 'people' नाम का एक ऑब्जेक्ट स्टोर बनाया जाता है. साथ ही, keyPath विकल्प में email प्रॉपर्टी को मुख्य कुंजी के तौर पर असाइन किया जाता है.

कुंजी जनरेटर का भी इस्तेमाल किया जा सकता है, जैसे कि autoIncrement. कुंजी जनरेटर, ऑब्जेक्ट स्टोर में जोड़े गए हर ऑब्जेक्ट के लिए एक यूनीक वैल्यू बनाता है. डिफ़ॉल्ट रूप से, अगर कोई कुंजी तय नहीं की जाती है, तो IndexedDB एक कुंजी बनाता है और उसे डेटा से अलग से सेव करता है.

यहां दिए गए उदाहरण में, 'notes' नाम का एक ऑब्जेक्ट स्टोर बनाया गया है. साथ ही, मुख्य कुंजी को अपने-आप बढ़ने वाले नंबर के तौर पर अपने-आप असाइन होने के लिए सेट किया गया है:

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('notes')) {
        db.createObjectStore('notes', { autoIncrement: true });
      }
    }
  });
}

createStoreInDB();

यह उदाहरण, पिछले उदाहरण से मिलता-जुलता है. हालांकि, इस बार अपने-आप बढ़ने वाली वैल्यू को, साफ़ तौर पर 'id' नाम वाली प्रॉपर्टी को असाइन किया गया है.

import {openDB} from 'idb';

async function createStoreInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('logs')) {
        db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
      }
    }
  });
}

createStoreInDB();

कुंजी तय करने के लिए कौनसा तरीका चुनना है, यह आपके डेटा के हिसाब से तय होता है. अगर आपके डेटा में ऐसी प्रॉपर्टी है जो हमेशा यूनीक होती है, तो उसे keyPath बनाया जा सकता है. अगर ऐसा नहीं है, तो अपने-आप बढ़ने वाली वैल्यू का इस्तेमाल करें.

यह कोड तीन ऑब्जेक्ट स्टोर बनाता है, जो ऑब्जेक्ट स्टोर में मुख्य कुंजियों को तय करने के अलग-अलग तरीकों को दिखाते हैं:

import {openDB} from 'idb';

async function createStoresInDB () {
  const dbPromise = await openDB('test-db2', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('people')) {
        db.createObjectStore('people', { keyPath: 'email' });
      }

      if (!db.objectStoreNames.contains('notes')) {
        db.createObjectStore('notes', { autoIncrement: true });
      }

      if (!db.objectStoreNames.contains('logs')) {
        db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
      }
    }
  });
}

createStoresInDB();

इंडेक्स तय करने का तरीका

इंडेक्स एक तरह के ऑब्जेक्ट स्टोर होते हैं. इनका इस्तेमाल किसी तय प्रॉपर्टी के ज़रिए, रेफ़रंस ऑब्जेक्ट स्टोर से डेटा पाने के लिए किया जाता है. इंडेक्स, रेफ़रंस ऑब्जेक्ट स्टोर के अंदर होता है और उसमें वही डेटा होता है. हालांकि, यह रेफ़रंस स्टोर की मुख्य कुंजी के बजाय, बताई गई प्रॉपर्टी को अपने मुख्य पाथ के तौर पर इस्तेमाल करता है. ऑब्जेक्ट स्टोर बनाते समय इंडेक्स किए जाने चाहिए. इनका इस्तेमाल, आपके डेटा पर यूनीक कंस्ट्रेंट तय करने के लिए किया जा सकता है.

इंडेक्स बनाने के लिए, किसी ऑब्जेक्ट स्टोर इंस्टेंस पर createIndex() तरीके को कॉल करें:

import {openDB} from 'idb';

async function createIndexInStore() {
  const dbPromise = await openDB('storeName', 1, {
    upgrade (db) {
      const objectStore = db.createObjectStore('storeName');

      objectStore.createIndex('indexName', 'property', options);
    }
  });
}

createIndexInStore();

यह तरीका एक इंडेक्स ऑब्जेक्ट बनाता है और दिखाता है. ऑब्जेक्ट स्टोर के इंस्टेंस पर createIndex() वाला तरीका, पहले तर्क के तौर पर नए इंडेक्स का नाम लेता है. वहीं दूसरा तर्क, उस डेटा की प्रॉपर्टी के बारे में बताता है जिसे आपको इंडेक्स करना है. आखिरी तर्क से, दो विकल्प तय किए जा सकते हैं. इनसे इंडेक्स के काम करने का तरीका तय होता है: unique और multiEntry. अगर unique को true पर सेट किया गया है, तो इंडेक्स किसी एक कुंजी के लिए डुप्लीकेट वैल्यू इस्तेमाल करने की अनुमति नहीं देता. इसके बाद, multiEntry यह तय करता है कि इंडेक्स की गई प्रॉपर्टी कोई कलेक्शन होने पर createIndex() कैसे काम करता है. अगर इसे true पर सेट किया जाता है, तो createIndex() हर ऐरे एलिमेंट के लिए इंडेक्स में एक एंट्री जोड़ता है. ऐसा न होने पर, यह अरे वाली एक एंट्री जोड़ता है.

यहां एक उदाहरण दिया गया है:

import {openDB} from 'idb';

async function createIndexesInStores () {
  const dbPromise = await openDB('test-db3', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('people')) {
        const peopleObjectStore = db.createObjectStore('people', { keyPath: 'email' });

        peopleObjectStore.createIndex('gender', 'gender', { unique: false });
        peopleObjectStore.createIndex('ssn', 'ssn', { unique: true });
      }

      if (!db.objectStoreNames.contains('notes')) {
        const notesObjectStore = db.createObjectStore('notes', { autoIncrement: true });

        notesObjectStore.createIndex('title', 'title', { unique: false });
      }

      if (!db.objectStoreNames.contains('logs')) {
        const logsObjectStore = db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
      }
    }
  });
}

createIndexesInStores();

इस उदाहरण में, 'people' और 'notes' ऑब्जेक्ट स्टोर के इंडेक्स हैं. इंडेक्स बनाने के लिए, पहले createObjectStore() (ऑब्जेक्ट स्टोर ऑब्जेक्ट) के नतीजे को किसी वैरिएबल में असाइन करें, ताकि आप उस पर createIndex() को कॉल कर सकें.

डेटा इस्तेमाल करने का तरीका

इस सेक्शन में डेटा बनाने, पढ़ने, अपडेट करने, और मिटाने का तरीका बताया गया है. जहां IndexedDB API, अनुरोधों का इस्तेमाल करता है, वहां प्रॉमिस का इस्तेमाल करके, ये सभी ऑपरेशन एसिंक्रोनस होते हैं. इससे, एपीआई आसान हो जाता है. अनुरोध से ट्रिगर होने वाले इवेंट को सुनने के बजाय, डेटाबेस के साथ इंटरैक्शन शुरू करने के लिए, openDB() तरीके से मिले डेटाबेस ऑब्जेक्ट पर .then() को कॉल किया जा सकता है या इसे बनाने के लिए await विकल्प चुना जा सकता है.

IndexedDB में मौजूद सभी डेटा कार्रवाइयां, ट्रांज़ैक्शन के अंदर होती हैं. हर कार्रवाई के बारे में नीचे बताया गया है:

  1. डेटाबेस ऑब्जेक्ट पाएं.
  2. डेटाबेस पर ट्रांज़ैक्शन खोलें.
  3. लेन-देन पर ऑब्जेक्ट स्टोर खोलें.
  4. ऑब्जेक्ट स्टोर पर कार्रवाई करें.

लेन-देन को किसी ऑपरेशन या ऑपरेशन के ग्रुप के लिए, एक सुरक्षित रैपर माना जा सकता है. अगर किसी लेन-देन की कोई भी कार्रवाई काम नहीं करती, तो सभी कार्रवाइयां वापस ले ली जाती हैं. ट्रांज़ैक्शन एक या एक से ज़्यादा ऑब्जेक्ट स्टोर के लिए खास होते हैं, जिन्हें ट्रांज़ैक्शन खोलते समय तय किया जाता है. वे रीड-ओनली हो सकते हैं या रीड-ओनली हो सकते हैं. इससे पता चलता है कि ट्रांज़ैक्शन में मौजूद कार्रवाइयां, डेटा को पढ़ती हैं या डेटाबेस में कोई बदलाव करती हैं.

डेटा बनाएं

डेटा बनाने के लिए, डेटाबेस इंस्टेंस पर add() तरीके को कॉल करें और वह डेटा पास करें जिसे आपको जोड़ना है. add() तरीके का पहला आर्ग्युमेंट वह ऑब्जेक्ट स्टोर है जिसमें आपको डेटा जोड़ना है. वहीं, दूसरा आर्ग्युमेंट वह ऑब्जेक्ट है जिसमें ऐसे फ़ील्ड और उससे जुड़ा डेटा होता है जिसे आपको जोड़ना है. यहां सबसे आसान उदाहरण दिया गया है, जिसमें डेटा की एक लाइन जोड़ी जाती है:

import {openDB} from 'idb';

async function addItemToStore () {
  const db = await openDB('example-database', 1);

  await db.add('storeName', {
    field: 'data'
  });
}

addItemToStore();

हर add() कॉल, लेन-देन के दौरान होता है. इसलिए, प्रॉमिस पूरा होने के बाद भी, इसका यह मतलब नहीं है कि कार्रवाई पूरी हो गई. यह पक्का करने के लिए कि जोड़ा गया है या नहीं, आपको यह देखना होगा कि transaction.done() तरीके का इस्तेमाल करके, पूरा लेन-देन पूरा हुआ है या नहीं. यह वादा, लेन-देन पूरा हो जाने पर पूरा हो जाता है. वहीं, लेन-देन की गड़बड़ियां होने पर इसे अस्वीकार कर दिया जाता है. आपको सभी "राइट" ऑपरेशन के लिए यह जाँच करनी चाहिए, क्योंकि डेटाबेस में हुए बदलाव असल में हुए हैं, यह जानने का यही एक तरीक़ा है.

यह कोड, लेन-देन में add() तरीके का इस्तेमाल करने का तरीका दिखाता है:

import {openDB} from 'idb';

async function addItemsToStore () {
  const db = await openDB('test-db4', 1, {
    upgrade (db) {
      if (!db.objectStoreNames.contains('foods')) {
        db.createObjectStore('foods', { keyPath: 'name' });
      }
    }
  });
  
  // Create a transaction on the 'foods' store in read/write mode:
  const tx = db.transaction('foods', 'readwrite');

  // Add multiple items to the 'foods' store in a single transaction:
  await Promise.all([
    tx.store.add({
      name: 'Sandwich',
      price: 4.99,
      description: 'A very tasty sandwich!',
      created: new Date().getTime(),
    }),
    tx.store.add({
      name: 'Eggs',
      price: 2.99,
      description: 'Some nice eggs you can cook up!',
      created: new Date().getTime(),
    }),
    tx.done
  ]);
}

addItemsToStore();

डेटाबेस खोलने (और अगर ज़रूरी हो, तो ऑब्जेक्ट स्टोर बनाने) के बाद, आपको transaction() तरीके को कॉल करके ट्रांज़ैक्शन खोलना होगा. इस तरीके में, उस स्टोर के लिए तर्क की ज़रूरत होती है जिस पर आपको लेन-देन करना है. साथ ही, इसमें मोड का भी इस्तेमाल किया जाता है. इस मामले में हमारी दिलचस्पी स्टोर को लिखित में देना है, इसलिए इस उदाहरण में 'readwrite' के बारे में बताया गया है.

अगले कदम में, ट्रांज़ैक्शन के तहत स्टोर में आइटम जोड़ना शुरू किया जाता है. पिछले उदाहरण में, हम 'foods' स्टोर पर तीन ऐसी कार्रवाइयों पर काम कर रहे हैं जिनमें हर कार्रवाई प्रॉमिस दिखती है:

  1. स्वादिष्ट सैंडविच का रिकॉर्ड जोड़ रही हूँ.
  2. कुछ अंडों के लिए रिकॉर्ड जोड़ा जा रहा है.
  3. इससे पता चलता है कि लेन-देन पूरा हो गया है (tx.done).

ये सभी कार्रवाइयां वादों पर आधारित होती हैं. इसलिए, हमें इनके पूरा होने का इंतज़ार करना होगा. इन वादों को Promise.all पूरा करना, ऐसा करने का अच्छा और आसान तरीका है. Promise.all कई वादे स्वीकार करता है और उन्हें पूरा करता है.

जोड़े जा रहे दो रिकॉर्ड के लिए, ट्रांज़ैक्शन इंस्टेंस का store इंटरफ़ेस, add() को कॉल करता है और डेटा को पास करता है. आप Promise.all कॉल को await कर सकते हैं, ताकि लेन-देन पूरा होने पर वह खत्म हो जाए.

डेटा पढ़ने की अनुमति दें

डेटा पढ़ने के लिए, डेटाबेस इंस्टेंस पर मौजूद get() तरीके को कॉल करें. यह तरीका openDB() तरीके का इस्तेमाल करके लिया गया है. get() में, स्टोर का नाम और उस ऑब्जेक्ट की मुख्य कुंजी वैल्यू होती है जिसे आपको वापस पाना है. यहां एक उदाहरण दिया गया है:

import {openDB} from 'idb';

async function getItemFromStore () {
  const db = await openDB('example-database', 1);

  // Get a value from the object store by its primary key value:
  const value = await db.get('storeName', 'unique-primary-key-value');
}

getItemFromStore();

add() की तरह ही, get() तरीके से प्रॉमिस मिलता है, ताकि आप चाहें, तो इसे await या प्रॉमिस के .then() कॉलबैक का इस्तेमाल करें.

इस उदाहरण में, 'test-db4' डेटाबेस के 'foods' ऑब्जेक्ट स्टोर में get() तरीके का इस्तेमाल किया गया है, ताकि 'name' प्राइमरी कुंजी से एक लाइन मिल सके:

import {openDB} from 'idb';

async function getItemFromStore () {
  const db = await openDB('test-db4', 1);
  const value = await db.get('foods', 'Sandwich');

  console.dir(value);
}

getItemFromStore();

डेटाबेस से एक पंक्ति फिर से पाना बहुत आसान है: डेटाबेस खोलें और उस पंक्ति का ऑब्जेक्ट स्टोर और प्राथमिक कुंजी वैल्यू तय करें जिससे आपको डेटा चाहिए. get() तरीके से प्रॉमिस मिलता है, इसलिए इसे await किया जा सकता है.

डेटा अपडेट करना

डेटा अपडेट करने के लिए, ऑब्जेक्ट स्टोर पर put() तरीके को कॉल करें. put() वाला तरीका, add() तरीके जैसा ही है. साथ ही, डेटा बनाने के लिए, add() की जगह पर इसका इस्तेमाल किया जा सकता है. यहां दिए गए उदाहरण में बताया गया है कि किसी ऑब्जेक्ट स्टोर में किसी लाइन को मुख्य कुंजी की वैल्यू के हिसाब से अपडेट करने के लिए, put() का इस्तेमाल कैसे किया जाता है:

import {openDB} from 'idb';

async function updateItemInStore () {
  const db = await openDB('example-database', 1);

  // Update a value from in an object store with an inline key:
  await db.put('storeName', { inlineKeyName: 'newValue' });

  // Update a value from in an object store with an out-of-line key.
  // In this case, the out-of-line key value is 1, which is the
  // auto-incremented value.
  await db.put('otherStoreName', { field: 'value' }, 1);
}

updateItemInStore();

दूसरे तरीकों की तरह, इस तरीके से भी प्रॉमिस मिलता है. लेन-देन के हिस्से के तौर पर, put() का भी इस्तेमाल किया जा सकता है. यहां 'foods' स्टोर को इस्तेमाल करने का उदाहरण दिया गया है, जो पहले मौजूद सैंडविच और अंडों की कीमत को अपडेट करता है:

import {openDB} from 'idb';

async function updateItemsInStore () {
  const db = await openDB('test-db4', 1);
  
  // Create a transaction on the 'foods' store in read/write mode:
  const tx = db.transaction('foods', 'readwrite');

  // Update multiple items in the 'foods' store in a single transaction:
  await Promise.all([
    tx.store.put({
      name: 'Sandwich',
      price: 5.99,
      description: 'A MORE tasty sandwich!',
      updated: new Date().getTime() // This creates a new field
    }),
    tx.store.put({
      name: 'Eggs',
      price: 3.99,
      description: 'Some even NICER eggs you can cook up!',
      updated: new Date().getTime() // This creates a new field
    }),
    tx.done
  ]);
}

updateItemsInStore();

आइटम अपडेट होने का तरीका इस बात पर निर्भर करता है कि आपने कुंजी कैसे सेट की है. अगर keyPath को सेट किया जाता है, तो ऑब्जेक्ट स्टोर में हर लाइन इनलाइन कुंजी से जुड़ी होती है. पिछला उदाहरण इस कुंजी के आधार पर पंक्तियों को अपडेट करता है. इस स्थिति में पंक्तियों को अपडेट करने पर, आपको ऑब्जेक्ट स्टोर में सही आइटम अपडेट करने के लिए, उस कुंजी की जानकारी देनी होगी. autoIncrement को मुख्य कुंजी के तौर पर सेट करके, आउट-लाइन कुंजी भी बनाई जा सकती है.

डेटा मिटाएं

डेटा मिटाने के लिए, ऑब्जेक्ट स्टोर पर delete() तरीके को कॉल करें:

import {openDB} from 'idb';

async function deleteItemFromStore () {
  const db = await openDB('example-database', 1);

  // Delete a value 
  await db.delete('storeName', 'primary-key-value');
}

deleteItemFromStore();

add() और put() की तरह, इसे भी लेन-देन के तौर पर इस्तेमाल किया जा सकता है:

import {openDB} from 'idb';

async function deleteItemsFromStore () {
  const db = await openDB('test-db4', 1);
  
  // Create a transaction on the 'foods' store in read/write mode:
  const tx = db.transaction('foods', 'readwrite');

  // Delete multiple items from the 'foods' store in a single transaction:
  await Promise.all([
    tx.store.delete('Sandwich'),
    tx.store.delete('Eggs'),
    tx.done
  ]);
}

deleteItemsFromStore();

डेटाबेस इंटरैक्शन की संरचना, अन्य ऑपरेशन की तरह ही होती है. यह जांचना न भूलें कि पूरा लेन-देन पूरा हुआ है या नहीं. इसके लिए, Promise.all को पास किए जाने वाले कलेक्शन में tx.done तरीके का इस्तेमाल करें.

सारा डेटा फ़ेच किया जा रहा है

अभी तक आपने स्टोर से एक बार में सिर्फ़ एक ऑब्जेक्ट वापस लाया है. getAll() तरीके या कर्सर का इस्तेमाल करके भी, किसी ऑब्जेक्ट स्टोर या इंडेक्स से पूरा डेटा या सबसेट पाया जा सकता है.

getAll() तरीका

किसी ऑब्जेक्ट स्टोर का पूरा डेटा वापस पाने का सबसे आसान तरीका यह है कि ऑब्जेक्ट स्टोर या इंडेक्स पर getAll() को कॉल किया जाए, जैसे कि:

import {openDB} from 'idb';

async function getAllItemsFromStore () {
  const db = await openDB('test-db4', 1);

  // Get all values from the designated object store:
  const allValues = await db.getAll('storeName');

  console.dir(allValues);
}

getAllItemsFromStore();

यह तरीका किसी भी सीमा के बिना, ऑब्जेक्ट स्टोर में सभी ऑब्जेक्ट दिखाता है. यह किसी ऑब्जेक्ट स्टोर से सभी वैल्यू पाने का सबसे सीधा तरीका है, लेकिन यह सबसे कम सुविधाजनक भी है.

import {openDB} from 'idb';

async function getAllItemsFromStore () {
  const db = await openDB('test-db4', 1);

  // Get all values from the designated object store:
  const allValues = await db.getAll('foods');

  console.dir(allValues);
}

getAllItemsFromStore();

इस उदाहरण में, 'foods' ऑब्जेक्ट स्टोर पर getAll() को कॉल किया गया है. इससे, 'foods' के सभी ऑब्जेक्ट दिखते हैं. ये ऑब्जेक्ट, मुख्य कुंजी के हिसाब से क्रम में दिखते हैं.

कर्सर इस्तेमाल करने का तरीका

कर्सर की मदद से, एक से ज़्यादा ऑब्जेक्ट को आसानी से वापस पाया जा सकता है. कर्सर, ऑब्जेक्ट स्टोर में हर ऑब्जेक्ट को एक-एक करके चुनता है या इंडेक्स करता है. इससे, डेटा को चुने जाने पर उस पर कुछ करने की सुविधा मिलती है. अन्य डेटाबेस ऑपरेशन की तरह ही कर्सर, लेन-देन में काम करते हैं.

कर्सर बनाने के लिए, ट्रांज़ैक्शन के तहत ऑब्जेक्ट स्टोर पर openCursor() को कॉल करें. पिछले उदाहरणों में से 'foods' स्टोर का इस्तेमाल करके, ऑब्जेक्ट स्टोर में डेटा की सभी लाइनों पर कर्सर को आगे बढ़ाने का तरीका बताया गया है:

import {openDB} from 'idb';

async function getAllItemsFromStoreWithCursor () {
  const db = await openDB('test-db4', 1);
  const tx = await db.transaction('foods', 'readonly');

  // Open a cursor on the designated object store:
  let cursor = await tx.store.openCursor();

  // Iterate on the cursor, row by row:
  while (cursor) {
    // Show the data in the row at the current cursor position:
    console.log(cursor.key, cursor.value);

    // Advance the cursor to the next row:
    cursor = await cursor.continue();
  }
}

getAllItemsFromStoreWithCursor();

इस मामले में लेन-देन 'readonly' मोड में खुलता है और इसके openCursor तरीके को कॉल किया जाता है. बाद के while लूप में, कर्सर की मौजूदा जगह की लाइन की key और value प्रॉपर्टी रीड हो सकती हैं. साथ ही, उन वैल्यू को उस तरीके से इस्तेमाल किया जा सकता है जो आपके ऐप्लिकेशन के लिए सबसे सही हो. जब आप तैयार हों, तब अगली लाइन पर जाने के लिए cursor ऑब्जेक्ट के continue() तरीके को कॉल करें और डेटासेट के आखिर तक पहुंचने पर while लूप खत्म हो जाता है.

रेंज और इंडेक्स के साथ कर्सर का इस्तेमाल करें

इंडेक्स की मदद से, ऑब्जेक्ट स्टोर में प्राइमरी कुंजी के अलावा किसी दूसरी प्रॉपर्टी का इस्तेमाल करके डेटा फ़ेच किया जा सकता है. किसी भी प्रॉपर्टी पर इंडेक्स बनाया जा सकता है, जो इंडेक्स के लिए keyPath बन जाता है. इसके बाद, उस प्रॉपर्टी के लिए कोई रेंज तय करें और getAll() या कर्सर का इस्तेमाल करके रेंज में डेटा पाएं.

IDBKeyRange ऑब्जेक्ट और इनमें से किसी एक तरीके का इस्तेमाल करके, अपनी रेंज तय करें:

upperBound() और lowerBound() तरीके से, रेंज की ऊपरी और निचली सीमाएं तय की जाती हैं.

IDBKeyRange.lowerBound(indexKey);

या:

IDBKeyRange.upperBound(indexKey);

इनमें से हर एक तर्क एक तर्क लेता है: उस आइटम के लिए इंडेक्स का keyPath मान जिसे आपको ऊपरी या निचली सीमा के तौर पर बताना है.

bound() तरीके से ऊपरी और निचली, दोनों सीमा तय होती है:

IDBKeyRange.bound(lowerIndexKey, upperIndexKey);

इन फ़ंक्शन की रेंज में डिफ़ॉल्ट रूप से शामिल होता है. इसका मतलब है कि इसमें वह डेटा शामिल है जो रेंज की सीमाओं के तौर पर तय किया गया है. उन वैल्यू को छोड़ने के लिए, रेंज को खास के तौर पर तय करें. इसके लिए, true को lowerBound() या upperBound() के लिए दूसरे तर्क के तौर पर या bound() के तीसरे और चौथे आर्ग्युमेंट के तौर पर, निचली और ऊपरी सीमाओं के लिए पास करें.

अगले उदाहरण में, 'foods' ऑब्जेक्ट स्टोर में 'price' प्रॉपर्टी के इंडेक्स का इस्तेमाल किया गया है. स्टोर के साथ अब एक फ़ॉर्म भी अटैच किया गया है. इसमें रेंज की ऊपरी और निचली सीमाओं के लिए दो इनपुट शामिल किए गए हैं. इन सीमाओं के बीच में कीमतों वाले खाने का पता लगाने के लिए, इस कोड का इस्तेमाल करें:

import {openDB} from 'idb';

async function searchItems (lower, upper) {
  if (!lower === '' && upper === '') {
    return;
  }

  let range;

  if (lower !== '' && upper !== '') {
    range = IDBKeyRange.bound(lower, upper);
  } else if (lower === '') {
    range = IDBKeyRange.upperBound(upper);
  } else {
    range = IDBKeyRange.lowerBound(lower);
  }

  const db = await openDB('test-db4', 1);
  const tx = await db.transaction('foods', 'readonly');
  const index = tx.store.index('price');

  // Open a cursor on the designated object store:
  let cursor = await index.openCursor(range);

  if (!cursor) {
    return;
  }

  // Iterate on the cursor, row by row:
  while (cursor) {
    // Show the data in the row at the current cursor position:
    console.log(cursor.key, cursor.value);

    // Advance the cursor to the next row:
    cursor = await cursor.continue();
  }
}

// Get items priced between one and four dollars:
searchItems(1.00, 4.00);

उदाहरण कोड पहले, सीमाओं की वैल्यू इकट्ठा करता है और देखता है कि सीमाएं मौजूद हैं या नहीं. कोड का अगला ब्लॉक, यह तय करता है कि वैल्यू के आधार पर रेंज को सीमित करने के लिए, कौनसा तरीका इस्तेमाल करना है. डेटाबेस इंटरैक्शन में, हमेशा की तरह ट्रांज़ैक्शन पर ऑब्जेक्ट स्टोर खोलें. इसके बाद, ऑब्जेक्ट स्टोर पर 'price' इंडेक्स खोलें. 'price' इंडेक्स की मदद से, आपको कीमत के हिसाब से आइटम खोजने की सुविधा मिलती है.

इसके बाद कोड, इंडेक्स पर कर्सर को खोलता है और रेंज में पास करता है. कर्सर, रेंज में पहले ऑब्जेक्ट को दिखाने वाला प्रॉमिस दिखाता है. वहीं, रेंज में कोई डेटा न होने पर, undefined प्रॉमिस दिखाता है. cursor.continue() वाला तरीका, अगले ऑब्जेक्ट को दिखाने वाला कर्सर दिखाता है. यह लूप में तब तक चलता रहता है, जब तक आप रेंज के आखिर तक नहीं पहुंच जाते.

डेटाबेस का वर्शन बनाना

openDB() वाले तरीके को कॉल करने पर, दूसरे पैरामीटर में डेटाबेस का वर्शन नंबर तय किया जा सकता है. इस गाइड में दिए गए सभी उदाहरणों में, वर्शन को 1 पर सेट किया गया है. हालांकि, अगर आपको डेटाबेस में किसी तरह का बदलाव करना है, तो उसे नए वर्शन में अपग्रेड किया जा सकता है. अगर बताया गया वर्शन, मौजूदा डेटाबेस के वर्शन से बड़ा है, तो इवेंट ऑब्जेक्ट में upgrade कॉलबैक लागू होता है. इससे डेटाबेस में नए ऑब्जेक्ट स्टोर और इंडेक्स जोड़े जा सकते हैं.

upgrade कॉलबैक में db ऑब्जेक्ट में एक खास oldVersion प्रॉपर्टी होती है. इससे पता चलता है कि ब्राउज़र के पास उस डेटाबेस का वर्शन नंबर है या नहीं. आपके पास इस वर्शन नंबर को switch स्टेटमेंट में पास करने का विकल्प है. इससे मौजूदा डेटाबेस वर्शन नंबर के आधार पर, upgrade कॉलबैक में कोड के ब्लॉक एक्ज़ीक्यूट किए जा सकते हैं. यहां एक उदाहरण दिया गया है:

import {openDB} from 'idb';

const db = await openDB('example-database', 2, {
  upgrade (db, oldVersion) {
    switch (oldVersion) {
      case 0:
        // Create first object store:
        db.createObjectStore('store', { keyPath: 'name' });

      case 1:
        // Get the original object store, and create an index on it:
        const tx = await db.transaction('store', 'readwrite');
        tx.store.createIndex('name', 'name');
    }
  }
});

इस उदाहरण में, डेटाबेस के सबसे नए वर्शन को 2 पर सेट किया गया है. जब यह कोड पहली बार काम करता है, तो इसका मतलब है कि ब्राउज़र में डेटाबेस मौजूद नहीं है. इसलिए, oldVersion 0 है और switch स्टेटमेंट case 0 से शुरू होता है. उदाहरण में, ऐसा करने से डेटाबेस में एक 'store' ऑब्जेक्ट स्टोर जुड़ जाता है.

अहम जानकारी: switch स्टेटमेंट में, आम तौर पर हर case ब्लॉक के बाद break आता है. हालांकि, यहां जान-बूझकर इसका इस्तेमाल नहीं किया गया है. इस तरह, अगर मौजूदा डेटाबेस के कुछ वर्शन पीछे हैं या यह मौजूद नहीं है, तो कोड बाकी case ब्लॉक में तब तक जारी रहता है, जब तक वह अप-टू-डेट नहीं हो जाता. उदाहरण में, ब्राउज़र store ऑब्जेक्ट स्टोर पर name इंडेक्स बनाते हुए, case 1 के ज़रिए प्रोसेस करना जारी रखता है.

'store' ऑब्जेक्ट स्टोर पर 'description' इंडेक्स बनाने के लिए, वर्शन नंबर अपडेट करें और इस तरह नया case ब्लॉक जोड़ें:

import {openDB} from 'idb';

const db = await openDB('example-database', 3, {
  upgrade (db, oldVersion) {
    switch (oldVersion) {
      case 0:
        // Create first object store:
        db.createObjectStore('store', { keyPath: 'name' });

      case 1:
        // Get the original object store, and create an index on it:
        const tx = await db.transaction('store', 'readwrite');
        tx.store.createIndex('name', 'name');

      case 2:
        const tx = await db.transaction('store', 'readwrite');
        tx.store.createIndex('description', 'description');
    }
  }
});

अगर पिछले उदाहरण में बनाया गया आपका डेटाबेस अब भी ब्राउज़र में मौजूद है, तो इसके लागू होने के बाद, oldVersion 2 होगा. ब्राउज़र, case 0 और case 1 को छोड़कर, case 2 में कोड को एक्ज़ीक्यूट करता है, जिससे description इंडेक्स बनता है. इसके बाद, ब्राउज़र के वर्शन 3 पर एक डेटाबेस मौजूद होता है. इसमें name और description इंडेक्स वाला store ऑब्जेक्ट स्टोर होता है.

इसके बारे में और पढ़ें

नीचे दिए गए संसाधन IndexedDB का इस्तेमाल करने के बारे में ज़्यादा जानकारी और संदर्भ उपलब्ध कराते हैं.

IndexedDB दस्तावेज़

डेटा स्टोरेज की सीमाएं