Концептуальные проблемы с IndexedDB (отношения и т. д.)

Я пишу диссертацию Об автономных возможностях веб-приложений. Моя задача-показать возможности автономного хранения через веб-приложение с реляционной базой данных на стороне сервера и трафиком Ajax/JSON между клиентом и сервером. Моя первая реализация использовала подход с localStorage, сохраняя каждый ответ Ajax как значение с URL-адресом запроса как ключ. Приложение работает просто отлично. Однако на следующем шаге я хочу (т. е. тезис требует) реализовать более продвинутую версию с клиентской базой данных. Поскольку сервер поддерживает реляционную базу данных, база данных Web SQL была бы интуитивным выбором. Но, как мы знаем, стандарт устарел, и я не хочу использовать технологию, будущее которой неопределенно. Таким образом, я хочу использовать IndexedDB для реализации логики базы данных на стороне клиента. К сожалению, после прочтения большого количества материала в интернете, который в основном очень сильно царапает поверхность (приложения todo-notes и т. д.), Я все еще не знаю, как продолжить.

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

  • серверная база данных является реляционной, IndexedDB (более или менее) объектно-ориентированной
  • нет интуитивного способа синхронизации клиентских и серверных баз данных
  • там нет интуитивного способа реализовать отношения в IndexedDB, которые реализованы с помощью внешних ключей и присоединений на сервере

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

Я хочу продемонстрировать свою мысль на примере ответа JSON с сервера (/* комментарий */):

{ "course": { /* course object */
    "id":1, 
    "lecturer": { "id":"1", /* lecturer object with many attributes */ },
    "semester": { "id":"1", /* semester object with many attributes */ }, 
    /* more references and attributes */
}}

алгоритм хранить данные с IndexedDB будет хранить каждый объект, который применяется к хранилищу объектов в соответствующем хранилище объектов и заменять объекты ссылками на эти объекты. Например, вышеуказанный объект course будет выглядеть следующим образом в хранилище объектов "course":

{ "course": { /* course object */
    "id":1, 
    "lecturer": 
    { "reference": { /* reference to the lecturer in the object store 'lecturer' */
        "objectstore":"lecturer",
        "id":"1" }
    },
    "semester":
    { "reference": { /* reference to the semester in the object store 'semester' */
        "objectstore":"semester",
        "id":"1" }
    }
    /* more references and attributes */
}}

В алгоритм для извлечение данных с помощью IndexedDB затем сделал бы следующее (У меня есть рекурсивный шаблон смутно в виду):

Retrieve the course object with id=1 from the object store 'course'
For each reference object in the retrieved course object, do
   Retrieve the object with id=reference.id from the object store reference.objectstore
   Replace the reference object with the retrieved object

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

как я могу сделать это лучше и проще?

Я уже смотрел на эти темы, которые представляют аналогичные проблемы:link1 и, link2. Я не вижу в них более простых решений. Более того, я бы предпочел избежать использования фреймворка оболочки IndexedDB по нескольким причинам.

Я также мог представить, что я полностью на неправильном пути с IndexedDB для моего проблема.

Edit:

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

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

1 ответов


Я сам новичок в IndexedDB, но я тоже много думал о том, как я буду использовать IndexedDB для таких целей. Первое, что я бы предложил, если вы еще этого не сделали,-это посмотреть, как другие базы данных ключей / документов (CouchDB, MongoDB и т. д.) работа, так как это по существу тип базы данных, которая является IndexedDB.

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

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

во-первых, для моего предлагаемого решения вам нужно будет хранить метаданные "objectstore "где-то, кроме самого объекта" reference " ...это не обязательно даже нужно хранить в IndexedDB вообще; вы можете просто использовать схему в памяти для этого:

var schema = {
    Course: {
        fields: [id, title],
        relationships: {
            lecturers: {objectstore: 'lecturer'},
            semester: {objectstore: 'semester'},
        }
    },
    Lecturer: { ... }
    ...
};

(кстати, в вашем примере JSON есть ошибка...у вас не может быть более одного ключа с именем "reference" - это должен быть массив "references".)

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

var course1 = {
    id:'C1',
    lecturers:['L1'],
    semester:1
};

var lecturer1 = {
    id:'L1',
    courses:['C1']
}

var semester1 = {
    id:'S1',
    courses:['C1']
}

вы, конечно, должны быть осторожны, чтобы все операции хранения/извлечения происходили через функции доступа к данным (например, insert (), update (), delete ()), которые были достаточно умными, чтобы гарантировать, что отношения всегда обновлялись правильно с обеих сторон...на самом деле вам может не понадобиться это в зависимости от того, как вы планируете запрашивать данные, но это кажется хорошей идеей, так как иногда вы можете просто захотеть получить Идентификаторы связанных объектов (для поиска позже или нет), а не фактическое их извлечение.

предположим, у вас есть индекс в поле "курсы" в магазине лекторов. Используя индекс, вы можете одним махом найти всех лекторов, связанных с определенным идентификатором курса:

lecturerStore.index("courses").get("C1").onsuccess = …

для этого примера это не имеет большого значения, потому что курсы, как правило, имеют только 1-2 лекторов, но рассмотрим, как индекс может быть использован для эффективного поиска всех курсы в конкретном семестре:

coursesStore.index("semester").get("S1").onsuccess = …

обратите внимание, что в Примере лектора (отношение "многие ко многим") индекс должен быть указан как "multientry", что означает, что если у вас есть поле, значение которого является массивом, каждый элемент массива будет добавлен в индекс. (См.https://developer.mozilla.org/en/IndexedDB/IDBObjectStore#createIndex ...Я не уверен, что поддержка браузера на этом.)

и я уверен, что вы могли бы делать другие умные вещи с индексированием тоже, используя курсоры и IDBKeyRange, чтобы помочь сделать какую-то операцию" соединения". Для идей проверьте эту ссылку, которая демонстрирует способы обработки отношений в CouchDB:

http://wiki.apache.org/couchdb/EntityRelationship

эта ссылка также упоминает использование встроенных документов, что вы определенно должны учитывать-не все объекты обязательно должны иметь свое собственное хранилище объектов, особенно для " агрегации" отношения.

(кстати, я не уверен, насколько это было бы полезно для вас, так как он не предоставляет много возможностей для запросов, но кто-то фактически реализовал CouchDB-подобную базу данных поверх IndexedDB:https://github.com/mikeal/pouchdb)

в дополнение к индексам, реализация механизма кэширования, вероятно, тоже поможет.

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

//select all courses taught by 'Professor Wilkins'
{
from: 'lecturer',  //open cursor on lecturer store 
where: function(lecturer) { return lecturer.name=='Professor Wilkins' }, //evaluate for each item found
select: function(lecturer) { return lecturer.courses }, //what to return from previous step
//this should be inferred in this case, but just to make it clear...
eagerFetch: function(lecturer) { return lecturer.courses }
}

Я не уверен, насколько сложно было бы реализовать, но определенно кажется, что это облегчит жизнь.

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

удачи!