tangshuang / indb

Maybe the best way to understand and use indexedDB. 😄😄😄
60 stars 10 forks source link

你好,请问使用select怎么做分页 #2

Closed xianzou closed 4 years ago

xianzou commented 4 years ago

感觉您的教程让使用indexdb更加方便,但是目前我还是有一些不清楚的地方,比如如何做分页,尤其是当我们多条件查询的时候做分页,能否添加一下示例,感谢~

tangshuang commented 4 years ago

分页需要自己做,indexedDB 没有分页的概念。 有两种方案:1. 全部 select 出来之后做分片,这样可以做缓存;2. 使用游标遍历 store,取出一定数量之后就结束遍历,这样可以减少运行时内存消耗。

xianzou commented 4 years ago

谢谢,全部 select 出来之后做分片,这样可以前端进行分页,然后可以做缓存,但是不足之处就是数据量大的时候,性能肯定损耗很大; 如果是用游标的话,如何带上参数和查询条件呢~

tangshuang commented 4 years ago

游标就是为了遍历,没有参数,在遍历过程中自己决定当前这个 value 要不要加入到你最后输出的结果中。

tangshuang commented 4 years ago

你可以用 iterate 来做这个遍历 https://github.com/tangshuang/indb#iterate

xianzou commented 4 years ago

谢谢你,我已经有思路了,不过我看到迭代iterate用的也是cursor,不过并不可以传递ranage,如果做分页ranage还是比较有用处的;

xianzou commented 4 years ago

你好,我有写一个方法,用来进行处理带条件分页的场景,传入页数获取总页数和分页信息,我不知道这样做是不是合理,如果有空可以帮忙看下嘛,谢谢?

conditions是查询条件,如果没有传入查询条件,则通过db.count()获取总页数,通过索引index(自增)来获取值的范围;

如果是有条件的查询的话,目前的做法是通过油标去遍历所有的项,然后根据页数来判断是否可以存储到数组中,总页数也是遍历所有的数组

//创建一个迭代器
function iterateor(fn, pageInfo = {}) {
  const {
    indbSocurse,
    index,
    currentPage = 1,
    conditions = [],
    pageSize,
    pageStart = 1,
    pageEnd = 10
  } = pageInfo;

  let range = null;
  // 没有查询条件
  if (!conditions.length) {
    range = IDBKeyRange.bound(
      pageStart,
      pageEnd,
      false,
      false
    );
  }
  return new Promise((resolve, reject) => {
    indbSocurse.cursor({
      writable: false,
      direction: "next",
      index: index,
      range: range,
      onTouch: (cursor, owner) => {
        const next = () => cursor.continue();
        const stop = () => {
          owner.transaction.abort();
          resolve();
        };
        fn(cursor, next, stop);
      },
      onDone: () => {
        resolve();
      },
      onError: e => {
        reject(e);
      }
    });
  });
}

/****
 * function ==> 获取列表信息
 * 参数解释
 *  pageSize 一页多少条 number
 *  conditions 条件 Array [{age:18},{name:'xxx'}] 注意 如果是string类型的使用indexOf查找,否则使用 ===
 *  indbSocurse indexdb数组源 示例:const indbSocurse = idb.use("students");
 *  index 索引列表 String类型 index在indexeddb里面的值必须为number类型,设置成自增长
 *  currentPage 当前第几页
 *  return {
 *      total,
 *      pageSize,
 *      pageNumber,
 *      data,
 *      currentPage
 *  }
 *
 */
export const queryList = async ({
  indbSocurse = {},
  conditions = [],
  pageSize = 10,
  index = "id",
  currentPage = 1
}) => {
  // 判断indbSocurse是不是正确的对象
  if (!indbSocurse.db) {
    throw Error("indbSocurse必须是一个idb实例化对象");
  }
  const data = [];
  let total = 0;
  // 计算当前页的开始和结束
  const pageStart = (currentPage - 1) * pageSize + 1;
  const pageEnd = currentPage * pageSize;

  await iterateor(
    (cursor, next, stop) => {
      //判断是否符合条件
      // 没有查询条件
      if (!conditions.length) {
        data.push(cursor.value);
      } else {
        let isMeet = false;
        for (let row of conditions) {
          if (typeof row[Object.keys(row)] === "string") {
            isMeet =
              cursor.value[Object.keys(row)].indexOf(row[Object.keys(row)]) >
              -1;
          } else {
            isMeet = cursor.value[Object.keys(row)] === row[Object.keys(row)];
          }
        }
        if (isMeet) {
          total++;
          if (total >= pageStart && total <= pageEnd) {
            data.push(cursor.value);
          }
        }
      }
      next();
    },
    {
      indbSocurse,
      index,
      currentPage,
      conditions,
      pageSize,
      pageStart,
      pageEnd
    }
  );
  if (!conditions.length) {
    //没有查询条件
    total = await indbSocurse.count();
  }
  //总共多少页
  const pageNumber = Math.ceil(total / pageSize);

  return {
    total,
    pageSize,
    pageNumber,
    data,
    currentPage
  };
};
li-breeder commented 3 years ago

同需求。 看看。

tangshuang commented 3 years ago

@xianzou 是否可以提个pr