0%

idb 程式庫使用範例 [2] Cursor

上次介紹的idb的文章裡,少講到了一個部份:Cursor
這個功能通常會用在資料量非常巨大,或是要針對整個資料做運算時使用,可以節省大量運算資源消耗。

基本操作

cursor類似於資料庫的游標,依序一次抓一筆資料來進行處理,目前市面上成熟的資料庫都有這樣的功能。關於 Cursor 的詳細說明可以參考這裡

這邊我們關注如何利用idb來使用這個功能

範例:取得學生們(students)內的本次考試最高的得分score:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let max;
database.then(async(db) => {
// 取得sheet的cursor
const cursor = await db.transaction('students').store.openCursor();

// cursor.key = 該筆資料的key; cursor.value = 該筆資料的值
max = cursor.value.score;
// 資料讀取完cursor回傳false
while(cursor) {
if(cursor.value.score > max) max = cursor.value.score;
cursor = await cursor.continue();
}
console.log('最高分', max);
});

除了直接遍歷整個資料庫外,也能使用index來篩出最需要的數據,加快執行效率

範例:取得學生們(students)內就讀學校(school)是建國高中,本次考試最高的得分score:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let max;
database.then(async(db) => {
// 設定filter
const range = IDBKeyRange.only('建國高中');
// 取得sheet的cursor; openCursor(搜尋範圍, 讀取方向); 讀取方向預設是next
const cursor = await db.transaction('students').store.index('school').openCursor(range);

max = cursor.value.score;
while(cursor) {
if(cursor.value.score > max) max = cursor.value.score;
cursor = await cursor.continue();
}
console.log('最高分', max);
});

排序

indexedDB有幾個特點:

  1. 使用index的話會自動根據index的值做從小到大來排序給資料
  2. cursor可以從資料尾部倒著讀

從這邊我們可以發現:使用index獲取資料是隱含排序功能的,且利用cursor倒著讀的特性,我們可以取得從大到小排序好的資料

範例:取得學生們(students),本次考試最高的得分(score)的前20名,並降冪排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
database.then(async(db) => {
// 取得sheet的cursor
let read = 20;
let students = [];
const cursor = await db.transaction('students').store.index('score').openCursor(null, "prev");

max = cursor.value.score;
while(cursor && read !== 0) {
read -= 1;
students.push(cursor.value);
cursor = await cursor.continue();
}
console.log('分數前20名', students);
});

補充

由於目前indexedDB並不支援多個index範圍查詢,所以如過還要多設定過濾條件的話,得直接在while裡面設置。
比如說以前一個範例來講,我只要找出建中最高分前20位,可寫成

1
2
3
4
5
while(cursor && read !== 0) {
read -= 1;
if(cursor.value.school === '建國高中') students.push(cursor.value);
cursor = await cursor.continue();
}

逐筆修正

如果這次大家分數實在太差,為了不要讓太多人被當掉,長官決定讓所有人成績開根號x10,有沒有比較便捷的作法呢?

1
2
3
4
5
6
7
8
9
10
11
database.then(async(db) => {
const cursor = await db.transaction('students').store.openCursor();

while(cursor) {
const student = cursor.value;
student.score = Math.sqrt(student.score) * 10;
// cursor除了可以.update(value)外還能.delete(),刪除該筆資料
await cursor.update(student);
cursor = await cursor.continue();
}
});