草庐IT

javascript - IndexedDB的概念性问题(关系等)

coder 2023-07-31 原文

我正在写一篇有关Web应用程序离线功能的论文。我的任务是展示通过具有服务器端关系数据库的Web应用程序以及客户端与服务器之间的Ajax/JSON流量进行脱机存储的可能性。我的第一个实现使用了localStorage方法,将每个Ajax响应保存为值,并以请求URL作为键。该应用程序运行正常。但是,在下一步中,我想(即论文要求)使用客户端数据库实现更高级的版本。由于服务器维护关系数据库,因此Web SQL数据库将是直观的选择。但是,正如我们所知,该标准已过时,我不想使用 future 不确定的技术。因此,我想使用IndexedDB来实现客户端数据库逻辑。不幸的是,在阅读了很多网上的 Material 后,这些 Material 几乎总是爬取表面(待办事项笔记应用程序等),我仍然不知道如何进行。

我的任务似乎很简单:使用IndexedDB在客户端上实现服务器端数据库,以复制曾经从服务器获取的所有数据。这些使事情变得不那么直接的问题是:

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

  • 现在,我心里有一个我真的很害怕开始实现的概念。我考虑过为服务器数据库中的每个表创建一个对象存储,并手动对不同对象存储中的关系对象进行编程。简而言之,在我管理大学类(class)的应用程序中,我有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 存储数据的算法将把适用于对象存储的每个对象存储在适当的对象存储中,并用对这些对象的引用替换这些对象。例如,以上类(class)对象在对象存储库“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的异步特性。仅仅为了检索类(class)对象,这还会导致数据库进行许多不同的事务,并且性能会受到很大的影响(我真的不知道IndexedDB事务的性能看起来如何)。

    我该如何做得更好,更简单?

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

    我也可以想象,对于我的问题,我在IndexedDB方面完全走错了路。

    编辑:

    最后,我最终采用了将引用存储在IndexedDB中的对象本身中的方法。在具有大量引用的大量数据的情况下,这可能会导致一些性能问题。但是,如果巧妙地使用它,则在大多数情况下都可以避免大量的迭代和数据库命中,并且无需在内存或IndexedDB本身中存储复杂的数据库架构。

    通常,我必须说,给人的印象是我以某种方式误解了IndexedDB作为无模式数据库的动态而直截了当的想法。但是无论如何,我都使用JavaScript实现了所有内容,并且一切正常,没有任何不一致的机会。

    最佳答案

    我本人对IndexedDB还是陌生的,但是我也一直在思考如何将IndexedDB用于这样的目的。我建议的第一件事是,如果您还没有做的话,那就看看其他键值/文档数据库(CouchDB,MongoDB等)如何工作,因为这实际上就是IndexedDB的数据库类型。

    处理文档数据库中的关系有几种不同的方法...要与关系型服务器端数据库进行同步,您可能需要创建某种自定义映射,因为某些关系方法对IndexedDB有意义不会非常清晰地映射到关系数据库。但是,我认为设置这样的映射肯定是可行的,而更大的问题是如何处理IndexedDB中的关系,因此我将在这里重点介绍...

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

    首先,对于我提出的解决方案,您需要将“对象存储”元数据存储在“引用”对象本身之外的其他位置……根本不需要甚至将其存储在IndexedDB中。您可以为此使用内存模式:

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

    (顺便说一句,您的JSON示例有一个错误...您不能有多个键称为“引用”-它必须是一个“引用”数组。)

    这使您有空将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(以后再查询或不查看) ),而不是实际检索它们。

    假设您在讲师商店的“类(class)”字段中有一个索引。使用索引,您可以一口气查找与特定类(class)ID相关的所有讲师:
    lecturerStore.index("courses").get("C1").onsuccess = …
    

    对于该示例,这并不重要,因为类(class)通常只有1-2位讲师,但是请考虑如何使用索引来有效地查找特定学期的所有类(class):
    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的数据库:https://github.com/mikeal/pouchdb)

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

    现在,关于简化查询过程,我知道您提到不想使用包装器库...但是我对可以创建一个方便的API的想法有所了解,该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之上实现图形数据库,但我还不确定它的实用性。

    祝你好运!

    关于javascript - IndexedDB的概念性问题(关系等),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8694941/

    有关javascript - IndexedDB的概念性问题(关系等)的更多相关文章

    1. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

      我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

    2. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

      尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

    3. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

      我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

    4. ruby - Fast-stemmer 安装问题 - 2

      由于fast-stemmer的问题,我很难安装我想要的任何ruby​​gem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=

    5. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

      当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

    6. java - 从 JRuby 调用 Java 类的问题 - 2

      我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

    7. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

      我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

    8. ruby - Rails 关联 - 同一个类的多个 has_one 关系 - 2

      我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下

    9. 【高数】用拉格朗日中值定理解决极限问题 - 2

      首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

    10. SPI接收数据异常问题总结 - 2

      SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

    随机推荐