IndexedDB“和”和“或”查询

IndexedDB “and” and “or” queries

我将以下内容存储在 IndexedDB 中:

[
  {
    name: 'pizza',
    ingredients: [ 'flour', 'water', 'yeast', 'salt', 'oil' ]
  }, {
    name: 'pasta',
    ingredients: [ 'semolina', 'salt', 'water' ]
  }, …
]

我在 ingredients 上有一个 multiEntry 索引:

store.createIndex('ingredientIdx', 'ingredients', { multiEntry: true });

现在,我想得到:

  1. 含有任何给定成分的膳食,例如flourwater

  2. 含有所有给定成分的膳食,例如semolinasalt water

我的天真方法目前对每个给定的成分执行多个查询,然后对结果进行过滤和重复数据删除。但这感觉一点也不像“数据库”。

IndexedDB 是否支持某种“或”或“和”查询?这样做显然 不起作用 :

const tx = db.transaction('meals');
const ingredientIdx = tx.objectStore('meals').index('ingredientsIdx');
const request = ingredientIdx.getAll([ 'flour' , 'water' ]);
// …

您已正确设置商店并正确使用索引并且正确地一次查询一个。您设置所有内容的方式没有任何问题。然而,不幸的是,indexedDB 并不能轻松地以您想要的方式有效地查询该数据,例如通过使用单个 cursor/getAll.

匹配多个对象

第一个问题是关于OR的使用。您不能在单个请求中执行此操作。您将需要执行多个请求。请注意,您至少可以同时执行这些请求。遍历操作数并从多条目索引中获取每个操作数,将每个结果附加到每个请求共享的单个数组中,同时考虑重复项。您需要以一种方式执行此操作,以便每个后续请求都不会在开始之前等待先前的请求完成。

第二个问题是关于AND的使用。您只能对一系列值进行 AND 式查询,例如就像这个下限和上限之间的所有数字一样。因此,您需要像现在一样一次查询一个。

关于 AND,您可以考虑聪明一点,以不同的方式构建数据。例如,为每种成分创建布尔值(使用 0 和 1)属性。例如。如果披萨有面粉,那么披萨对象有一个名为 ingredient_flour 的 属性,值为 1,如果披萨没有面粉,它要么没有 属性,要么有 属性 ingrediant_flour 的值为 0。然后您可以在不同的成分组合上创建各种索引。例如。您可以在面粉和水上创建一个索引,然后查询该索引以获取 IDBKeyRange.only([1,1]).

并发 OR 示例(类似这样):

function any(db, values) {
  return new Promise((resolve, reject) => {
    const objects = [];
    const transaction = db.transaction('meals');

    transaction.oncomplete = function(event) {
      resolve(objects);
    };

    transaction.onerror = function(event) {
      reject(event.target.error);
    };

    const store = transaction.objectStore('meals');
    const index = store.index('ingredients');

    function onsuccess(event) {
      const cursor = event.target.result;
      if (cursor) {
        const value = cursor.value;

        let exists = false;
        for (const object of objects) {
          if (object.name === value.name) {
            exists = true;
            break;
          }
        }

        if (!exists) {
          objects.push(value);
        }

        cursor.continue();
      }
    }

    for (const value of values) {
       // IDBKeyRange.only is implied in this next line
       const request = index.get(value);
       request.onsuccess = onsuccess;
    }

  });
}