Ответ 1
Я новичок в IndexedDB, но я тоже много думал о том, как использовать IndexedDB для таких целей. Первое, что я хотел бы предложить, если вы еще этого не сделали, - это посмотреть, как работают другие базы данных ключа/документа (CouchDB, MongoDB и т.д.), Поскольку это, по сути, тип базы данных, в которой указан IndexedDB.
Существует несколько различных подходов к обработке отношений в базе данных документов... что касается синхронизации с вашей реляционной серверной базой данных, вам, вероятно, потребуется создать какое-то настраиваемое сопоставление, поскольку некоторые из подходов к отношениям, которые смысл для IndexedDB не будет очень хорошо отображаться в реляционной базе данных. Тем не менее, я думаю, что создание такого сопоставления, безусловно, выполнимо, и большая проблема заключается в том, как обрабатывать отношения в IndexedDB, так что я сосредоточусь здесь...
Что касается вашего предлагаемого решения, я думаю, что он действительно может работать хорошо, и вы могли бы написать простую библиотеку запросов, которая помогла бы консолидировать сантехнический код (подробнее об этом ниже). Хранилища с ключевыми значениями построены так, чтобы быть очень эффективными при поиске элементов по ключевым словам, поэтому для каждого связанного объекта может быть не так малоэффективно, как вы думаете... однако, я придумал еще одну идею, которая лучше использует индексы...
Во-первых, для моего предлагаемого решения вам нужно будет хранить метаданные "objectstore" где-то иначе, чем внутри самого "ссылочного" объекта... его вообще не обязательно нужно хранить в IndexedDB; вы можете просто использовать для этого схему памяти:
var schema = {
Course: {
fields: [id, title],
relationships: {
lecturers: {objectstore: 'lecturer'},
semester: {objectstore: 'semester'},
}
},
Lecturer: { ... }
...
};
(Кстати, ваш пример JSON имеет ошибку... у вас не может быть более одного ключа с именем "reference" - он должен быть массивом "ссылок".)
Это освобождает вас, чтобы хранить значения идентификатора непосредственно в полях отношений, чтобы вы могли создавать индексы на них (я использовал префиксы писем для ясности, хотя на самом деле все они, вероятно, имели бы идентификатор 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 }
}
Я не уверен, насколько сложно было бы реализовать, но, похоже, это облегчит жизнь.
Я слишком долго болтал, но я хотел упомянуть последнее, что я также думал о заимствовании некоторых идей из графических баз данных, поскольку они намного лучше справляются с отношениями, чем с базами данных документов, и Я действительно думаю, что можно было бы реализовать базу данных графа поверх IndexedDB, я просто не уверен, насколько это практично.
Удачи!