IndexedDB的概念问题(关系等)

时间:2012-01-01 19:21:18

标签: javascript html5 local-storage web-sql indexeddb

我正在写一篇关于Web应用程序离线功能的论文。我的任务是通过Web应用程序显示离线存储的可能性,该应用程序具有服务器端关系数据库和客户端与服务器之间的Ajax / JSON流量。我的第一个实现使用了localStorage的方法,将每个Ajax响应保存为值,并将请求URL作为键。该应用程序工作正常。然而,在下一步中,我希望(即论文要求)使用客户端数据库实现更高级的版本。由于服务器维护关系数据库,因此Web SQL数据库将是直观的选择。但是,正如我们所知,该标准已被弃用,我不想使用未来不确定的技术。因此,我想使用IndexedDB来实现客户端数据库逻辑。不幸的是,在网上阅读了大量材料后,我们仍然不知道该如何处理。

我的任务似乎相当简单:使用IndexedDB在客户端上实现服务器端数据库,以复制曾经从服务器获取的所有数据。这些问题远远不那么简单:

  • 服务器端数据库是关系型的,IndexedDB是(或多或少)面向对象的
  • 没有直观的方法来同步客户端和服务器端数据库
  • 没有直观的方法来实现IndexedDB中使用服务器上的外键和JOIN实现的关系

现在,我有一个概念,我真的害怕开始实施。我考虑过为服务器数据库中的每个表创建一个对象存储,并手动编写不同对象存储中的关系对象。在我的应用程序中,简而言之,管理大学的课程,我有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 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事务的性能是什么样的)。

我怎样才能做得更好更简单?

我已经查看了代表类似问题的这些主题:link1link2。我没有看到任何更简单的解决方案。此外,由于几个原因,我宁愿避免使用IndexedDB包装器框架。

我还可以想象,对于我的问题,我对IndexedDB完全走错了路。

修改

我终于找到了将索引存储在IndexedDB中的对象本身的方法。在具有许多引用的大量数据的情况下,这可能导致一些性能问题。但是,如果巧妙地使用,在大多数情况下可以避免大量的迭代和数据库命中,并且不需要在内存或IndexedDB本身中存储复杂的数据库模式。

一般来说,我必须说,我得到的印象是,我在某种程度上将IndexedDB误解为动态和直接的想法作为无模式数据库。但无论如何,我用JavaScript实现了一切,它工作正常,没有任何不一致的可能性。

1 个答案:

答案 0 :(得分:21)

我自己是IndexedDB的新手,但我也一直在考虑如何将IndexedDB用于这样的目的。如果你还没有这样做,我建议的第一件事就是看看其他键值/文档数据库(CouchDB,MongoDB等)是如何工作的,因为它本质上是IndexedDB的数据库类型。 / p>

在文档数据库中处理关系有几种不同的方法...为了与关系服务器端数据库同步,您可能需要创建某种自定义映射,因为某些关系的方法会使IndexedDB的意义不会非常干净地映射到关系数据库。但是,我认为设置这样的映射肯定是可行的,更大的问题是如何处理IndexedDB中的关系,所以这就是我将关注的内容......

对于您提出的解决方案,我认为它实际上可以正常工作,您可以编写一个简单的查询库来帮助整合管道代码(更多内容见下文)。键值存储的构建非常有效地按键查找项目,因此对每个相关对象执行此操作可能不会像您想象的那样低效...但是,我提出了另一个更好地利用索引的想法。 ..

首先,对于我提出的解决方案,您需要将“objectstore”元数据存储在“reference”对象本身以外的某个位置......它根本不需要存储在IndexedDB中;你可以使用内存模式:

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

(顺便说一下,你的JSON示例有一个错误......你不能有多个名为“reference”的键 - 它需要是一个“引用”数组。)

这使您可以直接在关系字段中存储ID值,以便您可以在它们上创建索引(为了清晰起见,我使用了字母前缀,尽管实际上所有这些都可能具有1的ID,因为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 = …

请注意,在讲师示例(多对多关系)中,索引需要指定为“multientry”,这意味着如果您有一个值为数组的字段,则数组的每个元素都将被添加到索引中。 (参见https://developer.mozilla.org/en/IndexedDB/IDBObjectStore#createIndex ...我不确定浏览器支持是什么。)

而且我确信你也可以通过索引来做其他聪明的事情,使用游标和IDBKeyRange来帮助进行某种“连接”操作。有关想法,请查看此链接,该链接演示了在CouchDB中处理关系的方法:

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

该链接还提到使用嵌入式文档,这是您应该考虑的事项 - 并非所有对象都必须拥有自己的对象存储,尤其是“聚合”关系。

(顺便说一句,我不确定它对你有多大帮助,因为它没有提供很多查询方式,但有人实际上在IndexedDB上实现了类似CouchDB的数据库:{{ 3}})

除了索引之外,实现缓存机制也可能有很大帮助。

现在,为了简化查询过程,我知道你提到不想使用包装器库...但我对可以创建的方便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之上实现图形数据库,我还不确定它有多实用。

祝你好运!