IndexedDB的概念问题(关系等)

Conceptual problems with IndexedDB (relationships etc.)

本文关键字:关系 问题 IndexedDB      更新时间:2023-09-26

我正在写一篇关于web应用程序离线能力的论文。我的任务是通过带有服务器端关系数据库和客户端与服务器之间的Ajax/JSON流量的web应用程序展示离线存储的可能性。我的第一个实现使用了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": { /* 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的新手,但我也一直在思考如何将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(稍后查找或不查找),而不是实际检索它们。

假设您在讲师存储中的"courses"字段上有一个索引。使用索引,您可以一次性查找与特定课程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的数据库: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之上实现图形数据库是可能的,我只是还不确定它有多实用。

祝你好运!