テーブルに名前、ID、年齢、性別、学歴などがあるとします。IDがキーで、テーブルには名前、年齢、性別のインデックスも付けられます。私は、25歳以上のすべての男子生徒を名前順に並べておく必要があります。
これはmySQLでは簡単です。
SELECT * FROM table WHERE age > 25 AND sex = "M" ORDER BY name
IndexDBでは、インデックスを作成し、そのインデックスに基づいてクエリを並べ替えることができます。ただし、年齢や性別などの複数のクエリは許可されません。 queryIndexedDB(https://github.com/philikon/queryIndexedDB)と呼ばれる小さなライブラリを見つけました。これは、複合クエリを許可しますが、ソートされた結果を提供しません。
では、IndexedDBを使用しながら、ソートされた複合クエリを作成する方法はありますか?
この回答で使用されている複合クエリという用語は、WHERE句に複数の条件が含まれるSQL SELECTステートメントを指します。そのようなクエリはindexedDB仕様では言及されていませんが、配列で構成されるkeypathでインデックスを作成することにより、複合クエリの動作を概算できますプロパティ名の。
これは、インデックスの作成時にマルチエントリフラグを使用することとはまったく関係ありません。マルチエントリフラグは、indexedDBが単一の配列プロパティに対してインデックスを作成する方法を調整します。オブジェクトの単一の配列プロパティの値ではなく、オブジェクトプロパティの配列にインデックスを付けています。
この例では、「name」、「gender」、および「age」は、studentsオブジェクトストア内に格納されている学生オブジェクトのプロパティ名に対応しています。
_// An example student object in the students store
var foo = {
'name': 'bar',
'age': 15,
'gender': 'M'
};
function myOnUpgradeNeeded(event) {
var db = event.target.result;
var students = db.createObjectStore('students');
var name = 'males25';
var keyPath = ['name', 'gender', 'age'];
students.createIndex(name, keyPath);
}
_
次に、インデックスでカーソルを開くことができます。
_var students = transaction.objectStore('students');
var index = students.index('males25');
var lowerBound = ['AAAAA','male',26];
var upperBound = ['ZZZZZ','male',200];
var range = IDBKeyRange.bound(lowerBound, upperBound);
var request = index.openCursor(range);
_
ただし、これから説明しようとしている理由により、これが常に機能するとは限りません。
余談:openCursorまたはgetへの範囲パラメーターの使用はオプションです。範囲を指定しない場合、_IDBKeyRange.only
_が暗黙的に使用されます。言い換えると、境界カーソルにはIDBKeyRange
のみを使用する必要があります。
インデックスはオブジェクトストアに似ていますが、直接変更することはできません。代わりに、参照されるオブジェクトストアでCRUD(読み取り、読み取り、削除の作成)操作を使用すると、indexedDBが更新をインデックスに自動的にカスケードします。
ソートを理解することは、インデックスを理解するための基本です。インデックスは基本的に、特別にソートされたオブジェクトのコレクションです。技術的には、これもフィルタリングされますが、これについては後ほど触れます。一般に、インデックス上でカーソルを開くと、インデックスの順序に従って繰り返し処理が行われます。この順序は、参照されるオブジェクトストア内のオブジェクトの順序とは異なる可能性があり、おそらく異なります。これにより反復がより効率的になり、インデックス固有の順序のコンテキストでのみ意味を持つカスタムの下限と上限が許可されるため、順序は重要です。
インデックス内のオブジェクトは、ストアへの変更が発生したときにソートされます。ストアにオブジェクトを追加すると、オブジェクトはインデックスの適切な位置に追加されます。並べ替えは、2つの項目を比較し、1つのオブジェクトが他のオブジェクトよりも小さいか、他のオブジェクトよりも大きいか、等しいかを返す、Array.prototype.sortと同様の比較関数に要約されます。したがって、比較関数の詳細を掘り下げることにより、ソート動作をよりよく理解できます。
これは、たとえば、「Z」が「a」より小さく、string '10'がstring '020'より大きいことを意味します。
たとえば、仕様では、文字列型の値が日付型の値の前または後にどのように来るかを指定します。値が何を含んでいるかは問題ではなく、タイプのみです。
IndexedDBは型を強制しません。ここで自分を足で撃つことができます。一般的に、異なるタイプを比較したくありません。
既に述べたように、インデックスには、参照されるオブジェクトストアのすべてのオブジェクトが常に含まれるとは限りません。オブジェクトをオブジェクトストアに配置すると、インデックスの基になるプロパティの値が欠落している場合、そのオブジェクトはインデックスに表示されません。たとえば、年齢がわからない学生がいて、これを学生ストアに挿入した場合、特定の学生はmales25インデックスに表示されません。
インデックス上でカーソルを反復したときにオブジェクトが表示されない理由を不思議に思うときは、これを覚えておいてください。
Nullと空の文字列の微妙な違いにも注意してください。空の文字列はnot欠損値です。プロパティの空の文字列を持つオブジェクトは、そのプロパティに基づくインデックスに引き続き表示されますが、プロパティは存在するが未定義または存在しない場合はインデックスに表示されません。そして、それがインデックス内にない場合は、インデックス上でカーソルを反復しても表示されません。
範囲の上でカーソルを開くときに範囲で使用する下限または上限を作成するときは、配列のキーパスの各プロパティに有効な値を指定する必要があります。そうしないと、何らかのタイプのJavascriptエラーが発生します(ブラウザーによって異なります)。たとえば、nameプロパティが定義されていないため、IDBKeyRange.only([undefined, 'male', 25])
などの範囲を作成できません。
紛らわしいことに、IDBKeyRange.only(['male', 25])
のように、値の間違ったtypeを指定すると、名前が未定義の場合、上記の意味でエラーは発生しませんが、無意味になります。結果。
この一般的なルールには例外があります。長さの異なる配列を比較できます。したがって、配列のendから行う場合、および配列を適切に切り捨てる場合は、技術的に範囲からプロパティを省略できます。たとえば、IDBKeyRange.only(['josh','male'])
を使用できます。
indexedDB仕様 は、配列をソートするための明示的な方法を提供します。
タイプArrayの値は、次のようにタイプArrayの他の値と比較されます。
キャッチはステップ4と5にあります:残りのステップをスキップします。これが基本的に意味することは、[1、 'Z']と[0、 'A']のように2つの配列の順序を比較する場合、メソッドは最初の要素のみを考慮するということです。短絡評価(仕様のステップ4と5)のため、ZとAのチェックに取り掛かりません。
したがって、前の例は機能しません。実際には次のように機能します。
_WHERE (students.name >= 'AAAAA' && students.name <= 'ZZZZZ') ||
(students.name >= 'AAAAA' && students.name <= 'ZZZZZ' &&
students.gender >= 'male' && students.gender <= 'male') ||
(students.name >= 'AAAAA' && students.name <= 'ZZZZZ' &&
students.gender >= 'male' && students.gender <= 'male' &&
students.age >= 26 && students.age <= 200)
_
SQLや一般的なプログラミングでこのようなブール句を使用した経験がある場合は、条件の完全なセットが必ずしも関与していないことを認識しているはずです。これは、必要なオブジェクトのリストを取得できないことを意味します。これが、SQL複合クエリと同じ動作を実際に取得できない理由です。
現在の実装では、この短絡動作を簡単に回避することはできません。最悪の場合、ストア/インデックスからメモリにすべてのオブジェクトをロードしてから、独自のカスタムソート関数を使用してコレクションをソートする必要があります。
いくつかの短絡問題を最小化または回避する方法があります。
たとえば、index.get(array)またはindex.openCursor(array)を使用している場合、短絡の心配はありません。完全に一致するか、完全に一致しないかのいずれかです。この場合、比較関数は2つの値が同じかどうかを評価するだけで、一方が他方より大きいか小さいかを評価しません。
考慮すべき他のテクニック:
cmp function を使用すると、並べ替えの仕組みをすばやく簡単に確認できます。例えば:
_var a = ['Hello',1];
var b = ['World',2];
alert(indexedDB.cmp(a,b));
_
IndexedDB.cmp関数の1つの優れたプロパティは、そのシグネチャが Array.prototype.filter および Array.prototype.sort への関数パラメーターと同じであることです。接続、スキーマ、インデックスなどすべてを扱うことなく、コンソールから値を簡単にテストできます。さらに、indexedDB.cmpは同期であるため、テストコードに非同期のコールバック/約束を含める必要はありません。
私は数年遅れていますが、ジョシュの答えはクエリの「列」がインデックスのkeyPath
の一部であるシナリオのみを考慮していることを指摘しておきます。
上記の「列」のいずれかがインデックスのkeyPath
の外に存在する場合は、例で作成されたカーソルが反復する各エントリでそれらを含む条件をテストする必要があります。したがって、このようなクエリを処理している場合、またはインデックスがunique
でない場合は、反復コードを書く準備をしてください!
いずれにせよ、クエリをブール式で表すことができる場合は、 BakedGoods を確認することをお勧めします。
これらのタイプの操作の場合、厳密な等価クエリ(x ===? y
、xがobjectStoreまたはインデックスキーの場合)、ただし、独自のカーソル反復コードを記述する手間を省くことができます。
bakedGoods.getAll({
filter: "keyObj > 5 && valueObj.someProperty !== 'someValue'",
storageTypes: ["indexedDB"],
complete: function(byStorageTypeResultDataObj, byStorageTypeErrorObj){}
});
完全に透明にするために、BakedGoodsはmoiによって管理されています。
Linq2indexedDB を使用してみてください。このライブラリでは、複数のフィルト、複数のソートを使用でき、オブジェクトからデータを選択することもできます。また、クロスブラウザー(IE10、Firefox、Chrome)でも機能します。
非常に使いやすく、多くのコードと時間を節約できる、IndexedDBからのデータのクエリに使用できるライブラリJsStoreがあります。 。 ここ からさらに探索できます
これは JsStore を使用した同等のSQLクエリです。
var connection = new JsStore.Instance("DbName");
connection.select({
From: "TableName",
Where: {
age : {'>':'25'},
sex : 'M'
},
Order: {
By: 'Name'
},
OnSuccess:function (results){
console.log(results);
},
OnError:function (error) {
console.log(error);
}
});
SQLで考え、JSで書くだけです。お役に立てれば!
IndexedDBでは 1つのキー範囲クエリを開く のみを開くことができます。したがって、最も効率的なインデックス、この場合は「年齢」を使用します。カーソルの反復で性別を除外するだけです。配列反復メソッドを使用して後で実行できる順序。 IndexedDB APIは、事前に配置されたインデックスエントリ以外の順序付けには関心がありません。