在搜索文本中匹配的单词时,我可以优化核心数据查询吗? (这个问题也与iPhone上自定义SQL与核心数据的智慧有关。)
我正在开发一款新的(iPhone)应用程序,它是科学数据库的手持式参考工具。主界面是一个标准的可搜索表视图,我想要用户键入新单词时的类型响应。单词匹配必须是文本中单词的前缀。该文本由100,000个单词组成。
在我的原型中,我直接编写了SQL。我创建了一个单独的“单词”表,其中包含主实体文本字段中的每个单词。我索引了单词并按照
的顺序进行了搜索SELECT id, * FROM textTable
JOIN (SELECT DISTINCT textTableId FROM words
WHERE word BETWEEN 'foo' AND 'fooz' )
ON id=textTableId
LIMIT 50
运行速度非常快。使用IN可能也可以正常工作,即
SELECT * FROM textTable
WHERE id IN (SELECT textTableId FROM words
WHERE word BETWEEN 'foo' AND 'fooz' )
LIMIT 50
LIMIT至关重要,可让我快速显示结果。如果达到限制,我会通知用户显示的内容太多。这是kludgy。
我花了最近几天考虑转移到Core Data的优势,但我担心架构中缺乏控制,索引和查询重要查询。
理论上,textField MATCHES '.*\bfoo.*'
的NSPredicate会起作用,但我确信它会很慢。这种文本搜索似乎很常见,我想知道通常的攻击是什么?你会像我上面那样创建一个单词实体并使用“bEGINSWITH'foo'这个词的谓词吗?这会像我的原型一样快吗? Core Data会自动创建正确的索引吗?我找不到任何明确的方法来建议持久性存储关于索引。
我在iPhone应用程序中看到了Core Data的一些优点。故障和其他内存注意事项允许对tableview查询进行有效的数据库检索,而无需设置任意限制。对象图管理允许我轻松遍历实体而无需编写大量SQL。移植功能将来会很好。另一方面,在有限的资源环境(iPhone)中,我担心自动生成的数据库会因元数据,不必要的反向关系,低效的属性数据类型等而膨胀。
我应该潜入或谨慎行事吗?
答案 0 :(得分:10)
我做了一个解决方案。我认为它与this post类似。我将合并源代码添加到我的Core Data项目中,然后创建了一个不是托管对象子类的全文搜索类。在FTS类I #import "sqlite3.h"
(源文件)而不是sqlite框架。 FTS类保存到与Core Data持久存储区不同的.sqlite文件。
当我导入数据时,Core Data对象将相关FTS对象的rowid存储为整数属性。我有一个静态数据集,所以我不担心参照完整性,但保持完整性的代码应该是微不足道的。
要执行FTS,我MATCH
查询FTS类,返回一组rowid。在我的托管对象类中,我使用[NSPredicate predicateWithFormat:@"rowid IN %@", rowids]
查询相应的对象。我避免以这种方式遍历任何多对多的关系。
性能提升非常显着。我的数据集是142287行,包括194MB(核心数据)和92MB(删除了停用词的FTS)。根据搜索词频率,我的搜索频率从几秒到0.1秒不常用(<100次点击)和0.2秒(频繁的术语)(> 2000次点击)。
我确信我的方法存在无数问题(代码膨胀,可能存在命名空间冲突,丢失一些核心数据功能),但似乎有效。
答案 1 :(得分:3)
为了跟进这个问题,我发现使用Core Data查询是很慢的。我已经在这个问题上摸了好几个小时。
正如在我的问题中的SQL示例中,有两个实体:textTable和单词,其中单词包含每个单词,它被索引,并且textTable和单词之间存在多对多关系。我用4000个单词和360个textTable对象填充数据库。假设与单词对象的textTable关系称为searchWords,那么我可以在textTable实体上使用类似于
的谓词predicate = [NSPredicate predicateWithFormat:@"ANY searchWords.word BEGINSWITH %@", query];
(我可以为多个查询术语添加此谓词的连词。)
在iPhone上,此查询需要几秒钟。使用更大的测试集的手动编码SQL的响应是即时的。
但这甚至不是结束。 NSPredicate存在一些限制,使得相当简单的查询变得缓慢而复杂。例如,在上面的示例中想象您要使用范围按钮进行过滤。假设单词entity包含所有文本字段中的所有单词,但范围会将其限制为来自特定字段的单词。因此,单词可能具有“源”属性(例如,电子邮件的标题和消息正文)。
然后,自然地,全文将忽略源属性,如上例所示,但过滤后的查询会将搜索限制为特定的源值。这个看似简单的变化需要一个SUBQUERY。例如,这不起作用:
ANY searchWords.word BEGINSWITH "foo" AND ANY searchWords.source = 3
因为两个表达式的实体可能不同。相反,你必须做类似的事情:
SUBQUERY(searchWords, $x, $x.word BEGINSWITH "foo" AND $x.source = 3).@count > 0
我发现这些子查询可能不会令人惊讶地比使用“ANY”的谓词慢。
此时我非常好奇Cocoa程序员如何有效地使用Core Data进行全文搜索,因为我对谓词评估的速度和NSPredicates的可表达性感到沮丧。我碰到了墙。
答案 2 :(得分:2)
潜入。
这是一种方法:
NSFetchedResultsController
管理Word
实体上的结果集(与SQL相同的核心数据“字”表)UISearchDisplayController
实时在结果集上应用NSPredicate
通过NSFetchedResultsController
获得结果集后,应用谓词非常容易。根据我的经验,它也会有回应。例如:
if ([self.searchBar.text length]) {
_predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"(word contains[cd] '%@')", self.searchBar.text]];
[self.fetchedResultsController.fetchRequest setPredicate:_predicate];
}
NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
// handle error...
}
NSLog(@"filtered results: %@", [self.fetchedResultsController fetchedObjects]);
会动态过滤结果集[self.fetchedResultsController fetchedObjects]
,对word
进行不区分大小写的搜索。
答案 3 :(得分:2)
在遇到同样的问题之后,我遇到了一系列帖子,其中作者遇到了同样的问题,并提出了this solution。他报告说,从6到7秒的搜索时间到0.13到0.05秒之间有所改善。
他的FTS数据集是79个文件(文件大小175k,3600个离散标记,10000个参考文献)。我还没有尝试过他的解决方案,但我想我会尽快发布。另见他的帖子的Part 2有关问题的文档,以及Part 1他的数据集文档。