6

在这里阅读了很长时间后,我终于注册了一个问题。我最近一直在搞乱IndexedDB,偶然发现了复合索引的问题(我使用它们与此处的示例类似)。

我在对象存储中有一个带有字符串值和几个整数值的对象。例如:

[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

假设现在我搜索范围 (lower[1,1,1] , upper[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 与键范围不匹配。

4

2 回答 2

6
  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 不等于长度,则返回步骤 4。否则继续下一步。
  8. 如果 A 的长度小于 B 的长度,则 A 小于 B。如果 A 的长度大于 B 的长度,则 A 大于 B。否则 A 和 B 相等。

来自 KeyRange 部分:如果满足以下两个条件,则一个键在一个键范围内:

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

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

于 2014-05-22T14:34:09.257 回答
1

对于这种情况,您需要另一个复合索引。

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

于 2014-05-22T13:53:42.150 回答