基础知识可以在这里学习一下:

http://www.ruanyifeng.com/blog/2018/07/indexeddb.html 【浏览器数据库 IndexedDB 入门教程】

有一个对indexedDB的封装,https://github.com/dfahlander/Dexie.js 实现了丰富的操作。

1 快速入门:
1.1 数据库定义(declare database)

var db = new Dexie("MyDatabase");
db.version(1).stores({
    friends: "++id, name, age, *tags",
    gameSessions: "id, score"
});

提示: 不要在sql中声明所有的列,只需要声明需要建立索引的列,上面声明的几个列都是需要在where查询条件中要用到的列。

数据库定义语法

++   自增主键(Auto-incermented primary key)
&    唯一(Unique)
*    多条目索引(Multi-entry index)
[A+B]    复合索引(Compound Index)

提示:
① indexedDB中的多条目索引是指数组属性的索引,其中数组中的每个项都指向object/record/document。它类似于PostgreSQL中的GIN索引。
② compound(或composite)索引是基于多个keypath的索引。它可以用于在一个索引中高效地索引多个属性,以便轻松找到两个键及其值的组合的存在性。在dexe中,复合索引必须在定义数据库模式的时候分配。

1.2 数据库升级(upgrade)

db.version(1).stores({
    friends: "++id,name,age,*tags",
    gameSessions: "id,score"
});

db.version(2).stores({
    friends: "++id, [firstName+lastName], yearOfBirth, *tags", // 索引变更
    gameSessions: null // 删除该表

}).upgrade(tx => {
    // 只有在低于2版本的数据库中会执行以下操作
    return tx.table("friends").modify(friend => {
        friend.firstName = friend.name.split(' ')[0];
        friend.lastName = friend.name.split(' ')[1];
        friend.birthDate = new Date(new Date().getFullYear() - friend.age, 0);
        delete friend.name;
        delete friend.age;
    });
});

上面的操作中首先在定义时指定的版本是'1',后面执行了一次升级到了'2’版本,upgrade回调中是具体的升级内容。
提示:
① IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。

1.2 类绑定 (Class binding)

class Friend {
    // Prototype method
    save() {
        return db.friends.put(this); // Will only save own props.
    }

    // Prototype property
    get age() {
        return moment(Date.now()).diff (this.birthDate, 'years');
    }
}

db.friends.mapToClass(Friend);

1.3 向数据库中添加数据
向数据库中添加一条和多条数据

await db.friends.add({name: "Josephine", age: 21});

await db.friends.bulkAdd([
  {name: "Foo", age: 31},
  {name: "Bar", age: 32}
]);

1.4 更新数据库中的数据

await db.friends.put({id: 4, name: "Foo", age: 33});
await db.friends.bulkPut([
    {id: 4, name: "Foo2", age: 34},
    {id: 5, name: "Bar2", age: 44}
]);


await db.friends.update(4, {name: "Bar"});
await db.customers
    .where("age")
    .inAnyRange([ [0, 18], [65, Infinity] ])
    .modify({discount: 0.5});

1.5 删除数据

await db.friends.delete(4);
await db.friends.bulkDelete([1,2,4]);

const oneWeekAgo = new Date(Date.now() - 60*60*1000*24*7);

await db.logEntries
    .where('timestamp').below(oneWeekAgo)
    .delete();

1.6 查询数据

查询 数据中年龄在20-25之间,从第150行还是取25条数据,并以数组形式返回。

const someFriends = await db.friends
    .where("age").between(20, 25)
    .offset(150).limit(25)
    .toArray();

查询name是josephine的并忽略大小写,并且迭代输出 frind内容。

await db.friends
    .where("name").equalsIgnoreCase("josephine")
    .each(friend => {
        console.log("Found Josephine", friend);
    });

查询name中以a,b,c,d开头并忽略大小写,以数组形式返回。

const abcFriends = await db.friends
    .where("name")
    .startsWithAnyOfIgnoreCase(["a", "b", "c"])
    .toArray();

将age在0-18或者大于65的distance值更新为0.5。

await db.friends
    .where('age')
    .inAnyRange([[0,18], [65, Infinity]])
    .modify({discount: 0.5});

查找所有name中包含字符‘a’忽略大小写的,并以数组形式返回

const friendsContainingLetterA = await db.friends
    .filter(friend => /a/i.test(friend.name))
    .toArray();

1.7 复合索引使用示例

const forbundsKansler = await db.friends
    .where('[firstName+lastName]')
    .equals(["Angela", "Merkel"])
    .first();

如果使用的是dexie2.0版本,则可以更简单的方式

const forbundsKansler = await db.friends.where({
    firstName: "Angela",
    lastName: "Merkel"
}).first();
# 或者使用下面的方式
const forbundsKansler = await db.friends.get({
    firstName: "Angela",
    lastName: "Merkel"
});
// 该查询等同于:
//   select * from friends where firstName='Angela' order by lastName
const angelasSortedByLastName = await db.friends
    .where('[firstName+lastName]')
    .between([["Angela", ""], ["Angela", "\uffff"])
    .toArray()


await db.friends
    .where('age').above(25)
    .or('shoeSize').below(8)
    .or('interests').anyOf('sports', 'pets', 'cars')
    .modify(friend => friend.tags.push("marketing-target"));

1.8 检索前5项

const best5GameSession = await db.gameSessions
    .orderBy("score").reverse()
    .limit(5)
    .toArray();

1.9 连接查询

var db = new Dexie('music');
db.version(1).stores({
    genres: '++id,name',
    albums: '++id,name,year,*tracks',
    bands: '++id,name,*albumIds,genreId'
});

async function getBandsStartingWithA () {
    // Query
    const bands = await db.bands
        .where('name')
        .startsWith('A')
        .toArray();
    
    // Attach resolved properies "genre" and "albums" on each band
    // using parallel queries:
    await Promise.all (bands.map (async band => {
      [band.genre, band.albums] = await Promise.all([
        db.genres.get (band.genreId),
        db.albums.where('id').anyOf(band.albumIds).toArray()
      ]);
    }));
    
    return bands;
}

1.10 存储二进制数据

var db = new Dexie("MyImgDb");
db.version(1).stores({
    friends: "name"
});

// 下载并保存一个图片
async function downloadAndStoreImage() {
    const res = await fetch("some-url-to-an-image.png");
    const blob = await res.blob();
    // Store the binary data in indexedDB:
    await db.friends.put({
        name: "David",
        image: blob
    });
}

1.11 IndexedDB2.0中二进制数据索引

var db = new Dexie("MyImgDb");
db.version(1).stores({
    friends: "id, name" // 使用二进制UUID作为ID
});

// IndexedDB 2.0 允许索引 ArrayBuffer 和 XXXArray(typed arrays ) 不包括Blobs类型
async function playWithBinaryPrimKey() {
    // 在indexedDB中存储二进制数据
    await db.friends.put({
        id: new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]),
        name: "David"
    });

    // 二进制数据检索搜索
    const friend = await db.friends.get(
        new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]));

    if (friend) {
        console.log(`Found friend: ${friend.name}`);
    } else {
        console.log(`Friend not found`);
    }
}

1.12 IndexedDB 事务

await db.transaction('rw', [db.friends], async () => {
  const friend = await db.friends.get(1);
  ++friend.age;
  await db.friends.put(friend);
});

Tags: indexedDB, dexie.js, 浏览器数据库

Related Posts:
  • [尚无相关文章]

Leave a Comment