35

私はWebアプリケーションのオフライン機能についての論文を書いています。私の仕事は、サーバー側のリレーショナルデータベースとクライアントとサーバー間のAjax/JSONトラフィックを備えたWebアプリケーションを介したオフラインストレージの可能性を示すことです。私の最初の実装では、localStorageを使用したアプローチを使用し、リクエストURLをキーとして各Ajax応答を値として保存しました。アプリは問題なく動作します。ただし、次のステップでは、クライアント側のデータベースを使用して、より高度なバージョンを実装したいと思います(つまり、論文で必要です)。サーバーはリレーショナルデータベースを維持しているため、WebSQLデータベースが直感的な選択でした。しかし、ご存知のように、この標準は廃止されており、将来が不透明なテクノロジーは使用したくありません。したがって、IndexedDBを使用してクライアント側のデータベースロジックを実装したいと思います。不運にも、

私のタスクはかなり単純なようです 。IndexedDBを使用してクライアントにサーバー側データベースを実装し、サーバーから一度フェッチされたすべてのデータを複製します。これをはるかに簡単にしない問題は次のとおりです。

  • サーバー側のデータベースはリレーショナルであり、IndexedDBは(多かれ少なかれ)オブジェクト指向です
  • クライアント側とサーバー側のデータベースを同期する直感的な方法はありません
  • サーバー上で外部キーとJOINを使用して実装されるIndexedDBの関係を直感的に実装する方法はありません。

今、私は実装を開始することを本当に恐れている概念を念頭に置いています。サーバーデータベース内のすべてのテーブルにオブジェクトストアを作成し、さまざまなオブジェクトストア内のリレーションオブジェクトを手動でプログラムすることを考えました。要するに、大学のコースを管理する私のアプリケーションでは、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トランザクションのパフォーマンスがどのようになるかはわかりません)。

どうすればこれをより良く、より簡単に行うことができますか?

私はすでに同様の問題を表すこれらのスレッドを見ました:link1link2。これらには、これ以上簡単な解決策はありません。さらに、いくつかの理由から、IndexedDBラッパーフレームワークの使用は避けたいと思います。

また、自分の問題についてIndexedDBで完全に間違った方向に進んでいることも想像できます。

編集:

最終的に、IndexedDBのオブジェクト自体に参照を格納するというアプローチを追求することになりました。これにより、参照が多い大量のデータの場合に、パフォーマンスの問題が発生する可能性があります。ただし、賢く使用すれば、ほとんどの場合、膨大な量の反復とデータベースヒットを回避でき、複雑なデータベーススキーマをメモリまたはIndexedDB自体に格納する必要はありません。

一般的に言って、私はIndexedDBをスキーマレスデータベースとして動的でまっすぐなアイデアを何らかの方法で誤解しているという印象を受けます。しかし、いずれにせよ、私はすべてをJavaScriptで実装しました。それは正常に機能し、矛盾が発生する可能性はありません。

4

1 に答える 1

25

私自身、IndexedDB は初めてですが、このような目的で IndexedDB をどのように使用するかについて、私もよく考えてきました。まだ行っていない場合は、最初に、他のキー値/ドキュメント データベース (CouchDB、MongoDB など) がどのように機能するかを確認することをお勧めします。

ドキュメント データベースでリレーションシップを処理するには、いくつかの異なるアプローチがあります...リレーショナル サーバー側データベースとの同期に関しては、何らかのカスタム マッピングを作成する必要があるでしょう。リレーショナル データベースにきれいにマップされません。ただし、そのようなマッピングを設定することは間違いなく実行可能だと思います。より大きな問題は、IndexedDB で関係を処理する方法であるため、ここで焦点を当てます...

提案されたソリューションに関しては、実際にはうまく機能すると思います。また、配管コードを統合するのに役立つ単純なクエリ ライブラリを作成することもできます (詳細は以下を参照)。キーと値のストアは、キーでアイテムを検索する際に非常に効率的に構築されているため、関連するオブジェクトごとにこれを行うことは、思ったほど非効率的ではないかもしれません...しかし、インデックスをより有効に活用する別のアイデアを思いつきました。 ..

まず、私が提案するソリューションでは、「objectstore」メタデータを「参照」オブジェクト自体以外の場所に保存する必要があります...必ずしも IndexedDB に保存する必要さえありません。そのためにインメモリスキーマを使用できます。

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

(ちなみに、あなたの JSON の例にはエラーがあります...「参照」と呼ばれるキーを複数持つことはできません。「参照」配列である必要があります。)

これにより、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 のようなデータベースを実装した人がいます: 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 }
}

実装がどれほど難しいかはわかりませんが、生活が楽になることは間違いありません。

随分長い間とりとめなく言いましたが、最後にもう 1 つ言いたいことがあります。それは、グラフ データベースからいくつかのアイデアを借りることも考えているということです。グラフ データベースは、ドキュメント データベースよりもリレーションシップの処理に優れているからです。 IndexedDB の上にグラフ データベースを実装することは可能ですが、それがどれほど実用的かはまだわかりません。

幸運を!

于 2012-01-15T00:10:22.863 に答える