核心数据 ANY ANY 多重 INNER JOIN

Posted

技术标签:

【中文标题】核心数据 ANY ANY 多重 INNER JOIN【英文标题】:Core Data ANY ANY multi INNER JOIN 【发布时间】:2013-10-04 01:44:11 【问题描述】:

我有一个 CoreData 模型,其中包含大量可搜索的“食谱”对象。为了提高搜索效率,我有一个名为“关键字”的引用模型,它只有一个“术语”属性(索引)。该术语是一个单词,从“配方名称”进行处理和规范化。

因此,例如,如果一个食谱名为 Crème Brûlée,它将有两个关键字“creme”和“brulee”,然后我还将用户的搜索标准化,以便他们可以使用重音词来查找非重音词,反之亦然反之亦然。

当用户搜索时,我将字符串归一化,然后用空格分隔。搜索配方结果必须包含以所有拆分搜索组件开头的关键字。

我创建了一个动态谓词,该谓词会根据用户搜索的字数而增长。

如果您正在搜索“crem”,谓词将变为:

[NSPredicate predicateWithFormat:@"ANY keywords.term BEGINSWITH %@", @"crem"]

如果您搜索“crem bru”:

[NSPredicate predicateWithFormat:@"ANY keywords.term BEGINSWITH %@ AND ANY keywords.term BEGINSWITH %@", @"crem", @"bru"]

现在,我面临的问题。

就一个字,这速度很快。我可以在 3GS 上运行它,实时搜索建议没有明显滞后。当您在搜索中添加后续单词时,问题就出现了。我查看了 SQL 输出,我认为问题在于 CoreData 正在为每个“ANY keywords.term BEGINSWITH”做一个 INNER JOIN,但如果我自己重写查询,我可以简单地用一个 INNER JOIN 来完成。

在 3GS 上运行:

单个单词(0.0262 秒):

CoreData: sql: SELECT DISTINCT t0.Z_ENT, t0.Z_PK, t0.ZID, t0.ZNAME 从 ZCDRECIPE t0 加入 Z_4RECIPES t1 ON t0.Z_PK = t1.Z_5RECIPES 加入 ZCDKEYWORD t2 ON t1.Z_4KEYWORDS = t2.Z_PK WHERE NSCoreDataStringSearch(t2.ZTERM, ?, 8, 0) ORDER BY t0.ZNAME

CoreData:注解:sql连接获取时间:0.0262s

多字(0.2996 秒):

CoreData: sql: SELECT DISTINCT t0.Z_ENT, t0.Z_PK, t0.ZID, t0.ZNAME 从 ZCDRECIPE t0 加入 Z_4RECIPES t1 ON t0.Z_PK = t1.Z_5RECIPES 加入 ZCDKEYWORD t2 ON t1.Z_4KEYWORDS = t2.Z_PK JOIN Z_4RECIPES t3 ON t0.Z_PK = t3.Z_5RECIPES 加入 ZCDKEYWORD t4 ON t3.Z_4KEYWORDS = t4.Z_PK WHERE ( NSCoreDataStringSearch( t2.ZTERM, ?, 8, 0) AND NSCoreDataStringSearch(t4.ZTERM, ?, 8, 0)) ORDER BY t0.ZNAME

CoreData:注解:sql连接获取时间:0.2996s

即使它正在查看同一个表,您也可以看到,CoreData 不止一次地与 ZCDKEYWORD 进行 INNER JOIN。

在我的谓词中,有没有办法让它只将关键字表加载到联接中?

谢谢

【问题讨论】:

你是如何构建你的谓词的?带字符串? 使用字符串,它们相当于我帖子中的代码,但通过 for 循环生成,因此用户键入的每个单词都有自己的任何匹配项 【参考方案1】:

尝试使用复合谓词语法。在类似的情况下,我使用此设置获得了非常好的结果。

for (NSString *s in words) 
   finalPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:
     @[finalPredicate, [NSPredicate predicateWithFormat:
         @"ANY keywords.term BEGINSWITH %@", s]]];


编辑

在查找了我的旧解决方案后,我意识到您可能正在以错误的方式进行查找。这说得通。您有多个keywords 查找,因此会有两个连接。

最好从关系中的其他实体派生所需的数据。谓词应直接引用关键字实体:

[NSPredicate predicateWithFormat:@"term BEGINSWITH %@", s];

并且得出最终结果应该只是一次内存查找:

searchResults = [searchResults filteredArrayUsingPredicate:
    [NSPredicate predicateWithFormat:@"ANY keywords IN %@", fetchedKeywords]];

【讨论】:

这仍然在 CoreData 的 SQL 查询中进行了两次连接。 想一想,也许您必须反过来进行查找。查看我的编辑。 第二个代码 sn-p 中的第一个“searchResults”数组来自哪里?如果全部都在内存中,我有数千行,所以我不知道它的效果如何。 此外,如果它需要为过滤器的每次迭代引用多对多表,这可能比我当前的实现要慢:( Core Data 提供了“故障”机制,因此即使是大型对象数组也不会对内存造成太大压力。第一个searchResults 将是输入一个字母后的第一个结果,还有什么?在内存中过滤数组不应该是性能问题。 -- 顺便说一句,WWDC 2011 视频中描述了同样的模式。

以上是关于核心数据 ANY ANY 多重 INNER JOIN的主要内容,如果未能解决你的问题,请参考以下文章

保存到核心数据“ALL 或 ANY 运算符的左侧必须是 NSArray 或 NSSet

JOI:允许数组中的空值

JSON "Any" 不能转换为 'NSDictionary' 以及如何确定转换类型

使用WatchConnectivity获取核心数据获取请求

ALL 或 ANY 运算符的左侧必须是 NSArray 或 NSSet

核心数据测试关系的存在