在IndexedDB中搜索复合索引

Searching for compound indexes in IndexedDB

本文关键字:复合 索引 搜索 IndexedDB      更新时间:2023-09-26

看了这么久,我终于注册问一个问题了。我最近一直在摆弄IndexedDB,偶然发现了复合索引的一个问题(我使用它们与这里的示例类似)。

我在objectstore中有一个对象,它有一个字符串值和几个整数值。e . g。

[description:text, value1:int, value2:int, value3:int]

我在这个对象上创建了一个复合索引:

("compoundIndex", ["value1" , "value2" , "value3"] , { unique: false });

在html中,我得到了几个选择框和一个文本框,允许用户搜索特定的条目。整数作为键范围传递给索引上的opencursor函数。然后在结果集上使用indexOf(textfield)(就像这里所做的那样)

如果选择框有一个值,该值将用作上限和下限。如果选择框未被修改,则较低的范围为1,而较高的范围是我声明的MAX_INT变量(如这里所述)。

示例代码:

transaction = db.transaction(["schaden"] , "readonly").objectStore("schaden");
index = transaction.index("compoundIndex");
// keyrange-arrays from another function    
lowerBound = [valueOneLower, valueTwoLower, valueThreeLower];
upperBound = [valueOneUpper, valueTwoUpper, valueThreeUpper];
range = IDBKeyRange.bound( lowerBound, upperBound );
index.openCursor(range).onsuccess = function(e){
  var cursor = e.target.result;
  if (cursor){
    if (getTextfield.length == 0){
      console.log("Entry found: " + cursor.value.description + ". Object: " + JSON.stringify(cursor.value));
    }else if (cursor.value.bezeichnung.indexOf(getTextfield) !== -1){
      console.log("Entry found: " + cursor.value.description + ". Object: " + JSON.stringify(cursor.value));
    };
    cursor['continue']();                           
    };
  };    

当我在所有选择框中设置了所有值时,我可以很好地搜索条目。然而,如果我留下一个开放的字段,它就会扰乱搜索。假设我没有触及value1-select框,并将其他框设置为2,我将得到lowerBound =[1,2,2]和upperBound =[4294967295,2,2]。这将返回我IDB中的所有条目,它不考虑第二个和第三个值。

这是有意的吗?还是有别的办法?我一直在一遍又一遍地搜索有关这方面的信息,但似乎是在一个死胡同。我对这个API的天真理解使我相信它会在搜索时考虑到所有数组字段。由于我使用的对象和索引比上面的例子要复杂得多,因此在多个索引上执行搜索将非常混乱。

谢谢你的见解!

编辑:在第一个注释之后让它更清楚一点。假设在对象存储库中有以下对象:

obj1 { val1 = 1 , val2 = 3 , val3 = 1 }
obj2 { val1 = 1 , val2 = 2 , val3 = 2 }
obj3 { val1 = 2 , val2 = 1 , val3 = 3 }
obj4 { val1 = 1 , val2 = 1 , val3 = 1 }
obj5 { val1 = 1 , val2 = 2 , val3 = 3 }

索引按预期方式排序:

#1 [1,1,1] obj4
#2 [1,2,2] obj2
#3 [1,2,3] obj5
#4 [1,3,1] obj1
#5 [2,1,3] obj3

让我们假设现在我搜索范围(较低[1,1,1],较高[1,1,1]),我会得到obj4。这是当所有选择框都选择选项1时的行为。现在,如果我搜索val1 = 1, val2 = unknown和val3 = 1的条目,我将得到以下范围:较低[1,1,1],较高[1,4294967295,1]。预期结果为obj4[1,1,1]和obj1[1,3,1]。而不是这些,结果是给我4个点击,即obj4, obj2, obj5和obj1,尽管obj2和obj5的val3不匹配的关键范围。

  1. 当你在数组上创建索引时,只有当数组中对应底层对象属性的每个元素都有定义值时,你的存储条目才会出现在索引中。

    要绕过这个障碍,始终将定义的值存储在底层对象存储区中。例如,要表示布尔属性,请使用整数,其中0为假,1为真。这样,存储中的每个对象都可以出现在索引中。indexedDB在这里的行为与普通旧javascript中的真/假处理(其中0 == undefined)有很大不同。

  2. 在基于数组的索引上打开游标时指定的键范围必须为数组的每个元素使用已定义的参数。

    为了绕过这个障碍,你必须指定所有的边界,即使这些边界不是真实的值(例如,在我的例子中,200是最大年龄,因为我们可以安全地假设没有人是200岁)。

所以要解决你的问题,它可能是一个问题,在你的代码中的参数之一,你的边界变量(要么[valueOneLower, valueTwoLower, valueThreeLower]或[valueOneUpper, valueTwoUpper, valueThreeUpper])没有定义。

根据您的评论,我建议您使用indexedDB.cmp来测试您的期望。编写这些测试非常简单。它不需要任何数据库连接。下面是一个非常基本的示例:

// Build our test values
var lower1 = 1, lower2 = 1, lower3 = 1;
var upper1 = 3, upper3 = 3, upper3 = 3;
var middle1 = 2, middle2 = 2, middle3 = 2;
var lowerBound = [lower1,lower2,lower3];
var upperBound = [upper1,upper2,upper3];
var middleValue = [middle1,middle2,middle3];
// As the linked page provides, cmp returns -1 if first is less than second, 0 if equal, 1 if first is greater than second.
var lowerVsMiddle = indexedDB.cmp(lowerBound, middleValue);
console.log('Is %s < %s ? %s', lowerBound, middleValue, lowerVsMiddle == -1);
console.log('Is %s > %s ? %s', lowerBound, middleValue, lowerVsMiddle == 1);
var upperVsMiddle = indexedDB.cmp(upperBound, middleValue);
console.log('Is %s < %s ? %s', upperBound, middleValue, upperVsMiddle == -1);
console.log('Is %s > %s ? %s', upperBound, middleValue, upperVsMiddle == 1);

通过这样的测试,你应该能够准确地回答你的问题。

我为您检索了indexedDB规范的相关部分。首先要注意的是,只有当数组中的每一项都被定义并且是一个有效的键时,数组才是一个有效的键。这关系到对象是否会出现在索引中,也关系到键参数是否为cmp或IDBKeyRange。可以使用bound/lowerBound/upperBound。第二,再往下,注意以下内容:

Array类型的值与其他Array类型的值的比较如下:

  1. 设A为Array的第一个值,B为Array的第二个值。
  2. 取A的长度和B的长度中较小者
  3. 设为0
  4. 如果A的第i个值小于B的第i个值,则A小于B
  5. 如果A的第i个值大于B的第i个值,则A大于B,跳过其余步骤。
  6. 将i增加1.
  7. 如果i不等于length,返回步骤4。否则继续下一步。
  8. 如果A的长度小于B的长度,则A小于B。如果A的长度大于B的长度,则A大于B。否则A和B相等。

来自KeyRange部分:如果满足以下两个条件,则键位于键范围中:

  • 键范围下限未定义或小于key。如果lowerOpen为false,它也可能等于key。
  • 键范围上限未定义或大于key。如果upperOpen为false,它也可能等于key。

现在我根据评论和你的进一步编辑理解了你的问题:本质上indexedDB提供了标准的联合行为,但你想要的是一个交集。解决这个问题的一种方法是根本不考虑正常形式的数据,而是考虑如何设置数据,以便以您想要的方式查询数据。这很有趣,值得思考,我不能马上给你一个答案。

您需要另一个复合索引。

或者,您可以使用此处描述的键连接YDN-DB -使用SortedMerge使用混合数据类型的不正确结果