Статьи

Работа с IndexedDB

В последнее время одной из наиболее интересных разработок в области веб-стандартов является спецификация Indexed Database (для краткости IndexedDB). В свое удовольствие вы можете прочитать спецификацию самостоятельно. В этом уроке я объясню эту функцию и, надеюсь, вдохновлю вас использовать эту мощную функцию самостоятельно.


В качестве спецификации IndexedDB в настоящее время является Рекомендацией кандидата.

В двух словах, IndexedDB предоставляет вам возможность хранить большие объемы данных в браузере вашего пользователя. Любое приложение, которому необходимо передавать много данных по проводам, может получить выгоду от возможности хранить эти данные на клиенте. Конечно, хранение — это только часть уравнения. IndexedDB также предоставляет мощный API-интерфейс поиска на основе индекса для извлечения необходимых данных.

Вы можете спросить, чем IndexedDB отличается от других механизмов хранения?

Куки поддерживаются очень хорошо, но имеют юридические последствия и ограниченное пространство для хранения. Кроме того — они отправляются туда и обратно на сервер с каждым запросом, полностью сводя на нет преимущества хранения на стороне клиента.

Локальное хранилище также очень хорошо поддерживается, но ограничено с точки зрения общего объема хранилища, которое вы можете использовать. Локальное хранилище не обеспечивает истинного «поискового» API, поскольку данные извлекаются только через ключевые значения. Локальное хранилище отлично подходит для «конкретных» вещей, которые вы, возможно, захотите хранить, например, предпочтений, тогда как IndexedDB лучше подходит для данных Ad Hoc (во многом как база данных).

Прежде чем мы пойдем дальше, давайте поговорим о состоянии IndexedDB с точки зрения поддержки браузера. В качестве спецификации IndexedDB в настоящее время является Рекомендацией кандидата. На данный момент люди, стоящие за спецификацией, довольны ею, но теперь ждут отзывов от сообщества разработчиков. Спецификация может измениться между настоящим моментом и последним этапом, Рекомендацией W3C. В целом, браузеры, поддерживающие IndexedDB, теперь все работают достаточно согласованно, но разработчики должны быть готовы справиться с префиксами и принять к сведению обновления в будущем.

Что касается браузеров, поддерживающих IndexedDB, у вас есть небольшая дилемма. Поддержка чертовски хороша для настольных компьютеров, но практически отсутствует для мобильных устройств. Давайте посмотрим, что говорит отличный сайт CanIUse.com:

CanIUse Отчет для IndexedDB

Chrome для Android поддерживает эту функцию, но очень немногие в настоящее время используют этот браузер на устройствах Android. Означает ли отсутствие мобильной поддержки, что вы не должны ее использовать? Конечно, нет! Надеемся, что все наши читатели знакомы с концепцией прогрессивного улучшения. Такие функции, как IndexedDB, могут быть добавлены в ваше приложение таким образом, чтобы оно не нарушалось в неподдерживаемых браузерах. Вы можете использовать библиотеки-оболочки для переключения на WebSQL на мобильных устройствах или просто пропустить локальное хранение данных на своих мобильных клиентах. Лично я считаю, что возможность кэшировать большие блоки данных на клиенте достаточно важна, чтобы использовать ее сейчас даже без поддержки мобильных устройств.


Мы рассмотрели спецификацию и поддержку, теперь давайте рассмотрим использование этой функции. Самое первое, что мы должны сделать, это проверить поддержку IndexedDB. Хотя существуют инструменты, которые предоставляют общие способы проверки функций браузера, мы можем сделать это намного проще, поскольку мы просто проверяем одну конкретную вещь.

1
2
3
4
5
6
7
8
9
document.addEventListener(«DOMContentLoaded», function(){
 
    if(«indexedDB» in window) {
        console.log(«YES!!! I CAN DO IT!!! WOOT!!!»);
    } else {
        console.log(«I has a sad.»);
    }
 
},false);

Приведенный выше фрагмент кода (доступный в test1.html если вы загрузите zip-файл, прилагаемый к этой статье) использует событие DOMContentLoaded для ожидания загрузки страницы. (Ладно, это вроде очевидно, но я признаю, что это может быть не знакомо людям, которые использовали только jQuery.) Затем я просто вижу, существует ли indexedDB в объекте window и если да, то мы готовы идти. Это самый простой пример, но, как правило, мы, вероятно, захотим сохранить его, чтобы потом знать, сможем ли мы использовать эту функцию. Вот немного более test2.html пример ( test2.html ).

1
2
3
4
5
6
7
8
9
var idbSupported = false;
 
document.addEventListener(«DOMContentLoaded», function(){
 
    if(«indexedDB» in window) {
        idbSupported = true;
    }
 
},false);

Все, что я сделал, — это создал глобальную переменную idbSupported , которую можно использовать в качестве флага, чтобы увидеть, может ли текущий браузер использовать IndexedDB.


IndexedDB, как вы можете себе представить, использует базы данных. Чтобы было ясно, это не реализация SQL Server. Эта база данных является локальной для браузера и доступна только пользователю. Базы данных IndexedDB подчиняются тем же правилам, что и файлы cookie и локальное хранилище. База данных уникальна для домена, из которого она была загружена. Так, например, база данных с именем «Foo», созданная на foo.com, не будет конфликтовать с базой данных с тем же именем на goo.com. Он не только не будет конфликтовать, но и не будет доступен для других доменов. Вы можете хранить данные для своего веб-сайта, зная, что другой веб-сайт не сможет получить к ним доступ.

Открытие базы данных осуществляется с помощью команды open. В основном использовании вы предоставляете имя и версию. Версия очень важна по причинам, о которых я расскажу позже. Вот простой пример:

1
var openRequest = indexedDB.open(«test»,1);

Открытие базы данных является асинхронной операцией. Чтобы обработать результат этой операции, вам нужно добавить несколько слушателей событий. Существует четыре различных типа событий, которые можно инициировать:

  • успех
  • ошибка
  • upgradeneeded
  • блокированный

Вы, вероятно, можете догадаться, что подразумевает успех и ошибка. Событие upgradedeneeded используется как при первом открытии базы данных пользователем, так и при изменении версии. Заблокированные обычно не случаются, но могут срабатывать, если предыдущее соединение никогда не было закрыто.

Как правило, случается так, что при первом попадании на ваш сайт сработает событие upgradedeneeded. После этого — просто обработчик успеха. Давайте посмотрим на простой пример ( test3.html ).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var idbSupported = false;
var db;
 
document.addEventListener(«DOMContentLoaded», function(){
 
    if(«indexedDB» in window) {
        idbSupported = true;
    }
 
    if(idbSupported) {
        var openRequest = indexedDB.open(«test»,1);
 
        openRequest.onupgradeneeded = function(e) {
            console.log(«Upgrading…»);
        }
 
        openRequest.onsuccess = function(e) {
            console.log(«Success!»);
            db = e.target.result;
        }
 
        openRequest.onerror = function(e) {
            console.log(«Error»);
            console.dir(e);
        }
 
    }
 
},false);

Еще раз мы проверяем, поддерживается ли IndexedDB, и если это так, мы открываем базу данных. Здесь мы рассмотрели три события: событие, необходимое для обновления, событие успеха и событие ошибки. А пока сосредоточимся на успехе мероприятия. Событие передается обработчику через target.result . Мы скопировали это в глобальную переменную с именем db . Это то, что мы будем использовать позже, чтобы добавить данные. Если вы запустите это в своем браузере (в том, который поддерживает IndexedDB, конечно!), Вы должны увидеть сообщение об обновлении и успехе в консоли при первом запуске сценария. Второй и т. Д., Когда вы запускаете скрипт, вы должны видеть только сообщение об успехе.


Итак, мы проверили поддержку IndexedDB, подтвердили ее и открыли соединение с базой данных. Теперь нам нужно место для хранения данных. IndexedDB имеет понятие «хранилища объектов». Вы можете думать об этом как о типичной таблице базы данных. (Это гораздо более свободно, чем обычная таблица базы данных, но не беспокойтесь об этом сейчас.) Хранилища объектов имеют данные (очевидно), но также имеют путь к ключам и необязательный набор индексов. Ключевые пути в основном являются уникальными идентификаторами ваших данных и представлены в нескольких различных форматах. Индексы будут рассмотрены позже, когда мы начнем говорить о получении данных.

Теперь что-то решающее. Помните событие об обновлении, упомянутое ранее? Вы можете создавать хранилища объектов только во время события upgradedeneeded. Теперь — по умолчанию — он будет запускаться автоматически при первом входе пользователя на ваш сайт. Вы можете использовать это для создания ваших хранилищ объектов. Важно помнить, что если вам когда-нибудь понадобится изменить хранилища объектов, вам нужно обновить версию (вернуться в это открытое событие) и написать код для обработки ваших изменений. Давайте рассмотрим простой пример этого в действии.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var idbSupported = false;
var db;
 
document.addEventListener(«DOMContentLoaded», function(){
 
    if(«indexedDB» in window) {
        idbSupported = true;
    }
 
    if(idbSupported) {
        var openRequest = indexedDB.open(«test_v2»,1);
 
        openRequest.onupgradeneeded = function(e) {
            console.log(«running onupgradeneeded»);
            var thisDB = e.target.result;
 
            if(!thisDB.objectStoreNames.contains(«firstOS»)) {
                thisDB.createObjectStore(«firstOS»);
            }
 
        }
 
        openRequest.onsuccess = function(e) {
            console.log(«Success!»);
            db = e.target.result;
        }
 
        openRequest.onerror = function(e) {
            console.log(«Error»);
            console.dir(e);
        }
 
    }
 
},false);

Этот пример ( test4.html ) основан на предыдущих записях, поэтому я просто сосредоточусь на том, что нового. В событии upgradedeneeded я использовал переданную ему переменную базы данных ( thisDB ). Одним из свойств этой переменной является список существующих хранилищ объектов с именем objectStoreNames . Для людей, любопытных, это не простой массив, а «DOMStringList». Не спрашивай меня — но ты пойдешь. Мы можем использовать метод contains чтобы увидеть, существует ли наше хранилище объектов, и если нет, создать его. Это одна из немногих синхронных функций в IndexedDB, поэтому нам не нужно слушать результат.

Подводя итоги — это то, что произойдет, когда пользователь заходит на ваш сайт. В первый раз, когда они здесь, происходит событие повышенного качества. Код проверяет, существует ли хранилище объектов «firstOS». Он не будет. Следовательно — оно создано. Затем запускается обработчик успеха. Во второй раз, когда они посещают сайт, номер версии будет таким же, поэтому событие upgradedeneeded не будет запущено.

Теперь представьте, что вы хотите добавить второй объект хранилища. Все, что вам нужно сделать, это увеличить номер версии и в основном продублировать кодовый блок содержит / createObjectStore, который вы видите выше. Круто то, что ваш обновленный код будет поддерживать как новичков на сайте, так и тех, у кого уже было первое хранилище объектов. Вот пример этого ( test5.html ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
var openRequest = indexedDB.open(«test_v2»,2);
 
openRequest.onupgradeneeded = function(e) {
    console.log(«running onupgradeneeded»);
    var thisDB = e.target.result;
 
    if(!thisDB.objectStoreNames.contains(«firstOS»)) {
        thisDB.createObjectStore(«firstOS»);
    }
 
    if(!thisDB.objectStoreNames.contains(«secondOS»)) {
        thisDB.createObjectStore(«secondOS»);
    }
 
}

Когда у вас есть готовые хранилища объектов, вы можете начать добавлять данные. Это, пожалуй, один из самых крутых аспектов IndexedDB. В отличие от традиционных баз данных на основе таблиц, IndexedDB позволяет хранить объект как есть. Это означает, что вы можете взять универсальный объект JavaScript и просто сохранить его. Выполнено. Очевидно, что здесь есть некоторые предостережения, но по большей части это все.

Работа с данными требует использования транзакции. Транзакции принимают два аргумента. Первый — это массив таблиц, с которыми вы будете работать. В большинстве случаев это будет один стол. Второй аргумент — это тип транзакции. Существует два типа транзакций: readonly и readwrite. Добавление данных будет операцией чтения-записи. Давайте начнем с создания транзакции:

1
2
//Assume db is a database variable opened earlier
var transaction = db.transaction([«people»],»readwrite»);

Обратите внимание, что хранилище объектов «people» — это только то, что мы создали в примере выше. Наша следующая полная демонстрация будет использовать это. После получения транзакции вы запрашиваете хранилище объектов, с которым, как вы сказали, будете работать:

1
var store = transaction.objectStore(«people»);

Теперь, когда у вас есть магазин, вы можете добавлять данные. Это делается с помощью метода — wait for — add .

1
2
3
4
5
6
7
8
9
//Define a person
var person = {
    name:name,
    email:email,
    created:new Date()
}
 
//Perform the add
var request = store.add(person,1);

Помните, ранее мы говорили, что вы можете хранить любые данные, которые вы хотите (по большей части). Так что мой личный объект выше совершенно произвольный. Я мог бы использовать firstName и lastName вместо просто name. Я мог бы использовать свойство пола. Вы поняли идею. Второй аргумент — это ключ, используемый для уникальной идентификации данных. В этом случае мы жестко закодировали его в 1, что довольно быстро вызовет проблему. Это нормально — мы научимся это исправлять.

Операция добавления является асинхронной, поэтому давайте добавим два обработчика событий для результата.

1
2
3
4
5
6
7
8
request.onerror = function(e) {
    console.log(«Error»,e.target.error.name);
    //some type of error handler
}
 
request.onsuccess = function(e) {
    console.log(«Woot! Did it»);
}

У нас есть обработчик ошибок для ошибок и onsuccess изменений. Довольно очевидно, но давайте посмотрим полный пример. Вы можете найти это в файле test6.html .

>

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<!doctype html>
<html>
<head>
</head>
 
<body>
 
<script>
var db;
 
function indexedDBOk() {
    return «indexedDB» in window;
}
 
document.addEventListener(«DOMContentLoaded», function() {
 
    //No support?
    if(!indexedDBOk) return;
 
    var openRequest = indexedDB.open(«idarticle_people»,1);
 
    openRequest.onupgradeneeded = function(e) {
        var thisDB = e.target.result;
 
        if(!thisDB.objectStoreNames.contains(«people»)) {
            thisDB.createObjectStore(«people»);
        }
    }
 
    openRequest.onsuccess = function(e) {
        console.log(«running onsuccess»);
 
        db = e.target.result;
 
        //Listen for add clicks
        document.querySelector(«#addButton»).addEventListener(«click», addPerson, false);
    }
 
    openRequest.onerror = function(e) {
        //Do something for the error
    }
 
},false);
 
function addPerson(e) {
    var name = document.querySelector(«#name»).value;
    var email = document.querySelector(«#email»).value;
 
    console.log(«About to add «+name+»/»+email);
 
    var transaction = db.transaction([«people»],»readwrite»);
    var store = transaction.objectStore(«people»);
 
    //Define a person
    var person = {
        name:name,
        email:email,
        created:new Date()
    }
 
    //Perform the add
    var request = store.add(person,1);
 
    request.onerror = function(e) {
        console.log(«Error»,e.target.error.name);
        //some type of error handler
    }
 
    request.onsuccess = function(e) {
        console.log(«Woot! Did it»);
    }
}
</script>
 
<input type=»text» id=»name» placeholder=»Name»><br/>
<input type=»email» id=»email» placeholder=»Email»><br/>
<button id=»addButton»>Add Data</button>
 
</body>
</html>

В приведенном выше примере содержится небольшая форма с кнопкой для запуска события для сохранения данных в IndexedDB. Запустите это в своем браузере, добавьте что-нибудь в поля формы и нажмите «Добавить». Если у вас открыты инструменты разработки для вашего браузера, вы должны увидеть что-то вроде этого.

Форма ввода данных

Это прекрасное время для того, чтобы отметить, что в Chrome есть отличная программа просмотра данных IndexedDB. Если щелкнуть вкладку «Ресурсы», разверните раздел «IndexedDB», вы увидите базу данных, созданную этой демонстрацией, а также только что введенный объект.

Инструменты разработчика Chrome и IndexedDB

Для этого, нажмите кнопку Добавить данные еще раз. Вы должны увидеть ошибку в консоли:

Ошибка добавления данных снова

Сообщение об ошибке должно быть подсказкой. ConstraintError означает, что мы только что попытались добавить данные с тем же ключом, который уже существовал. Если вы помните, мы жестко закодировали этот ключ и знали, что это будет проблемой. Пришло время говорить ключи.


Ключи — это версия первичных ключей IndexedDB. Традиционные базы данных могут иметь таблицы без ключей, но в каждом хранилище объектов должен быть ключ. IndexedDB позволяет использовать пару различных типов ключей.

Первый вариант — просто указать это самостоятельно, как мы делали выше. Мы могли бы использовать логику для генерации уникальных ключей.

Второй вариант — это путь к ключу, где ключ основан на свойстве самих данных. Рассмотрим пример наших людей — мы могли бы использовать адрес электронной почты в качестве ключа.

Ваш третий вариант, и, на мой взгляд, самый простой, это использовать генератор ключей. Это работает так же, как первичный ключ autonumber и является самым простым методом указания ключей.

Ключи определяются при создании хранилищ объектов. Вот два примера — один с использованием ключевого пути и один генератор.

1
2
thisDb.createObjectStore(«test», { keyPath: «email» });
thisDb.createObjectStore(«test2», { autoIncrement: true });

Мы можем изменить нашу предыдущую демонстрацию, создав хранилище объектов с ключом autoIncrement:

1
thisDB.createObjectStore(«people», {autoIncrement:true});

Наконец, мы можем взять вызов Add, который мы использовали ранее, и удалить жестко закодированный ключ:

1
var request = store.add(person);

Это оно! Теперь вы можете добавлять данные в течение всего дня. Вы можете найти эту версию в test7.html .


Теперь давайте переключимся на чтение отдельных фрагментов данных (позже мы рассмотрим чтение больших наборов данных). Еще раз, это будет сделано в транзакции и будет асинхронным. Вот простой пример:

1
2
3
4
5
6
7
8
9
var transaction = db.transaction([«test»], «readonly»);
var objectStore = transaction.objectStore(«test»);
 
//x is some value
var ob = objectStore.get(x);
 
ob.onsuccess = function(e) {
 
}

Обратите внимание, что транзакция только для чтения. Вызов API — это простой вызов get с передаваемым ключом. Если коротко, если вы думаете, что использование IndexedDB немного многословно, обратите внимание, что многие из этих вызовов также можно объединить в цепочку. Вот точно такой же код, написанный гораздо плотнее:

1
db.transaction([«test»], «readonly»).objectStore(«test»).get(X).onsuccess = function(e) {}

Лично я все еще нахожу IndexedDB немного сложным, поэтому я предпочитаю «сломанный» подход, чтобы помочь мне следить за тем, что происходит.

Результатом обработчика onsuccess get является объект, который вы сохранили ранее. Когда у вас есть этот объект, вы можете делать все, что хотите. В нашей следующей демонстрации ( test8.html ) мы добавили простое поле формы, чтобы вы могли ввести ключ и распечатать результат. Вот пример:

Получение данных

Обработчик для кнопки «Получить данные» находится ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function getPerson(e) {
    var key = document.querySelector(«#key»).value;
    if(key === «» || isNaN(key)) return;
 
    var transaction = db.transaction([«people»],»readonly»);
    var store = transaction.objectStore(«people»);
 
    var request = store.get(Number(key));
 
    request.onsuccess = function(e) {
 
        var result = e.target.result;
        console.dir(result);
        if(result) {
            var s = «&lt;h2>Key «+key+»&lt;/h2>&lt;p>»;
            for(var field in result) {
                s+= field+»=»+result[field]+»&lt;br/>»;
            }
            document.querySelector(«#status»).innerHTML = s;
        } else {
            document.querySelector(«#status»).innerHTML = «&lt;h2>No match&lt;/h2>»;
        }
    }
}

По большей части это должно быть само за себя. Получите значение из поля и выполните вызов get для хранилища объектов, полученного из транзакции. Обратите внимание, что код отображения просто получает все поля и выводит их. В реальном приложении вы (надеюсь) будете знать, что содержат ваши данные, и работать с конкретными полями.


Так вот, как вы получите один кусок данных. Как насчет большого количества данных? IndexedDB поддерживает так называемый курсор. Курсор позволяет перебирать данные. Вы можете создавать курсоры с дополнительным диапазоном (базовый фильтр) и направлением.

Например, следующий блок кода открывает курсор для извлечения всех данных из хранилища объектов. Как и все остальное, что мы сделали с данными, это асинхронно и в транзакции.

01
02
03
04
05
06
07
08
09
10
11
12
13
var transaction = db.transaction([«test»], «readonly»);
var objectStore = transaction.objectStore(«test»);
 
var cursor = objectStore.openCursor();
 
cursor.onsuccess = function(e) {
    var res = e.target.result;
    if(res) {
        console.log(«Key», res.key);
        console.dir(«Data», res.value);
        res.continue();
    }
}

Обработчику успеха передается объект результата (переменная res выше). Он содержит ключ, объект для данных (в ключе значения выше) и метод continue, который используется для перехода к следующему фрагменту данных.

В следующей функции мы использовали курсор для перебора всех данных хранилища объектов. Так как мы работаем с данными о человеке, мы назвали это getPeople:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
function getPeople(e) {
 
    var s = «»;
 
    db.transaction([«people»], «readonly»).objectStore(«people»).openCursor().onsuccess = function(e) {
        var cursor = e.target.result;
        if(cursor) {
            s += «&lt;h2>Key «+cursor.key+»&lt;/h2>&lt;p>»;
            for(var field in cursor.value) {
                s+= field+»=»+cursor.value[field]+»&lt;br/>»;
            }
            s+=»&lt;/p>»;
            cursor.continue();
        }
        document.querySelector(«#status2»).innerHTML = s;
    }
}

Вы можете увидеть полную демонстрацию этого в вашей загрузке в виде файла test9.html . Он имеет логику Add Person, как в предыдущих примерах, поэтому просто создайте несколько человек, а затем нажмите кнопку, чтобы отобразить все данные.

Список данных

Итак, теперь вы знаете, как получить один фрагмент данных, а также как получить все данные. Давайте теперь перейдем к нашей последней теме — работе с индексами.


Мы говорили о IndexedDB для всей статьи, но на самом деле еще не сделали никаких — хорошо — индексов. Индексы являются важной частью хранилищ объектов IndexedDB. Они предоставляют способ извлечения данных на основе их значения, а также указание, должно ли значение быть уникальным в магазине. Позже мы продемонстрируем, как использовать индексы для получения диапазона данных.

Первый — как вы создаете индекс? Как и все остальное структурное, они должны быть выполнены в событии обновления, в основном, когда вы создаете хранилище объектов. Вот пример:

1
2
3
4
5
var objectStore = thisDb.createObjectStore(«people»,
                { autoIncrement:true });
//first arg is name of index, second is the path (col);
objectStore.createIndex(«name»,»name», {unique:false});
objectStore.createIndex(«email»,»email», {unique:true});

В первой строке мы создаем магазин. Мы берем этот результат (объект objectStore) и запускаем метод createIndex . Первый аргумент — это имя индекса, а второй — свойство, которое будет проиндексировано. В большинстве случаев я думаю, что вы будете использовать одно и то же имя для обоих. Последний аргумент — это набор опций. На данный момент мы используем только один, уникальный. Первый индекс для имени не является уникальным. Второй по электронной почте. Когда мы храним данные, IndexedDB проверит эти индексы и убедится, что свойство электронной почты уникально. Он также выполнит некоторую обработку данных на сервере, чтобы гарантировать, что мы можем получать данные по этим индексам.

Как это работает? Получив хранилище объектов посредством транзакции, вы можете запросить индекс из этого хранилища. Используя приведенный выше код, вот пример этого:

1
2
3
4
5
6
var transaction = db.transaction([«people»],»readonly»);
var store = transaction.objectStore(«people»);
var index = store.index(«name»);
 
//name is some value
var request = index.get(name);

Сначала мы получаем транзакцию, затем магазин, а затем индекс. Как мы уже говорили ранее, вы можете объединить эти первые три строки, чтобы сделать их немного компактнее, если хотите.

Когда у вас есть индекс, вы можете выполнить его, чтобы получить данные по имени. Мы могли бы сделать что-то подобное для электронной почты. Результатом этого вызова является еще один асинхронный объект, с которым вы можете связать обработчик onsuccess. Вот пример этого обработчика, найденный в файле test10.html :

01
02
03
04
05
06
07
08
09
10
11
12
13
request.onsuccess = function(e) {
 
    var result = e.target.result;
    if(result) {
        var s = «&lt;h2>Name «+name+»&lt;/h2>&lt;p>»;
        for(var field in result) {
            s+= field+»=»+result[field]+»&lt;br/>»;
        }
        document.querySelector(«#status»).innerHTML = s;
    } else {
        document.querySelector(«#status»).innerHTML = «&lt;h2>No match&lt;/h2>»;
    }
}

Обратите внимание, что индексный вызов get может возвращать несколько объектов. Поскольку наше имя не уникально, мы, вероятно, должны изменить код для его обработки, но это не обязательно.

Теперь давайте поднимемся на ступеньку выше. Вы видели использование API get для индекса, чтобы получить значение, основанное на этом свойстве. Что если вы хотите получить более широкий набор данных? Последний термин, который мы собираемся выучить сегодня — это диапазоны Диапазоны — это способ выбора подмножества индекса. Например, учитывая индекс свойства name, мы можем использовать диапазон для поиска имен, которые начинаются с A до имен, которые начинаются с C. Диапазоны бывают нескольких разных разновидностей. Они могут быть «все ниже некоторого маркера», «все выше некоторого маркера» и «что-то между более низким маркером и более высоким маркером». Наконец, просто чтобы сделать вещи интересными, диапазоны могут быть включающими или исключающими. В основном это означает, что для диапазона, идущего от AC, мы можем указать, хотим ли мы включить A и C в диапазон или только значения между ними. Наконец, вы также можете запросить как восходящий, так и нисходящий диапазоны.

Диапазоны создаются с использованием объекта верхнего уровня с именем IDBKeyRange. У него есть три метода, представляющих интерес: lowerBound , upperBound и bound . lowerBound используется для создания диапазона, который начинается с более низкого значения и возвращает все данные «над» ним. upperBound противоположна. И, наконец, bound используется для поддержки набора данных с нижней и верхней границами. Давайте посмотрим на некоторые примеры:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
//Values over 39
var oldRange = IDBKeyRange.lowerBound(39);
 
//Values 40a dn over
var oldRange2 = IDBKeyRange.lowerBound(40,true);
 
//39 and smaller…
var youngRange = IDBKeyRange.upperBound(40);
 
//39 and smaller…
var youngRange2 = IDBKeyRange.upperBound(39,true);
 
//not young or old… you can also specify inclusive/exclusive
var okRange = IDBKeyRange.bound(20,40)

Если у вас есть диапазон, вы можете передать его в метод индекса openCursor. Это дает вам итератор для циклического перебора значений, соответствующих этому диапазону. На практике это не поиск как таковой. Вы можете использовать это для поиска контента на основе начала строки, но не середины или конца. Давайте посмотрим на полный пример. Сначала мы создадим простую форму для поиска людей:

1
2
3
Starting with: <input type=»text» id=»nameSearch» placeholder=»Name»><br/>
Ending with: <input type=»text» id=»nameSearchEnd» placeholder=»Name»><br/>
<button id=»getButton»>Get By Name Range</button>

Мы собираемся разрешить поиск, который состоит из любого из трех типов диапазонов (опять же, значение и выше, наибольшее значение или значения в двух входах). Теперь давайте посмотрим на обработчик событий для этой формы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function getPeople(e) {
    var name = document.querySelector(«#nameSearch»).value;
 
    var endname = document.querySelector(«#nameSearchEnd»).value;
 
    if(name == «» &amp;&amp; endname == «») return;
 
    var transaction = db.transaction([«people»],»readonly»);
    var store = transaction.objectStore(«people»);
    var index = store.index(«name»);
 
    //Make the range depending on what type we are doing
    var range;
    if(name != «» &amp;&amp; endname != «») {
        range = IDBKeyRange.bound(name, endname);
    } else if(name == «») {
        range = IDBKeyRange.upperBound(endname);
    } else {
        range = IDBKeyRange.lowerBound(name);
    }
 
    var s = «»;
 
    index.openCursor(range).onsuccess = function(e) {
        var cursor = e.target.result;
        if(cursor) {
            s += «&lt;h2>Key «+cursor.key+»&lt;/h2>&lt;p>»;
            for(var field in cursor.value) {
                s+= field+»=»+cursor.value[field]+»&lt;br/>»;
            }
            s+=»&lt;/p>»;
            cursor.continue();
        }
        document.querySelector(«#status»).innerHTML = s;
    }
 
}

Сверху вниз — мы начинаем с захвата двух полей формы. Далее мы создаем транзакцию и из этого получаем магазин и индекс. Теперь о полусложной части. Поскольку у нас есть три различных типа диапазонов, которые мы должны поддерживать, нам нужно сделать немного условной логики, чтобы выяснить, какой нам нужен. Диапазон, который мы создаем, зависит от того, какие поля вы заполняете. Приятно, что, получив диапазон, мы просто передаем его в индекс и открываем курсор. Это оно! Вы можете найти этот полный пример в test11.html . Обязательно сначала введите некоторые значения, чтобы у вас были данные для поиска.


Хотите верьте, хотите нет — мы только начали наше обсуждение IndexedDB. В следующей статье мы рассмотрим дополнительные темы, включая обновления и удаления, значения на основе массива, а также некоторые общие советы по работе с IndexedDB.