在IndexedDB中搜索复合索引

时间:2014-05-22 12:19:32

标签: javascript html5 indexeddb compound-index

在这里读了好几年之后,我终于注册了一个问题。我最近一直在搞乱IndexedDB,并且遇到了复合索引的问题(我将它们用于示例here)。

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

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

我在这个对象上创建了一个复合索引,如下所示:

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

在html中,我有两个选择框和一个文本字段,允许用户搜索特定条目。整数作为键范围传递给索引上的opencursor函数。然后我在结果集上使用indexOf(textfield)(比如done here

如果selectbox有值,则该值用作上限和下限。如果选择框不变,则较低的范围是1,而较高的是我声明的MAX_INT变量(如所描述的here)。

示例代码:

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的条目,我得到以下范围:lower [1,1,1],upper [1,4294967295,1]。预期结果是obj4 [1,1,1]和obj1 [1,3,1]。而不是这些,结果给了我4次命中,即obj4,obj2,obj5和obj1,尽管obj2和obj5的val3与键范围不匹配。

2 个答案:

答案 0 :(得分:3)

  1. 在数组上创建索引时,如果数组中与基础对象中的属性对应的每个元素都具有已定义的值,则商店的条目仅出现在索引中。

    要解决此障碍,请始终在底层对象库中存储已定义的值。例如,要表示布尔属性,请使用整数,其中0为false,1为true。这样,商店中的每个对象都可以出现在索引中。 indexedDB在这里的行为与普通旧javascript中的truthy / falsy处理完全不同(其中0 == undefined)。

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

    要解决这个障碍,你必须指定所有边界,即使这些边界不是真正的值(例如,在我链接的示例中,200作为最大年龄,因为我们可以安全地假设没有人是200岁)

  3. 因此,为了解决您的问题,您的代码中可能存在一个问题,即未定义边界变量的参数之一([valueOneLower,valueTwoLower,valueThreeLower]或[valueOneUpper,valueTwoUpper,valueThreeUpper])。 / p>

    根据您的评论,我建议您使用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 spec的相关部分。首先请注意“如果数组中的每个项都已定义并且是有效键,则”数组只是一个有效键...“。这与对象是否出现在索引中有关,也与cmp或IDBKeyRange.bound / lowerBound / upperBound的关键参数是否有效有关。其次,更进一步,请注意以下几点:

    将Array类型的值与Array类型的其他值进行比较,如下所示:

    1. 设A为第一个数组值,B为第二个数组值。
    2. 设长度是A的长度和B的长度中的较小者。
    3. 让我成为0.
    4. 如果A的第i个值小于B的第i个值,则A小于 比B.跳过剩下的步骤。
    5. 如果A的第i个值大于B的第i个值,则A大于B.跳过剩余的步骤。
    6. 将我增加1.
    7. 如果i不等于长度,请返回步骤4.否则继续执行下一步。
    8. 如果A的长度小于B的长度,则A小于B.如果A的长度大于B的长度,则A大于B.否则A和B相等。
    9. 从KeyRange部分:如果满足以下两个条件,则键位于键范围内:

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

      现在再说一遍我根据评论和您的进一步编辑理解您的问题:基本上indexedDB提供了条件的联合行为,但您想要的是一个交集。解决这个问题的一种方法是根本不考虑正常形式的数据,而是考虑如何设置数据,以便以您想要的方式查询数据。这是有趣的思考,我没有立即回答你。

答案 1 :(得分:1)

你需要另一个复合索引。

或者,您可以使用密钥加入,如YDN-DB - Incorrect results using mixed data types with SortedMerge

所述