Статьи

Добавление поддержки «Фильтровать по мере ввода» в IndexedDB

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

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

Диапазоны могут идти в любом направлении и могут быть включающими или исключающими. Под этим я подразумеваю, что вы можете сказать «Любой объект с именем« выше », включая Барри» или «Любой объект с именем« ниже », Зельда, но без этого имени». Вы также можете объединить оба и получить один объект тоже.

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

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

 function displayNotes() {
        var transaction = db.transaction(["note"], IDBTransaction.READ);
        var content="<ul>";
        transaction.oncomplete = function(event) {
            console.log("All done!");
            $("#noteList").html(content);
        };
        transaction.onerror = function(event) {
          // Don't forget to handle errors!
          console.dir(event);
        };
        var objectStore = transaction.objectStore("note");
        objectStore.openCursor().onsuccess = function(event) {
          var cursor = event.target.result;
          if (cursor) {
            content += "<li data-key=\""+cursor.key+"\"><span class=\"label\">"+cursor.value.title+"</span>";
            content += " <a class=\"delete\">[Delete]</a>";
            content +="</li>";
            cursor.continue();
          }
          else {
            content += "</ul>";
            console.log("Done with cursor");
          }
        };
    }
view raw
gistfile1.js
Этот Gist доставлен вам
GitHub .

Чтобы поддерживать связанный диапазон, вы должны изменить способ открытия хранилища объектов (помните, думайте об этом как о таблице базы данных). Когда мы просто получаем все, мы запускаем openCursor в объектном магазине (строка 18 выше). Когда нам нужен связанный список данных, мы сначала получаем индекс (это свойство, которое, как мы сказали, мы хотим иметь возможность фильтрации), создаем диапазон и затем открываем на нем курсор. Поэтому с небольшим объемом работы мы можем обновить нашу функцию displayNotes, чтобы использовать дополнительный фильтр. (Обратите внимание, что я также переключился на отображение таблицы. Здесь изменение HTML не очень важно, поэтому я не буду его освещать.)

function displayNotes(filter) {

        var transaction = db.transaction(["note"], IDBTransaction.READ);
        var content="<table class='table table-bordered'><thead><tr><th>Title</th><th>Created</th><th> </td></thead><tbody>";

        transaction.oncomplete = function(event) {
            $("#noteList").html(content);
        };

        transaction.onerror = function(event) {
          // Don't forget to handle errors!
          console.dir(event);
        };

        var handleResult = function(event) {
          var cursor = event.target.result;
          if (cursor) {
            content += "<tr data-key=\""+cursor.key+"\"><td class=\"notetitle\">"+cursor.value.title+"</td>";
            content += "<td>"+dtFormat(cursor.value.created)+"</td><td><a class=\"btn btn-danger delete\">Delete</a></td>";
            content +="</tr>";
            cursor.continue();
          }
          else {
            content += "</tbody></table>";
            console.log("Done with cursor");
          }
        };

        var objectStore = transaction.objectStore("note");

        if(filter) {
            console.log("need to make a range on "+filter);
            //Credit: http://stackoverflow.com/a/8961462/52160
            var range = IDBKeyRange.bound(filter, filter + "z");
            var index = objectStore.index("title");
            index.openCursor(range).onsuccess = handleResult;
        } else {
            console.log("Not making a range");
            objectStore.openCursor().onsuccess = handleResult;
        }
        
            
    }

Сосредоточьтесь на строках с 31 по 40. Вы можете увидеть различные способы открыть курсор. Обратите внимание, что мы делаем нашу привязку на основе входной строки, например, «ra» и добавляем «z», чтобы закончить диапазон. Таким образом, введя «ra», мы хотим, чтобы все заметки с заголовком от «raa» до «raz»

Помимо этого — код идентичен. Я переместил часть успеха в новую внутреннюю функцию (handleResult), но она работает одинаково, независимо от того, как я получаю курсор.

Вы можете продемонстрировать это самостоятельно, нажав на кнопку демо ниже, но, как и раньше, это Firefox только из-за — как я считаю — плохой реализации в Chrome. (Я думаю, что это может быть сделано для работы в Chrome, но, поскольку я создаю эти примеры, чтобы помочь мне узнать больше о IndexedDB, я прекрасно поддерживаю наиболее совместимый браузер.)

Вы можете найти полный источник, просмотрев …. источник. ?