在 IndexedDB 中搜索复合索引

Posted

技术标签:

【中文标题】在 IndexedDB 中搜索复合索引【英文标题】:Searching for compound indexes in IndexedDB 【发布时间】:2014-07-11 11:30:46 【问题描述】:

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

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

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

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

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

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

如果选择框有值,则该值用作上限和下限。如果未触及选择框,则下限为 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 的天真理解使我相信它会在搜索中考虑所有数组字段。由于对象以及我使用的索引比上面的示例复杂得多,因此对多个索引执行搜索会非常混乱。

感谢您的见解!

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

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 与键范围不匹配。

【问题讨论】:

感谢您的光临。我们在 IDB 标签上获得了很多新用户。如果您真的已经“在这里阅读了很多年”,那么既然您已经注册了,我鼓励您返回并支持一些您认为有帮助的答案。它将奖励我们中的一些人,他们试图确保 *** 是一个向 IndexedDB 提问的好地方。 @buley:当然。我会重新审视我设置的书签。 :) 编辑:哦,我只是想我需要 15 的声望才能投票... 【参考方案1】:

    当您在数组上创建索引时,仅当数组中与基础对象中的属性对应的每个元素都具有定义的值时,您的商店的条目才会出现在索引中。

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

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

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

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

根据您的 cmets,我建议您使用 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 类型的其他值进行比较,如下所示:

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

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

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

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

【讨论】:

To 1.: Atm 我有多个具有某种关系的对象。例如 ObjectStore Car id,brand、Bike id,brand 和 Person id,name,car,bike。汽车现在将有 id:1 brand:Ford, id:2 brand:BMW, ...。自行车也是如此。在主要对象 Person 中,我只会将表示字符串的整数存储在其他对象中,例如名称:史蒂夫,汽车:1,自行车:2。在我的应用程序中,我使用不同的对象,但它们必须定义定义的交叉引用(与此示例不同,在此示例中,人不一定有汽车或自行车)。所以我可以排除这个。 至 2.:已经检查过那个。我总是定义界限。继续上面的例子,假设我有一个选择框来选择一辆车。它读取对象存储 Car 并将字符串列为要选择的选项。如果选择了某些内容,则选择框将返回一个表示 ID 的整数。如果未选择任何内容(因为您想搜索所有条目),我将得到一个 NaN。然后我将其转换为一个范围(lower=1,upper=MAX_INT)。相反,选择条目时的范围是(lower=pickedID,upper=pickedID)。我已经大量测试过,我总是能得到一个合适的范围。 现在的问题是,当我没有选择第一个选择框(因此获得 1 到 MAX_INT 的范围),但第二个和第三个,它会给我所有条目,因为搜索似乎只对数组中的第一个值执行。它不会丢弃那些不在第二个和第三个值范围内的条目。 嗯。首先,请澄清确保您在创建索引时没有使用多条目标志,这使得在这里很难回答您的问题。 其次,确保索引包含您期望的内容。使用开发工具检查索引的内容。查看 keypath 列。查看特定索引的排序方式。【参考方案2】:

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

或者,您可以使用此处描述的密钥加入YDN-DB - Incorrect results using mixed data types with SortedMerge

【讨论】:

感谢您的评论。所以在这种情况下,我必须再选择 2 个复合索引,对吗?例如。 [val2, val3, val1] 和 [val3, val1, val2]。我看不出这与每个属性都有一个简单的索引之间的区别。这个解决方案的问题是我计划的对象将有 15 到 20 个属性。这会给我留下很多索引和不必要的高复杂性。 Atm 我真的不明白为什么我们能够拥有复合索引,因为它们无助于在一个事务中搜索多个条件(即键范围)。 顺便说一句,我已经查看了您的图书馆,看起来很酷。我只是想在没有任何额外包装的情况下解决这个问题,以便一开始就习惯股票行为。看来我得试一试了。继续努力! 复合索引只能有一个范围查询。当其中一个过滤器关闭(未选中复选框)时,它成为另一个范围查询。复合索引查询不适用于两个范围查询。因此。是的,复合会导致索引爆炸问题developers.google.com/appengine/docs/python/datastore/… 解决方案是使用 zigzag 合并。你真的需要一个图书馆来做到这一点。

以上是关于在 IndexedDB 中搜索复合索引的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB 复合索引与单字段索引在空间消耗方面的对比

Pandas中xs()函数索引复合索引数据的不同切面数据(索引复合索引中需要的数据):索引列复合索引中的一个切面索引行复合索引中的一个切面

复合数据类型,英文词频统计

使用 fts + 复合索引优化查询

MySQL:制作 3 个字段的复合索引,还是制作 3 个单独的索引?

sql server 2005 一个索引多个字段,字段的排列顺序对搜索有啥影响??