العمل باستخدام IndexedDB

يتناول هذا الدليل أساسيات IndexedDB API. نحن نستخدم مكتبة جيك أرشيبالدز الوعد إلى IndexedDB تشبه إلى حد كبير واجهة برمجة التطبيقات IndexedDB API، ولكنها تستخدم الوعود التي يمكنك await للحصول على بنية أكثر إيجازًا. يؤدي ذلك إلى تبسيط واجهة برمجة التطبيقات أثناء والحفاظ على هيكله.

ما هي قاعدة البيانات المفهرسة؟

IndexedDB هو نظام تخزين NoSQL واسع النطاق يسمح بتخزين ملفات عن أي شيء في متصفح المستخدم. بالإضافة إلى البحث المعتاد، إجراءات، وتدعم أداة IndexedDB المعاملات، وهي مناسبة تمامًا تخزين كميات كبيرة من البيانات المهيكلة.

تكون كل قاعدة بيانات في IndexedDB فريدة لكل مصدر. (نطاق الموقع الإلكتروني أو النطاق الفرعي عادةً)، بمعنى أنه لا يمكن الوصول إليه أو الوصول إليه بأي مصدر آخر. حدود تخزين البيانات عادةً ما تكون كبيرة، إذا كانت موجودة على الإطلاق، إلا أنّ المتصفحات المختلفة تتعامل مع الحدود المسموح بها. وإخلاء البيانات بشكل مختلف. راجِع قسم قراءة إضافية لمعرفة مزيد من المعلومات.

مصطلحات قاعدة البيانات المفهرسة

قاعدة بيانات
أعلى مستوى في IndexedDB. وهي تحتوي على متاجر الكائنات سيحتوي على البيانات التي تريد الاحتفاظ بها. يمكنك إنشاء قواعد بيانات متعددة باستخدام أيًا كانت الأسماء التي تختارها
متجر أغراض
حزمة فردية لتخزين البيانات تشبه الجداول في قواعد البيانات الارتباطية يتوفّر عادةً متجر عناصر واحد لكل نوع (وليس بيانات JavaScript.) النوع) للبيانات التي تخزنها. على عكس جداول قاعدة البيانات، لا تكفي بيانات أنواع البيانات في مخزن لا يلزم أن تكون متسقة. على سبيل المثال، إذا كان التطبيق يحتوي على متجر عناصر people يحتوي على معلومات حول ثلاثة أشخاص، أي يمكن أن تكون الخصائص العمرية للأشخاص 53 و'twenty-five' وunknown.
الفهرس
وهو نوع من مخزن الكائنات لتنظيم البيانات في مخزن كائنات آخر (يسمى مخزن عناصر مرجعي) بواسطة خاصية فردية للبيانات. يتم استخدام الفهرس لاسترداد السجلات في مخزن الكائنات من خلال هذه السمة. على سبيل المثال، إذا كنت الأشخاص، قد ترغب في جلبهم لاحقًا حسب أسمائهم أو عمرهم أو الحيوان المفضّل لديك
العملية
التفاعل مع قاعدة البيانات:
المعاملة
يشير هذا المصطلح إلى برنامج تضمين حول عملية أو مجموعة من العمليات التي تضمن قاعدة البيانات. وسلامته. في حال تعذّر تنفيذ أحد الإجراءات في معاملة، لن يتم تنفيذ أيّ منها. وتعود قاعدة البيانات إلى الحالة التي كانت عليها قبل إجراء المعاملة بدأ. يجب أن تكون جميع عمليات القراءة أو الكتابة في 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، يمكنك إنشاء قواعد بيانات متعددة بأي أسماء تختارها. في حال حذف عدم وجود قاعدة بيانات عند محاولة فتحها، تم إنشاؤه تلقائيًا. لفتح قاعدة بيانات، استخدِم الطريقة openDB() من مكتبة idb:

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. إذا كان بإمكان الدالة متابعة، فهي تستدعي الطريقة openDB() لفتح قاعدة بيانات تسمى 'test-db1'. في هذا المثال، تم ترك كائن الأحداث الاختياري للاحتفاظ بالأشياء أمرًا بسيطًا، ولكن عليك تحديده للقيام بأي عمل مفيد باستخدام IndexedDB.

كيفية التعامل مع متاجر العناصر

تحتوي قاعدة بيانات IndexedDB على متجر كائنات واحد أو أكثر، يحتوي كل منها على وعمودًا لمفتاح، وعمود آخر للبيانات المرتبطة بهذا المفتاح.

إنشاء متاجر كائنات

يجب أن تتضمن قاعدة البيانات المفهرسة ذات البنية الجيدة مخزن عناصر واحدًا لكل نوع من البيانات التي يجب الاحتفاظ بها. على سبيل المثال، يستخدم الموقع الإلكتروني يمكن أن تحتوي الملفات الشخصية والملاحظات على متجر عناصر people يحتوي على person ومتجر كائنات notes يحتوي على note عناصر.

لضمان تكامل قاعدة البيانات، يمكنك فقط إنشاء مخازن الكائنات أو إزالتها في كائن الأحداث في استدعاء openDB(). يعرض كائن الأحداث upgrade() تتيح لك إنشاء متاجر الكائنات. عليك استدعاء createObjectStore() داخل طريقة upgrade() لإنشاء مخزن الكائنات:

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' ويعيّن email. باعتباره المفتاح الأساسي في الخيار keyPath.

يمكنك أيضًا استخدام منشئ مفاتيح مثل 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 الطلبات. وسيؤدي ذلك إلى تبسيط واجهة برمجة التطبيقات. فبدلاً من الاستماع إلى الأحداث التي تم بدؤها بواسطة الطلب، يمكنك استدعاء .then() على كائن قاعدة البيانات الذي يتم إرجاعه من openDB() لبدء التفاعلات مع قاعدة البيانات، أو 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. لكي تكتمل عند اكتمال المعاملة.

قراءة البيانات

لقراءة البيانات، يمكنك الاتصال بالرقم 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() بالوعد.

يستخدم المثال التالي طريقة get() في قاعدة البيانات 'test-db4' تخزين عناصر 'foods' للحصول على صف واحد من خلال مفتاح '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();

هيكل تفاعل قاعدة البيانات هو ذاته لبرنامج العمليات التجارية. يُرجى التأكّد من اكتمال المعاملة بالكامل بحلول بما في ذلك طريقة tx.done في الصفيف الذي تمرِّره إلى Promise.all.

الحصول على جميع البيانات

لقد استعدت حتى الآن العناصر من المتجر واحدًا تلو الآخر. يمكنك أيضًا لاسترداد كافة البيانات، أو مجموعة فرعية، من مخزن أو فهرس الكائنات باستخدام إما طريقة 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();

يستدعي هذا المثال getAll() في متجر عناصر 'foods'. يؤدي ذلك إلى إرجاع جميع الكائنات من '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 للموضع الحالي للمؤشر، يمكنك العمل على هذه القيم بأي طريقة تكون أكثر منطقية بالنسبة التطبيق. وعندما تكون مستعدًا، يمكنك استدعاء continue() لكائن cursor للانتقال إلى الصف التالي، وتنهي حلقة while عندما يشير المؤشر يصل إلى نهاية مجموعة البيانات.

استخدام المؤشرات مع النطاقات والفهارس

تتيح لك الفهارس استرجاع البيانات في متجر عناصر من خلال موقع آخر غير الأساسي. يمكنك إنشاء فهرس على أي موقع، والذي سيصبح keyPath. للفهرس، حدد نطاقًا على هذه الخاصية، ثم احصل على البيانات داخل النطاق باستخدام getAll() أو مؤشر.

حدِّد نطاقك باستخدام الكائن IDBKeyRange. وأيّ مما يلي الطرق:

تحدّد الطريقتان upperBound() وlowerBound() الحدّ الأقصى والأدنى. من النطاق.

IDBKeyRange.lowerBound(indexKey);

أو:

IDBKeyRange.upperBound(indexKey);

ويأخذ كلٌّ منهما وسيطة واحدة: قيمة keyPath في الفهرس للعنصر الذي تريده. لتحديده كالحد الأقصى أو الأدنى.

تحدّد الطريقة bound() حدًا أقصى وأدنى معًا:

IDBKeyRange.bound(lowerIndexKey, upperIndexKey);

نطاق هذه الدوال شامل بشكل افتراضي، مما يعني أنه يتضمن البيانات المحددة كحدود النطاق. لاستبعاد هذه القيم، تحديد النطاق على أنه حصري من خلال ضبط true كوسيطة ثانية lowerBound() أو upperBound()، أو كوسيطتين الثالثة والرابعة bound()، للحدود الدنيا والعليا على التوالي.

يستخدم المثال التالي فهرسًا على السمة 'price' في الكائن 'foods'. المتجر. يحتوي المتجر الآن أيضًا على نموذج مرفق به يحتوي على مدخلين الحد الأقصى والأدنى للنطاق. استخدِم الرمز التالي للبحث عن أطعمة تتضمّن الأسعار بين هذه الحدود:

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 في كائن الحدث، بالسماح لك بإضافة متاجر كائنات وفهارس جديدة إلى قاعدة البيانات.

الكائن db في استدعاء الدالة upgrade له خاصية 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، يكون هناك عادةً break بعد كل case. حظر، ولكن لم يتم استخدام ذلك عمدًا هنا. بهذه الطريقة، إذا كانت العناصر الحالية بإصدارات متأخرة من قاعدة البيانات، أو إذا لم تكن موجودة، يستمر الكود باقي الأجزاء الـ case إلى أن يتم تحديثها. لذلك في المثال، يواصل المتصفّح تنفيذ المهام من خلال case 1، ما يؤدي إلى إنشاء فهرس name على متجر عناصر store

لإنشاء فهرس 'description' على مخزن عناصر 'store'، يجب تعديل رقم الإصدار وإضافة مجموعة 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

حدود تخزين البيانات