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 回调
代码语言:javascript复制// 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类库的时候,我们就避免了在回调中写后续的逻辑。我们可以通过一个声明,自动完成相关的数据库操作。
代码语言:javascript复制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就十分简单了:
代码语言:javascript复制// 这是一个同步操作 返回Collection对象
let table = db.collection('user');
打开集合对象之后,我们就可以根据需要进行增删改查的工作了。
Collection Methods 查询方法
-- Promise方法 ( 以下方法返回Promise, 相当于直接有结果 可以通过最后一个参数call或者使用 promise.then() )
insert 插入数据 :Promise
可以插入一条数据( object ) 或多条数据 ( [object] )
代码语言:javascript复制db.collection('user').insert( {username:'user',age:5} );
db.collection('user').insert( [ {username:'user1',age:1}, {username:'user2',age:2} ] );
update 更新数据 :Promise
通过第一个参数进行数据筛选,第二个参数进行更新
代码语言:javascript复制db.collection('user').update( {
age: { $gte: 18 }
}, {
adult: true
});
remove 移除数据 :Promise
通过第一个参数筛选
代码语言:javascript复制db.collection('user').remove({
age: { $eq: 100 }
});
findOne 查询单条数据 :Promise
可选参数1用于筛选数据( WHERE ),可选参数2用于重新组织表结构( SELECT )
代码语言:javascript复制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对象。
代码语言:javascript复制db.collection('user').find({
gender: { $eq: "male" }
},{
userid:1,
username:1,
avatar:1
});
aggregate 管道查询 * :Cursor
管道参数可以是 object, 或 array [object] object是单次管道的具体参数 而array的话,则是表示了一系列管道操作的步骤。 每一次的操作结果都会传递到下一步管道中。 返回Cursor对象。
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。:Cursor
代码语言:javascript复制db.collection('article').aggregate(
{ $project : {
_id : 0 , // 不包含_id
title : 1 ,
author : 1
}});
// SELECT title,author FROM article
$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作 (库中只能使用库所支持的) 。:Cursor
代码语言:javascript复制db.collection('user').aggregate(
{ $match : {
gender: { $eq: "male" }
}});
// SELECT * FROM user WHERE gender = 'male'
$limit:用来限制MongoDB聚合管道返回的文档数。:Cursor
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。:Cursor
limit, skip加起来相当于SQL中的 LIMIT 语法
代码语言:javascript复制db.collection('user').aggregate([
{ $limit: 100 }, { $skip: 50 }
]);
// SELECT * FROM user LIMIT 50,50
$sort:将输入文档排序后输出。:Cursor
代码语言:javascript复制db.collection('user').find().sort({createtime:1});
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。( 在$group对象里可以使用group支持的运算进行统计和一些简单运算 )
混合写法:
代码语言:javascript复制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)
代码语言:javascript复制// 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
代码语言:javascript复制// 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 综合示例
代码语言:javascript复制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类似的。
代码语言:javascript复制### 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 (排序) 将输入文档排序后输出。
补充: 模糊查询
代码语言:javascript复制// 查询 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
代码语言:javascript复制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();
将自动更新放在你的数据库最开始的部分,修改你自己的结构。