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

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

IndexedDB क्या है?

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

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

IndexedDB से जुड़ी शर्तें

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

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 ब्लॉक में, createObjectStore() को कॉल करें, ताकि 'firstOS' नाम का ऑब्जेक्ट स्टोर बनाया जा सके.

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

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

की पाथ एक ऐसी प्रॉपर्टी है जो हमेशा मौजूद होती है और इसमें एक यूनीक वैल्यू होती है. उदाहरण के लिए, 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() और उसे डेटा पास करता है. await Promise.all कॉल को होल्ड पर रखा जा सकता है, ताकि लेन-देन पूरा होने पर कॉल खत्म हो जाए.

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

डेटा पढ़ने के लिए, openDB() तरीके का इस्तेमाल करके वापस पाए गए डेटाबेस इंस्टेंस पर, get() तरीके को कॉल करें. 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() कॉलबैक का इस्तेमाल किया जा सकता है.

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

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);

इन फ़ंक्शन के लिए रेंज, डिफ़ॉल्ट रूप से इंक्लूसिव होती है. इसका मतलब है कि इसमें वह डेटा शामिल होता है जो रेंज की सीमाओं के तौर पर तय किया गया है. उन वैल्यू को छोड़ने के लिए, रेंज को एक्सक्लूज़िव के तौर पर तय करें. इसके लिए, lowerBound() या upperBound() के लिए दूसरे आर्ग्युमेंट के तौर पर true को पास करें. इसके अलावा, 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 के बाकी ब्लॉक के ज़रिए तब तक जारी रहता है, जब तक कि वह अप-टू-डेट न हो जाए. इसलिए, उदाहरण में ब्राउज़र case 1 के ज़रिए एक्ज़ीक्यूट करना जारी रखता है. साथ ही, store ऑब्जेक्ट स्टोर पर name इंडेक्स बनाता है.

'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 का एक डेटाबेस होता है. इसमें store ऑब्जेक्ट स्टोर होता है. साथ ही, name और description इंडेक्स होते हैं.

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

IndexedDB का इस्तेमाल करने के बारे में ज़्यादा जानकारी और कॉन्टेक्स्ट के लिए, यहां दिए गए लेख पढ़ें.

IndexedDB से जुड़ा दस्तावेज़

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