Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

indexedDB(柒) openCursor参数说明

  在indexedDB中需要查询多条数据,或者说遍历数据,就得用游标(Cursor)。它通过存储对象或索引对象上的openCursor方法来创建。这个方法虽然只有两个参数,但是很复杂,与上一篇的索引关系非常密切。游标的作用就是通过索引来遍历并筛选数据。
  为了测试,我们先创建这样一个索引结构的数据,并添加数据 var db,cn=indexedDB.open("MyDB");
indexedDB.deleteDatabase("MyDB");
cn.onupgradeneeded=function(e){
  db=e.target.result;
  var obj=db.createObjectStore("MyOBJ",{keyPath:"id"});
  obj.createIndex("a","a");
  obj.createIndex("b","b");
  obj.createIndex("ab",["a","b"]);
};
cn.addEventListener("success",function(e){
  var i,transaction,obj;
  db=e.target.result;
  transaction=db.transaction("MyOBJ","readwrite");
  obj=transaction.objectStore("MyOBJ");
  //添加测试数据
  for(i=0;i<12;i++)obj.put({id:i,a:i%4,b:Math.random()});
});
  存储对象上直接就有openCursor方法,我们可以不使用索引直接遍历数据。这在indexedDB(壹)篇中就演示过了,这种方式是根据主键来排序的。这篇中我们要强调的是条件与排序。排序很简单,和上一篇中的截图一样,索引对象已经把数据排好序了。indexedDB中的索引不仅类似SQL中的索引,还与SQL中的视图有一点点相似之处。如果不介意,可以把它暂时看做是一个以某个字段排序的视图。那么,我们只要从这个视图中读取数据便可以按照相应顺序访问。 cn.addEventListener("success",function(e){
  var i,transaction,obj,ind;
  db=e.target.result;
  transaction=db.transaction("MyOBJ","readwrite");
  obj=transaction.objectStore("MyOBJ");
  ind=obj.index("b"); //从存储对象中获取b这个索引对象
  console.log("开始遍历:");
  //从b索引对象中遍历数据
  ind.openCursor().onsuccess=function(e){
    if(e=e.target.result){
      console.log(e.value);
      e.continue();
    }else console.log("遍历完毕!");
  };
});

  由于b索引是b字段的索引,而b字段在写入数据时我们用了随机数,所以按照b字段排序的话就会得到一片混乱的结果。不过知悉看b字段是被从小到大排序好的。当然我们也可以用倒序来排列,这在openCursor的参数中指定。
  openCursor方法有两个参数。第一个是条件,默认值为null;第二个是排序,默认值为next表示从小到大排序,可以设置成prev来让结果倒序排列,类似SQL中的ASC和DESC。第二个参数比较简单,我就不演示了。openCursor的难点在于第一个参数!
  我们如果对输出的数据进行筛选呢?在indexedDB中这个功能比较弱,我们无法像SQL那样自己写个复杂的条件表达式。我们每次只能选择一个索引,所以条件也只能针对这个选中的索引来设置。而且,我们无法直接使用条件表达式,必须使用已经对象化好的东西——IDBKeyRange对象。这个对象不是作为构造器使用的,而是调用上面的静态方法。它有这么几个方法:
    IDBKeyRange.lowerBound(any,bool)
    IDBKeyRange.upperBound(any,bool)
    IDBKeyRange.bound(any,any,bool,bool)
    IDBKeyRange.only(any)
  别看这些方法乱糟糟的,其实很简单。他们只是为了定义一个区间而已,脑子里只要有一条数轴就OK。lower是左边界,upper是右边界。如果我们需要的区间之后一个边界,另一边是无穷大或无穷小,就可以时候lowerBound或upperBound来实现,他们的第一个参数就是相应边界的位置;第二个参数是一个布尔值,用于表示这个边界是开的还是闭的,或者说是包含给定值还是不包含给定值。默认值是false也就是包含指定值,比如 IDBKeyRange.lowerBound(2,false); //大于或等于2
IDBKeyRange.lowerBound(2,true); //大于2
  当我们需要同时设置区间的上下界时候就用bound方法,参数类似上面的两个方法。最后一个only方法是判断相等,和使用bound时把前两个参数设置成相同的值是等价的。总之只要记住它是区间的概念就行。 cn.addEventListener("success",function(e){
  var i,transaction,obj,ind,range;
  db=e.target.result;
  transaction=db.transaction("MyOBJ","readwrite");
  obj=transaction.objectStore("MyOBJ");
  ind=obj.index("a"); //从存储对象中获取b这个索引对象
  //区间:[1,3)
  range=IDBKeyRange.bound(1,3,false,true);
  console.log("开始遍历:");
  //从a索引对象中遍历数据(倒序)
  ind.openCursor(range,"prev").onsuccess=function(e){
    if(e=e.target.result){
      console.log(e.value);
      e.continue();
    }else console.log("遍历完毕!");
  };
});

  这个结果在a字段上包含1不包含3就是a∈[1,3),而且用了倒序的方式,所以是从大到小的输出。但是条件设置并没这么简单,我们还要多字段索引的情况。注意IDBKeyRange提供的方法中,前面的参数都是任意类型的。如果一个索引设置了多个字段,它就会是一个数组,我们如果传入数组就是数组与数组的比较。但这不是传统意义上的数组比较,在JavaScript中数组会被转换成字符来比较。而indexedDB中,这里的数组比较使用的是一种类似排序的算法。这东西比较难解释,先来看程序吧 cn.addEventListener("success",function(e){
  var i,transaction,obj,ind,range;
  db=e.target.result;
  transaction=db.transaction("MyOBJ","readwrite");
  obj=transaction.objectStore("MyOBJ");
  ind=obj.index("ab");
  range=IDBKeyRange.lowerBound([2,0.5]);
  console.log("开始遍历:");
  ind.openCursor(range).onsuccess=function(e){
    if(e=e.target.result){
      console.log(e.value);
      e.continue();
    }else console.log("遍历完毕!");
  };
});

  实际上我们的区间依然是个数轴,维度并没有因为数据的维度增加而增加,只是在比较的算法上做了一些处理。假设我们传入的参数是A,数据库中的数据是B。在比较时先对A[0]和B[0]做比较。如果A[0]大于或小于B[0],那么只有两种情况,要么丢弃数据,要么使用数据,不会再做其它项的比较了。只有A[0]和B[0]相等时候才会去比较A[1]和B[1],这就是我说的类似排序的算法。如果A[1]和B[1]也相等,那么才会根据我们在调用lowerBound时候设置的第二个布尔参数来判断。
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^