ZangoDB是一个indexedDB的类MongoDB轻量级接口库,主要是为了更轻松快速的编写indexedDB相关的操作。

关于indexedDB: IndexedDB - MDN

Github: ZangoDB

在MDN的推荐中介绍了几款不同的轻量级类库 来简化indexdb的使用,其中dexie.js也是不错的,但是在多条件筛选上并没有支持,所以介绍一下ZangoDB。

对于熟悉MongoDB这类NoSQL的开发者来说,应该简单看一下文档就能够快速上手。这里我将会对熟悉关系型数据库的来做一下说明。

ZangoDB主要将indexedDB简化为3个对象

Db - 数据库

Collection - 集合(表)

Cursor - 游标 查询( SQL )

不同于关系型数据库的初始化时数据库,表,所有字段名称和类型,索引结构都要确定。

NoSQL数据库通常只需要建立数据库的名称,表名称以及需要索引的字段。 其他的数据可以任意存储。

ZangoDB的主要特性集中在运算符的部分,类似于SQL中的( GROUP BY, ORDER BY 等 ) 包括以下几类

文末会给出更详细的介绍

Filter Operators 筛选查询运算符

Expression Operators 表达式运算符

Update Operators 更新运算符

Group Operators 组运算符


1. 数据库 Db

打开和初始化数据库 :Db

在indexedDB环境下,通常数据库的结构是直接写在打开数据库的部分。 因为打开数据库时,当数据库版本号不一致或没有该数据库时会触发 onupgradeneeded 回调

// https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB
var db;
var request = indexedDB.open("MyTestDatabase",2);
request.onsuccess = function(event) {
  db = event.target.result;
};
request.onupgradeneeded = function(event) {
  // 保存 IDBDataBase 接口
  var db = event.target.result;

  // 为该数据库创建一个对象仓库
  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
};

而在使用ZangoDB类库的时候,我们就避免了在回调中写后续的逻辑。我们可以通过一个声明,自动完成相关的数据库操作。

let DBName = 'myDB', DBVersion = 10;
let DBStruct = {
    user:{ username:true, createtime:true },
    article:{ type:true },
    log:true
};
// 数据库结构只需要保留 表名称(user,article等) 而后续的只需要保留需要索引的字段名称, username:true 表示在建立username索引
let db = new zango.Db( DBName, DBVersion, DBStruct );

2. Collection 集合

打开集合( 选择数据表 ) :Collection

indexedDB需要先选择对应的集合,再进行相关的查找,而不是像SQL中使用 SELECT * FROM Table 语法。 举个例子:

SQL: 教导主任通过学校喇叭公告,“x年x班的全体同学到操场报道”。 IndexedDB: 教导主任先进入x年x班的教室,然后说“全体到操场报道”

indexedDB打开仓库(objectStore)涉及的比较多,而ZangoDB就十分简单了:

// 这是一个同步操作 返回Collection对象
let table = db.collection('user');

打开集合对象之后,我们就可以根据需要进行增删改查的工作了。

Collection Methods 查询方法

-- Promise方法 ( 以下方法返回Promise, 相当于直接有结果 可以通过最后一个参数call或者使用 promise.then() )

insert 插入数据 :Promise

可以插入一条数据( object ) 或多条数据 ( [object] )

db.collection('user').insert( {username:'user',age:5} );
db.collection('user').insert( [ {username:'user1',age:1}, {username:'user2',age:2} ] );
update 更新数据 :Promise

通过第一个参数进行数据筛选,第二个参数进行更新

db.collection('user').update( {
    age: { $gte: 18 }
}, {
    adult: true
});
remove 移除数据 :Promise

通过第一个参数筛选

db.collection('user').remove({
    age: { $eq: 100 }
});
findOne 查询单条数据 :Promise

可选参数1用于筛选数据( WHERE ),可选参数2用于重新组织表结构( SELECT )

db.collection('user').findOne({
    userid: { $eq: "100" }
},{
    userid:1,
    username:1,
    email:1
});

-- Cursor方法 (以下方法返回Cursor对象,需要进一步使用Cursor中的Methods方法才可以获得结果) find, aggregate 都是比较重要的部分。也是比较灵活多变的。

find 筛选查询* :Cursor

可选参数1用于筛选数据(WHERE),可选参数2用于重新组织结构(SELECT)。 返回Cursor对象。

db.collection('user').find({
    gender: { $eq: "male" }
},{
    userid:1,
    username:1,
    avatar:1
});

aggregate 管道查询 * :Cursor

管道参数可以是 object, 或 array [object] object是单次管道的具体参数 而array的话,则是表示了一系列管道操作的步骤。 每一次的操作结果都会传递到下一步管道中。 返回Cursor对象。

$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。:Cursor
db.collection('article').aggregate(
    { $project : {
        _id : 0 , // 不包含_id
        title : 1 ,
        author : 1
    }});
//  SELECT title,author FROM article
$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作 (库中只能使用库所支持的) 。:Cursor
db.collection('user').aggregate(
    { $match : {
        gender: { $eq: "male" }
    }});
// SELECT * FROM user WHERE gender = 'male'
$limit:用来限制MongoDB聚合管道返回的文档数。:Cursor
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。:Cursor

$limit, $skip加起来相当于SQL中的 LIMIT 语法

db.collection('user').aggregate([
    { $limit: 100 }, { $skip: 50 }
    ]);
// SELECT * FROM user LIMIT 50,50
$sort:将输入文档排序后输出。:Cursor
db.collection('user').find().sort({createtime:1});
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。( 在$group对象里可以使用group支持的运算进行统计和一些简单运算 )

混合写法:

db.collection('user').aggregate([
    { $match: { age: { $lt: 8 } } },
    { $group: { _id: '$userid', names: { $push: '$username' } } },
    { $unwind: '$names' }
]);

3. Cursor 游标

游标支持一系列的数据操作,类似于管道中的操作符,以方法的形式实现,均返回一个Cursor对象即可以链式多重操作。 ( 这里的操作附加在管道操作之后,所以不会影响管道的结果 ) 如果需要最终输出数据 使用 toArray(), forEach() 方法。 filter, group, limit, project, skip, sort, unwind, hint ? 除hint外使用参数与管道用法一致。

forEach() 遍历对象 :Promise

遍历Cursor对象中所有有效数据,并依次调用回调函数(Promise success)

// Callback
db.collection('user').find().forEach(function(usr){
    console.log(usr);
});
// Promise
function log( data ){ console.log(data); }
db.collection('user').find().forEach().then( log );

toArray() 输出数组 :Promise

// Callback
db.collection('user').find().toArray(function(users){
    console.log(users);
});
// Promise
function log( data ){ console.log(data); }
db.collection('user').find().toArray().then( log );

4. Sample 综合示例

db.collection('user')
    .find({gender:{$eq:'male'},createtime:{$gt:ISODate("2014-03-01T08:00:00Z")}})
    .sort({createtime:-1})
    .toArray()
    .then(function(users){  console.log(users);});

// SELECT * FROM user WHERE gender='male' AND createtime > 2014-03-01T08:00:00Z ORDER BY createtime DESC

5. Operators 操作符

由于ZangoDB是一个模拟MongoDB的接口实现,所以其提供的操作符是和MangoDB类似的。

### Filter Operators
### 筛选运算符
The following filter operators are supported:

$and        AND                     交集
$or         OR                      并集
$not        NOT                     不等于或字段值不存在
$nor        NOT ANY                 全不等于
$eq         =                       等于
$ne         != OR N-EXITS           不等于或不存在
$gt         >                       大于
$gte        >=                      大于等于
$lt         <                       小于
$lte        <=                      小于等于
$in         IN [,]                  属于
$nin        NOT IN [,] OR N-EXITS   不属于或不存在
$elemMatch  Match at least One      至少匹配一项
$regex      Match Regex             匹配正则
$exists     TRUE OR NULL            布尔真或存在值(包括null)

### Expression Operators
### 表达式运算符( 运算符 )
Expression operators can be used in combination with the group and projection operators.
这些运算符只能在 group, projection操作时结合使用.

The following expression operators are supported

$literal        string()    转字符
$add            +
$subtract       -
$multiply       *
$divide         /
$mod            mod         求余
$abs            abs         绝对值
$ceil           ceil        向上取整
$floor          floor       向下取整
$ln             ln
$log10          log10
$pow            pow         n次方
$sqrt           sqrt        n次方根
$trunc          trunc       截取
$concat         concat      合并
$toLower        toLower     转小写
$toUpper        toUpper     转大写
$concatArrays   concat[]    合并数组
$dayOfMonth                 天(日期号)
$year                       年
$month                      月
$hour                       时
$minute                     分
$second                     秒
$millisecond                毫秒


### Update Operators
### 更新操作符

The following update operators are supported: 

$set            set         设置值
$unset          unset       删除值
$rename         rename      重命名字段名称
$inc            increment   自增(Number)
$mul            multiply    自乘(Number)
$min                        小于 则设置为
$max                        大于 则设置为

$push           push        向数组中追加
$pop            pop         删除数组字段中的第一个或最后一个元素
$pullAll                    删除数组字段中所有指定值,如果指定值为数组,则删除匹配数组内的元素   
$pull                       符合条件的值将被删除
$addToSet                   数组字段增加一个值   

### Group Operators
### 组运算操作符

The following group operators are supported: 

$sum            sum         和
$avg            avg         平均数
$min            min         取最小
$max            max         取最大
$push                       向数组中追加
$addToSet                   数组字段增加一个值


### Aggregation Pipeline Stages 管道操作符
### The following aggregation pipeline stages are supported: 

$project:                   (缓存表)   修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match:         WHERE       (筛选)    用于过滤数据,只输出符合条件的文档。$match使用 ZangoDB 的标准查询操作。
$limit:         LIMIT       (数量)    用来限制 该查询 聚合管道返回的文档数。
$skip:                      (开始于)   在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind:                    (解对象)   将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:         GROUP BY    (成组)    将集合中的文档分组,可用于统计结果。
$sort:          ORDER BY    (排序)    将输入文档排序后输出。

补充: 模糊查询

// 查询 title 包含"教"字的文档:
db.col.find({title:/教/})

// 查询 title 字段以"教"字开头的文档:
db.col.find({title:/^教/})

// 查询 titl e字段以"教"字结尾的文档:
db.col.find({title:/教$/})

// 查询 title 包含"教书"或者“育人”字的文档:
db.col.find({title:/(教书|育人)/})

ZangoDB无法自动更新数据库结构的问题

ZangoDB can not update struct and indexes

const DBName    = 'Memoreasy';
const DBVersion = 28;
const DBStruct  = {
    memory: {"uid":true,"isRoot":true,"parentid":true,"category":true,"type":true,"createtime":true,"lasttime":true},
};

const DBUpdates = {

    22:{
        create:{
            test:{"uid":true}
        }
    },
    23:{
        remove:{
            test:true
        }
    },
    27:{
        update:{
            memory: {"parentid":false,"category":false},
        }
    },
    28:{
        update:{
            memory: {"parentid":true,"category":true},
        }
    }
};

const updateDB = function () {

    var request = window.indexedDB.open( DBName, DBVersion );
    request.onerror = function(event){
        console.log('打开数据库失败 Open indexedDB failed. Try Catch error');
        throw(event);
    };
    request.onsuccess = function(event){
        // window.ASDB = event.target.result;
        // suc( event.target.result );
    };
    request.onupgradeneeded = function(event){

        let db = event.target.result;
        let versionNumber = event.oldVersion;

        let upgradeStore = function( struct, method ){
            switch (method) {
                case 'create':
                    for ( let table in struct ){
                        var store = db.createObjectStore( table ,{ keyPath: "_id",autoIncrement:true });
                        for ( let idx in struct[table] ){
                            store.createIndex( idx, idx, {unique:false} );
                        }
                    }
                    break;
                case 'update':
                    for ( let table in struct ){
                        var store = event.currentTarget.transaction.objectStore(table);
                        for ( let idx in struct[table] ){
                            if( struct[table][idx] ){
                                store.createIndex( idx, idx, {unique:false} );
                            }else{
                                store.deleteIndex( idx );
                            }
                        }
                    }
                    break;
                case 'remove':
                    for ( let table in struct ){
                        db.deleteObjectStore( table );
                    }
                    break;
            }
        };

        for ( let version in DBUpdates ){
            if( version <= versionNumber ) continue;
            for ( let method in DBUpdates[version] ){
                upgradeStore( DBUpdates[version][method], method);
            }
        }
    };
};

updateDB();

将自动更新放在你的数据库最开始的部分,修改你自己的结构。

文章地址:




标签: indexeddb, zangodb

添加新评论