IndexedDB 的概念问题(关系等)
Posted
技术标签:
【中文标题】IndexedDB 的概念问题(关系等)【英文标题】:Conceptual problems with IndexedDB (relationships etc.) 【发布时间】:2012-01-31 11:16:47 【问题描述】:我正在写一篇关于 Web 应用程序的离线能力的论文。我的任务是通过具有服务器端关系数据库和客户端和服务器之间的 Ajax/JSON 流量的 Web 应用程序来展示离线存储的可能性。我的第一个实现使用了 localStorage 的方法,将每个 Ajax 响应保存为值,并将请求 URL 作为键。该应用程序运行良好。然而,在下一步中,我想(即论文要求)使用客户端数据库实现更高级的版本。由于服务器维护一个关系数据库,Web SQL 数据库将是直观的选择。但是,正如我们所知,该标准已被弃用,我不想使用未来不确定的技术。因此,我想使用 IndexedDB 来实现客户端数据库逻辑。不幸的是,在阅读了网络上的大量资料后,这些资料大多只停留在表面(todo-notes 应用程序等),我仍然不知道如何继续。
我的任务似乎相当简单:使用 IndexedDB 在客户端上实现服务器端数据库,以复制曾经从服务器获取的所有数据。使这变得不那么简单的问题是:
服务器端数据库是关系型的,IndexedDB (或多或少)是面向对象的 没有直观的方法来同步客户端和服务器端数据库 没有直观的方法来实现 IndexedDB 中通过服务器上的外键和 JOIN 实现的关系现在,我有一个概念,我真的很害怕开始实施。我考虑为服务器数据库中的每个表创建一个对象存储,并手动对不同对象存储中的关系对象进行编程。在我的应用程序中,简而言之,它管理一所大学的课程,我有 7 个对象存储。
我想通过一个来自服务器的 JSON 响应示例来展示我的想法(/* 这些是 cmets */):
"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 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 的轨道。
编辑:
我最终采用了将引用存储在 IndexedDB 中的对象本身的方法。在具有大量引用的大量数据的情况下,这可能会导致一些性能问题。但是,如果巧妙地使用,在大多数情况下可以避免大量的迭代和数据库命中,并且无需将复杂的数据库模式存储在内存或 IndexedDB 本身中。
通常我必须说,我的印象是我在某种程度上误解了 IndexedDB 作为无模式数据库的动态和直接的想法。但无论如何,我用 javascript 实现了一切,它运行良好,没有任何不一致的机会。
【问题讨论】:
所以你基本上完全放弃了使用 IndexedDB 的关系概念? 【参考方案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”的键 - 它需要是一个“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())发生的在两端正确...实际上您可能不需要它,具体取决于您计划如何查询数据,但这似乎是一个好主意,因为您有时可能只想获取相关对象的 ID(稍后查找, 或不) 而不是实际检索它们。
假设您在讲师商店的“课程”字段中有一个索引。使用索引,您可以一举查找与特定课程 ID 关联的所有讲师:
lecturerStore.index("courses").get("C1").onsuccess = …
对于这个例子来说,这并不重要,因为课程通常只有 1-2 位讲师,但请考虑如何使用索引来有效地查找特定学期的所有课程:
coursesStore.index("semester").get("S1").onsuccess = …
请注意,在讲师示例中(多对多关系),索引需要指定为“多条目”,这意味着如果您有一个值为数组的字段,则该数组的每个元素将被添加到索引中。 (参见https://developer.mozilla.org/en/IndexedDB/IDBObjectStore#createIndex ...我不确定浏览器对此的支持是什么。)
而且我相信您也可以通过索引来做其他聪明的事情,使用游标和 IDBKeyRange 来帮助执行某种“加入”操作。有关想法,请查看此链接,该链接演示了在 CouchDB 中处理关系的方法:
http://wiki.apache.org/couchdb/EntityRelationship
该链接还提到使用嵌入式文档,这是您绝对应该考虑的事情 - 并非所有对象都必须拥有自己的对象存储,尤其是对于“聚合”关系。
(顺便说一句,我不确定它对您有多大帮助,因为它没有提供太多查询方式,但实际上有人在 IndexedDB 之上实现了一个类似 CouchDB 的数据库:@987654323 @)
除了索引之外,实现缓存机制可能也会有很大帮助。
现在,关于简化查询过程,我知道您提到不想使用包装库……但我有一个想法,可以创建一个方便的 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 之上实现图形数据库,但我还不确定它的实用性。
祝你好运!
【讨论】:
我知道这已经晚了,但是感谢您的详细回答,这真的很有价值。我在下面发布了一个答案,最初还包括一些免费的东西,但不幸的是这些部分被删除了。 很高兴您发现它有帮助!感谢您发布您的论文……我还没有机会看它,但我一定会的。 我刚刚发现了这个库,它似乎是一种很有前途的方法,可以使用相同的 API 从 IndexedDB 以及许多其他数据库中查询数据:jaydata.org以上是关于IndexedDB 的概念问题(关系等)的主要内容,如果未能解决你的问题,请参考以下文章
indexedDB 在概念上与 HTML5 本地存储有何不同?