
この記事では、普遍的な解決策を見つけようとする私の試みについて述べています。
たとえば、2つの関連テーブルのデータを表示する必要があるとします。

通常、標準的なアプローチは次の手順で構成されます。
- サーバーで、JOINを使用してSQLクエリを記述します
- サーバー上で、オブジェクトの配列を返す関数を追加し、ルートを介してアクセスできるようにします
- クライアントで、AJAX呼び出しをサーバーに追加し、結果をテーブルに描画します
標準アプローチの欠点は次のとおりです。
- SQLクエリとラッパー関数は、列名の衝突の可能性を考慮する必要があります。つまり、「 SELECT * 」を実行することはできません。
- サーバーの応答には、関連するテーブルからの多数の重複エントリが含まれます。 この例では、departmentsテーブルのキー「sales」を持つレコードが2回転送されます。
- 多数のテーブルを接続する場合、長いキーを受け取るため、これらのキーを転送するための不必要なメモリとトラフィックの消費が増加するか、SQLクエリの列名を手動でリストする必要があり、データベース構造を変更するときに追加コストが発生します。
- 関連するテーブルからデータを取得するためのAPI関数の数は、テーブルの数を大幅に超える可能性があり、これによりコードが肥大化し、結果としてコストがかかります。
非標準のアプローチは、テーブルを個別に取得し、クライアントでリンクすることです。 これは時々簡単に行えます。 たとえば、上記の構造では、「departments」テーブルをハッシュにロードし、「id」でアクセスできます。 しかし、多くの場合、これは実行できず、Array.findやArray.indexOfなどのさまざまな検索機能を使用する必要があります。
次のプロジェクトに取り組み、両方のアプローチの欠点を回避するためにデータをより良く整理する方法についてもう一度頭を悩ませ、このノードを完全にカットする時がきたと判断しました。
サーバーが正規化されたテーブルを提供し、それらをJavaScriptコードでリンクするアプローチは、私にとってより魅力的でした。 必要なのは、それらを簡単にバインドするツールだけでした。 私はすべての出来事を先送りにし、彼を書くために座った。
要件は次のとおりです。
- このツールにより、配列に任意のインデックスを作成し、それらを最新の状態に保つことができます。
- このツールは、可能であれば、低速の要素検索操作なしで、インデックスを使用して配列を検索できる必要があります。
- インストゥルメントは、特定の宣言オブジェクト(SQLのJOIN演算子など)に従ってインデックス付きフィールドで配列を接続できる必要がありますが、クエリとSQLが提供するすべての機能を解析する必要はありません。
そのため、Strelki.jsが登場し、これまでのところ、唯一のクラスはIndexedArrayです。
したがって、新しいIndexedArrayを作成します。
var emp = new StrelkiJS.IndexedArray();
データを追加します。
emp.put({ id: "001", first_name: "John", last_name: "Smith", dep_id: "sales", address_id: "200" }); emp.put({ id: "002", first_name: "Ivan", last_name: "Krasonov", dep_id: "sales", address_id: "300" });
中身を見てみましょう:

内部では、IndexedArrayはオブジェクトへの参照が保存されるハッシュ(this.data)です。 ハッシュキーは、保存するアイテムのidフィールドであり、一意である必要があります。 多くの最新のサーバーフレームワークもこの方法でidフィールドを使用するため、この制限は問題になりません。
さらに、IndexedArrayにはthis.indexDataのハッシュがあります。 このハッシュのキーにはインデックスフィールドの名前が含まれ、値はメインハッシュの対応する要素のIDを持つハッシュです。 インデックスがまだないため、this.indexDataは空です。
インデックスを追加します。
emp.createIndex("dep_id");
this.indexDataを見てみましょう。

this.indexDataには、インデックスデータをネストされたハッシュとして含むdep_idキーが含まれるようになりました。
インデックスで何かを探しましょう:
> emp.findIdsByIndex("dep_id","sales") < ["001", "002"]
Array.findなどの関数とは異なり、インデックス検索ではデータ列挙を使用せず、ハッシュのみを使用して高速化を実現します。 ただし、測定はこれまで行っていませんが、すぐに機能するはずです。
データを追加します。
emp.put({ id: "003", first_name: "George", last_name: "Clooney", dep_id: "hr", address_id: "400" }); emp.put({ id: "004", first_name: "Dev", last_name: "Patel", dep_id: "board", address_id: "500" });
インデックスで要素を見つけ、それらから新しいIndexedArrayを作成します。
var sales_emp = emp.where("dep_id","sales");
別のIndexedArrayを作成して設定します。
var adr = new StrelkiJS.IndexedArray(); adr.put({ id: "200", address: "New Orleans, Bourbon street, 100"}); adr.put({ id: "300", address: "Moscow, Rojdestvensko-Krasnopresnenskaya Naberejnaya"}); adr.put({ id: "500", address: "Bollywood, India"});
配列バインディング
次の形式のオブジェクトは、このIndexedArrayと他のオブジェクトとの関係を記述するために使用されます。
{ from_col: "address_id", // IndexedArray to_table: adr, // to_col: "id", // "id", type: "outer", // "outer" LEFT OUTER JOIN, null INNER JOIN join: // null , JOIN- }
adrをemp JOINに接続します。
var res = emp.query([ { from_col: "address_id", // name of the column in "emp" table to_table: adr, // reference to another table to_col: "id", // "id", or other indexed field in "adr" table type: "outer", // "outer" for LEFT OUTER JOIN, or null for INNER JOIN //join: [ // optional recursive nested joins of the same structure // { // from_col: ..., // to_table: ..., // to_col: ..., // ... // }, // ... //], } ])
同様のSQLステートメントは次のようになります。
SELECT ... FROM emp LEFT OUTER JOIN adr ON emp.address_id = adr.id
結果は次のようになります。
[ [ {"id":"001","first_name":"John","last_name":"Smith","dep_id":"sales","address_id":"200"}, {"id":"200","address":"New Orleans, Bourbon street, 100"} ], [ {"id":"002","first_name":"Ivan","last_name":"Krasonov","dep_id":"sales","address_id":"300"}, {"id":"300","address":"Moscow, Rojdestvensko-Krasnopresnenskaya Naberejnaya"} ], [ {"id":"003","first_name":"George","last_name":"Clooney","dep_id":"hr","address_id":"400"}, null ], [ {"id":"004","first_name":"Dev","last_name":"Patel","dep_id":"board","address_id":"500"}, {"id":"500","address":"Bollywood, India"} ] ]
3つのテーブルをリンクするより複雑な例
var dep = new StrelkiJS.IndexedArray(); dep.createIndex("address"); dep.put({id:"sales", name: "Sales", address_id: "100"}); dep.put({id:"it", name: "IT", address_id: "100"}); dep.put({id:"hr", name: "Human resource", address_id: "100"}); dep.put({id:"ops", name: "Operations", address_id: "100"}); dep.put({id:"warehouse", name: "Warehouse", address_id: "500"}); var emp = new StrelkiJS.IndexedArray(); emp.createIndex("dep_id"); emp.put({id:"001", first_name: "john", last_name: "smith", dep_id: "sales", address_id: "200"}); emp.put({id:"002", first_name: "Tiger", last_name: "Woods", dep_id: "sales", address_id: "300"}); emp.put({id:"003", first_name: "George", last_name: "Bush", dep_id: "sales", address_id: "400"}); emp.put({id:"004", first_name: "Vlad", last_name: "Putin", dep_id: "ops", address_id: "400"}); emp.put({id:"005", first_name: "Donald", last_name: "Trump", dep_id: "ops", address_id: "600"}); var userRoles = new StrelkiJS.IndexedArray(); userRoles.createIndex("emp_id"); userRoles.put({id:"601", emp_id: "001", role_id: "worker"}); userRoles.put({id:"602", emp_id: "001", role_id: "picker"}); userRoles.put({id:"603", emp_id: "001", role_id: "cashier"}); userRoles.put({id:"604", emp_id: "002", role_id: "cashier"}); var joinInfo = [ { from_col: "id", to_table: emp, to_col: "dep_id", type: "outer", join: [{ from_col: "id", to_table: userRoles, to_col: "emp_id", type: "outer", }], }, // { // from_col: "id", // to_table: assets, // to_col: "dep_id", // } ]; //var js1 = IndexedArray.IndexedArray.doLookups(dep.get("sales"),joinInfo); var js = dep.where(null,null,function(el) { return el.id === "sales"}).query(joinInfo); // result [ [ {"id":"sales","name":"Sales","address_id":"100"}, {"id":"001","first_name":"john","last_name":"smith","dep_id":"sales","address_id":"200"}, {"id":"601","emp_id":"001","role_id":"worker"} ], [ {"id":"sales","name":"Sales","address_id":"100"}, {"id":"001","first_name":"john","last_name":"smith","dep_id":"sales","address_id":"200"}, {"id":"602","emp_id":"001","role_id":"picker"} ], [ {"id":"sales","name":"Sales","address_id":"100"}, {"id":"001","first_name":"john","last_name":"smith","dep_id":"sales","address_id":"200"}, {"id":"603","emp_id":"001","role_id":"cashier"} ], [ {"id":"sales","name":"Sales","address_id":"100"}, {"id":"002","first_name":"Tiger","last_name":"Woods","dep_id":"sales","address_id":"300"}, {"id":"604","emp_id":"002","role_id":"cashier"} ], [ {"id":"sales","name":"Sales","address_id":"100"}, {"id":"003","first_name":"George","last_name":"Bush","dep_id":"sales","address_id":"400"} ,null ] ]
ご覧のとおり、配列バインディングは、ループと反復を伴う関数から単純な関係の宣言に進化しました。
制限事項
IndexedArrayはオブジェクトのコピーを保存せず、それらへのポインターのみを保存します(そのためStrelkiという名前です)。 したがって、オブジェクトがput()によってIndexedArrayに配置されてから変更された場合、インデックス内の情報が正しくなくなる可能性があります。 この状況を回避するには、変更する前にdel()メソッドを使用してIndexedArrayからオブジェクトを削除する必要があります。
リンクは、インデックス付きフィールドまたは「id」フィールドでのみ実行できます。
IndexedArrayオブジェクトの一部のメソッド(たとえば、length())では、「id」キー配列の構築が必要です。 この場合、キーの配列は再利用可能なようにオブジェクトに格納されます。 配列が変更されるたびに(put()、del()メソッドなど)、キー配列は無効になります。 したがって、キーの配列を作成してから無効にする方法を交互に実行すると、大きなデータセットでパフォーマンスの問題が発生する可能性があります。
計画
StrelkiJSは、先ほど書いたメインのKidsTrackプロジェクトの作成を容易にするために作成されました。 したがって、新しい機能に関するすべての決定は、親プロジェクトのニーズによって決まります。 近い将来-JOINの結果の列により便利にアクセスできるように、
ダウンロード先
Github: github.com/amaksr/Strelki.js
サンドボックス: www.izhforum.info/strelkijs