在自己的app.js文件中把Wilddog作为依赖注入到我们的module中:
angular.module("starter",["ionic","wilddog"])
现在我们就可以使用 wild-angular 的$wilddogObject、$wilddogArray、$wilddogAuth来对数据进行操作了。
提示和建议
1,我们建议你直接使用野狗官方提供的SDK地址。
这样,你将无需更新任何代码,即可获得更新。
2,wilddog.js是经过大量测试的RELEASE版本。
3,野狗全站均支持Spdy3.1和Gzip压缩。
我们正在尝试更高的压缩比,例如SDCH,Http2来进一步提升静态资源加载速度。
4,不用担心Https的性能。
我们对Https进行了极致的优化。
野狗的官网,直到windows.onload事件触发,也只花费了不到500ms。
2.了解数据
数据是一棵JSON树
所有的数据都存储在各个JSON对象中,没有任何表的概念。
当你把数据添加到这棵json树中,这些数据就变成这棵树的子树。
比如,我们在users/mchen 下增加 widget后,我们的数据是这样的:
{
"users":
{
"mchen":
{
"friends":
{"brinchen":
true},
"name":
"MaryChen",
//新数据节点会增加在已经存在的JSON树中
"widgets":
{"one":
true,"three":
true}
},
"brinchen":
{...},
"hmadi":
{...}
}
}
创建一个Wilddog对象引用
在html中读写wilddog数据,需要创建一个Wilddog对象引用,要操作和同步哪些数据取决于创建Wilddog对象引用时传入的URL
newWilddog('https:
//
创建一个Wilddog引用并不是直接访问这个URL,或创建一个连接。
数据直到需要的时候才会传输。
一旦这个数据被查询,这个数据会一直与服务端保持一致。
你可以直接访问一个子节点:
newWilddog('https:
//
你还可以通过child接口进行相对路径访问:
varrootRef=newWilddog('https:
//
rootRef.child('users/mchen/name');
Wilddog中的数组
Wilddog并不天然支持数组,当我们想存数组时,我们把数组变成对象:
//原始数据
['hello','world']
//存储后的新数据
{0:
'hello',1:
'world'}
需要注意的是,如果某节点的所有子节点的key都是整数,且0到key的最大值之间,超过一半的key都有非空的值,那么wilddog客户端会将它们当作数组来处理。
限制和约束
描述
约束
备注
树的深度
32
key的长度
768bytes
UTF-8编码,不能包含. $ # [ ] /和ASCII控制字符0-31和127
一个叶子节点的数据大小
1mb
UTF-8编码
通过SDK写入的数据大小限制
2mb
UTF-8编码
通过REST写入数据大小限制
4mb
一次能读取的节点
2000
一次条件查询能返回的最大条数
500
如使用limitToFirst()、limitToLast()等
3.保存数据
保存数据的方式
method
description
set()
写入和替换当前路径的数据
update()
修改部分子节点的数据
push()
在当前节点下新增一个数据,数据的key随机生成
transaction()
一个复杂数据被并发更新导致数据错误,使用事务防止数据被并发更新
用set()写数据
set是Wilddog最基本的写数据操作。
set()设置当前节点的值,如果当前节点已经存在值,set会将旧值替换成新值。
为了理解set的工作原理,我们创建一个简单博客app,这个博客的app储存在这里:
varref=newWilddog("https:
//
我们用唯一的用户名来标识一个用户,存储他们的全名和生日。
首先,我们创建一个引用,然后用set储存数据,set可以传入 string,number,boolean,object 类型:
varusersRef=ref.child("users");
usersRef.set({
alanisawesome:
{
date_of_birth:
"June23,1912",
full_name:
"AlanTuring"
},
gracehop:
{
date_of_birth:
"December9,1906",
full_name:
"GraceHopper"
}
});
这时,数据被嵌套保存到了相应的位置上,完成上面的过程,你也可以直接这样做:
usersRef.child("alanisawesome").set({
date_of_birth:
"June23,1912",
full_name:
"AlanTuring"
});
usersRef.child("gracehop").set({
date_of_birth:
"December9,1906",
full_name:
"GraceHopper"
});
这两种方式的区别是,如果/user下面原来有数据的时候,第一种方式会把数据全部覆盖掉,而第二种方式只会覆盖 alanisawesome 和gracehop 两个子节点.
更新已经存在的数据
如果你想同时更新多个子节点,而不覆盖其他的子节点,你可以使用update()函数:
varhopperRef=usersRef.child("gracehop");
hopperRef.update({
"nickname":
"AmazingGrace"
});
这样会更新Grace的数据,更新她的 nickname 。
如果我们用 set 而不是 update ,date_of_birth 和 full_name 都会被删除。
保存一个列表
当多个用户同时试图在一个节点下新增一个子节点的时候,这时,数据就会被重写,覆盖。
为了解决这个问题,Wilddogpush()采用了生成唯一ID作为key的方式。
通过这种方式,多个用户同时在一个节点下面push数据,他们的key一定是不同的。
这个key是通过一个基于时间戳和随机的算法生成的。
wilddog采用了足够多的位数保证唯一性。
用户可以用push向博客app中写新内容:
varpostsRef=ref.child("posts");
postsRef.push({
author:
"gracehop",
title:
"AnnouncingCOBOL,aNewProgrammingLanguage"
});
postsRef.push({
author:
"alanisawesome",
title:
"TheTuringMachine"
});
产生的数据有一个唯一ID:
{
"posts":
{
"-JRHTHaIs-jNPLXO":
{
"author":
"gracehop",
"title":
"AnnouncingCOBOL,aNewProgrammingLanguage"
},
"-JRHTHaKuITFIhnj":
{
"author":
"alanisawesome",
"title":
"TheTuringMachine"
}
}
}
获取唯一ID调用push会返回一个引用,这个引用指向新增数据所在的节点。
你可以通过调用 key() 来获取这个唯一ID
//通过push()来获得一个新的数据库地址
varnewPostRef=postsRef.push({
author:
"gracehop",
title:
"AnnouncingCOBOL,aNewProgrammingLanguage"
});
//获取push()生成的唯一ID
varpostID=newPostRef.key();
使用事务保存数据
当处理可能被并发更新导致损坏的复杂数据时,比如增量计数器,我们提供了事务操作。
事务操作需要提供两个参数:
一个更新函数和一个可选的完成callback函数。
更新函数提供当前数据,当前数据是云端读取的。
举例说明,如果我们想在一个的博文上计算点赞的数量,我们可以这样写一个事务:
varupvotesRef=newWilddog('https:
//
upvotesRef.transaction(function(current_value){
//
return(current_value||0)+1;
});
我们使用currentData.getValue()!
=null来判断计数器是否为空或者是自增加。
如果上面的代码没有使用事务,当两个客户端在同时试图累加,那结果可能是为数字1且非数字2。
注意:
doTransaction()可能被多次被调用,必须处理currentData变量为null的情况。
当事务允许时,本地不会缓存从云端读取的数据。
更多关于事务 API的文档。
4.获取数据
到目前为止,我们已经了解到如何向Wilddog中保存数据,现在我们来看看如何查询数据。
Wilddog查询数据的方式是绑定一个异步监听的回调函数,每当数据被第一次查询或者数据发生变化,这个回调函数都会被执行。
重新看博客app的例子,我们可以这样读取博客数据:
//获得一个数据库连接实例
varref=newWilddog("https:
//
//监听数据
ref.on("value",function(snapshot){
console.log(snapshot.val());
},function(errorObject){
console.log("Thereadfailed:
"+errorObject.code);
});
运行这段代码,我们会看到控制台中打印出一个对象,包含所有的博客数据。
每次当有新的博客数据被写入时,这个回调函数都会被触发执行。
回调函数接收一个Snapshot类型作为参数。
一个Snapshot对象代表的是在指定的数据路径下,某一个时间点上数据的一个快照。
调用它的val()函数,可以得到一个代表当前节点数据的javascript对象。
如果路径节点下没有数据,那么这个Snapshot将为null。
这个例子中我们用 'value' 作为事件类型,用于读取当前路径节点下的所有数据。
我们还可以使用另外三种数据事件类型。
事件类型
WildDog提供了四种数据事件:
value,child_added,child_changed,child_removed。
valuevalue 事件用来读取当前节点的静态数据快照, value 事件在初次获取到数据时被触发一次,此后每当数据发生变化都会被触发。
回调函数被执行时候,当前节点下所有数据的静态快照会被作为参数传入。
child_added与value事件返回指定数据节点下的所有数据不同,child_added事件会为每一个现存的子节点数据触发一次,此后每当有子节点数据被增加时被触发一次。
回调函数传入的也是DataSnapshot对象,包含的是新增子节点的数据。
如果我们只想获取新增的博客数据,可以使用 child_added事件:
//获得一个数据库连接实例
varref=newWilddog("https:
//
//获得新增加的数据
ref.on("child_added",function(snapshot){
varnewPost=snapshot.val();
console.log("Author:
"+newPost.author);
console.log("Title:
"+newPost.title);
});
child_changed当子节点数据发生了改变时,child_changed事件会被触发,事件回调函数中传入的参数是子节点改变后的数据快照。
我们可以使用child_changed事件来读取被修改的博客数据:
//获得一个数据库连接实例
varref=newWilddog("https:
//
//获得发生改变的数据
ref.on("child_changed",function(snapshot){
varchangedPost=snapshot.val();
console.log("Theupdatedposttitleis"+changedPost.title);
});
child_removed当直接子节点被删除时,child_removed 事件被触发。
事件回调函数中传入的参数是被删除的直接子节点的数据快照。
在博客的例子中,我们可以使用child_removed事件,在有博客数据被删除时,在控制台中打印出来:
//获得一个数据库连接实例
varref=newWilddog("https:
//
//获取被删除的数据
ref.on("child_removed",function(snapshot){
vardeletedPost=snapshot.val();
console.log("Theblogposttitled'"+deletedPost.title+"'hasbeendeleted");
});
取消事件绑定
通过off()函数可以取消一个事件回调函数的绑定:
ref.off("value",originalCallback);
一次性读取数据
在某些场景下,也许需要事件的回调函数只被触发一次,然后立即取消。
可以使用once()函数:
ref.once("value",function(data){
//执行业务处理,此回调函数只会被调用一次
})
查询数据
Wilddog支持选择性的查询数据。
要构造一个查询,需要先指定数据如何排序,可以使用以下几个函数:
orderByChild(),orderByKey(), orderByValue(), orderByPriority()。
接下来,可以组合以下五个函数,构造出一个复杂的条件查询:
limitToFirst(),limitToLast(),startAt(),endAt(),equalTo()。
下面我们来举例如何进行数据查询。
假设现在有一些关于恐龙的数据如下:
{
"lambeosaurus":
{
"height":
2.1,
"length":
12.5,
"weight":
5000
},
"stegosaurus":
{
"height":
4,
"length":
9,
"weight":
2500
}
}
按照指定的子节点排序通过将子节点的路径名作为参数传递给orderByKey(),可以实现按指定子节点排序。
例如,要按照height进行排序,可以:
varref=newWilddog("https:
//
ref.orderByChild("height").on("child_added",function(snapshot){
console.log(snapshot.key()+"was"+snapshot.val().height+"meterstall");
});
不包含指定子节点的数据节点,将会按照该子节点为null进行排序,会排在最前边。
每个查询都只能有一个排序。
在一个查询中多次调用orderByChild()会抛出错误。
按照数据节点名称排序使用orderByKey()函数,可以实现按照数据节点的名称进行排序。
下面的例子按照alpha字母顺序读取所有的恐龙数据:
varref=newWilddog("https:
//
ref.orderByKey().on("child_added",function(snapshot){
console.log(snapshot.key());
});
按照数据节点的值排序使用orderByValue()函数,我们可以按照子节点的值进行排序。
假设恐龙们进行了一场运动会,我们统计到它们的得分数据:
{
"scores":
{
"bruhathkayosaurus":
55,
"lambeosaurus":
21,
"linhenykus":
80,
"pterodactyl":
93,
"stegosaurus":
5,
"triceratops":
22
}
}
要按照得分进行排序,我们可以构造一个这样的查询:
varref=newWilddog("https:
//
scoresRef.orderByValue().on("value",function(snapshot){
snapshot.forEach(function(data){
console.log("The"+data.key()+"dinosaur'sscoreis"+data.val());
});
});
按照优先级排序关于优先级,请参考API文档中的相关部分。
复杂查询
我们已经了解到数据排序的函数。
接下来是limit类的和range类的查询,通过它们,可以构建出更为复杂的查询:
limit查询limitToFirst()和limitToLast()两个函数用于设置最大多少个子节点数据会被同步。
如果我们设置为100,那么初次获取到数据时,就只会最多触发100次事件回调函数。
如果数据库中有少于100条消息数据,child_added事件将会为每一条消息数据被触发一次。
如果消息数量多于100条,那么只有其中的100条会被触发child_added事件。
如果我们使用了limitToFirst()函数,那么被触发的100条将会是排序最前边的100条。
如果使用了limitToLast()函数,那么被触发的100条将是排序最后的100条。
继续恐龙的例子,我们可以获得体重最大的两种恐龙:
varref=newWilddog("https:
//
ref.orderByChild("weight").limitToLast
(2).on("chil