2009-11-21 66 views
13

在文本中搜索匹配词时,我可以优化核心数据查询吗? (这个问题也适用于定制SQL与iPhone上的核心数据的智慧。)如何针对全文搜索优化核心数据查询

我正在研究一个新的(iPhone)应用程序,它是科学数据库的手持参考工具。主界面是一个标准的可搜索表格视图,当用户键入新单词时,我想让你的类型响应。单词匹配必须是文本中单词的前缀。文本由10万字组成。

在我的原型中,我直接编码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。

我已经花了最近几天思考转移到核心数据的优势,但我担心模式,索引和查询重要查询缺乏控制。

从理论上讲,textField MATCHES '.*\bfoo.*'的NSPredicate可以正常工作,但我相信它会很慢。这种文本搜索看起来很普遍,我不知道通常的攻击是什么?你会像我上面做的那样创建一个单词实体,并使用“单词BEGINSWITH'foo'”的谓词吗?它的工作速度与我的原型一样快吗?核心数据会自动创建正确的索引?我找不到任何明确的建议关于索引的持久存储的方法。

我在我的iPhone应用程序中看到了Core Data的一些不错的优点。错误和其他内存考虑允许为tableview查询进行有效的数据库检索,而不设置任意限制。对象图管理允许我轻松遍历实体而无需编写大量的SQL。将来移植功能将会很好。另一方面,在有限的资源环境(iPhone)中,我担心自动生成的数据库会因为元数据,不必要的反向关系,低效的属性数据类型等而臃肿。

我应该潜入还是谨慎行事? 。

回答

10

我提出了一个解决方法。我认为它与this post类似。我将合并源代码添加到我的Core Data项目中,然后创建了一个不是托管对象子类的全文搜索类。在FTS类I #import "sqlite3.h"(源文件)中代替sqlite框架。 FTS类保存到与Core Data持久存储不同的.sqlite文件。

当我导入我的数据时,核心数据对象将相关FTS对象的rowid存储为整数属性。我有一个静态数据集,所以我不担心引用完整性,但维护完整性的代码应该是微不足道的。

要执行FTS,我MATCH查询FTS类,返回一组rowid。在我的托管对象类中,我使用[NSPredicate predicateWithFormat:@"rowid IN %@", rowids]查询相应的对象。我避免以这种方式遍历任何多对多关系。

性能提高很戏剧性。我的数据集是142287行,包括194MB(核心数据)和92MB(移除了停用词的FTS)。根据搜索词频率的不同,我的搜索时间从几秒到0.1秒(< 100次),频繁搜索(> 2000次)为0.2秒。

我确定我的方法有很多问题(代码膨胀,可能的命名空间冲突,某些核心数据功能的丢失),但它似乎正在工作。

2

潜水在

这里是去了解的一种方式:

  1. 把你的记录到核心数据持久性存储
  2. 使用NSFetchedResultsController管理的结果对你Word实体集(核心数据等同于SQL“字”表
  3. 使用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不区分大小写的搜索。

+0

感谢您的回复。我现在正在编写命令行工具,以将初始sqlite数据加载到xcdatamodel兼容数据库中。涉及实质性劳动。我会报告我的经验。 – 2009-11-23 17:38:02

+0

为了跟进你的例子,我认为问题是一个提取请求不会在Word实体上,而是在textTable实体上。 (例如,假设textTable包含电子邮件信息,Word包含所有电子邮件字段中的所有单词)。我认为这会使问题显得复杂化,因为fetchResultsController必须保存通过谓词过滤的textTable实体 - 而这样的ANY或SUBQUERY谓词是慢。 也许有一种方法可以在“相反”的方向上做到这一点:通过启动w/Word匹配,遵循反向关系,以及解析textTable。嗯。 – 2009-12-11 06:27:33

+0

如果谓词的第一部分尽可能地减少了搜索空间,那么谓词的其余部分的整体执行速度会更快,其搜索空间更少。查看Core Data指南的性能部分:http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/ TP40003468 – 2009-12-11 06:47:24

3

为了跟进这个问题,我发现使用核心数据查询是很慢的。我在这个问题上摸了好几个小时。

正如在我的问题中的SQL示例中,有两个实体:textTable和单词包含每个单词,它被索引,并且textTable和单词之间存在多对多关系。我用4000个单词和360个textTable对象填充数据库。假设textTable关系被称为searchWords字对象,然后我可以看起来像

predicate = [NSPredicate predicateWithFormat:@"ANY searchWords.word BEGINSWITH %@", query]; 

的textTable实体使用谓词(我可以添加这个断言对于多个查询条件的连词。)

在iPhone上,这个查询需要几秒钟。对于使用更大测试集的手写SQL的响应是即时的。

但是,这甚至没有结束。 NSPredicate有一些限制,使得相当简单的查询变得缓慢和复杂。例如,想象在上面的例子中,你想使用范围按钮进行过滤。假设单词实体包含所有文本字段中的所有单词,但范围会将其限制为来自特定字段的单词。因此,单词可能具有“源”属性(例如电子邮件的标题和邮件正文)。

当然,如上例所示,全文将忽略源属性,但是过滤后的查询会将搜索限制为特定的源值。这个看似简单的变化需要一个SUBQUERY。例如,这不起作用:

ANY searchWords.word BEGINSWITH "foo" AND ANY searchWords.source = 3 

因为对于这两个表达式而言真实的实体可能不同。相反,你必须做一些事情,如:

SUBQUERY(searchWords, $x, $x.word BEGINSWITH "foo" AND $x.source = 3)[email protected] > 0 

我发现,这些子查询,也许并不令人惊讶,这比使用“ANY”谓词慢。

在这一点上,我很好奇Cocoa程序员如何有效地使用Core Data进行全文搜索,因为我对谓词评估的速度和NSPredicates的可表达性都感到气馁。我跑到了墙上。

+1

请考虑查看此处的性能部分:http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468 – 2009-12-11 08:04:03

+0

感谢那个链接。从那里我发现可执行参数“-com.apple.CoreData.SQLDebug 1”将发送sqlite调试到stderr。从那个转储我看到了查询。这个查询没有什么问题,但是因为<=>文字表格关系是多对多的,所以有一个关系表要加入。因此,查询必须连接3个表。当我删除反转时,查询现在在iPhone硬件上运行得更快!唉,新的模式在Word表中有外键,因此每个事件都会重复这个词本身和元数据。浪费空间。 – 2009-12-11 17:17:27

+0

您可能会加快速度,但Apple建议保持反向关系以保持数据完整性。 “您通常应该在两个方向上建模关系,并适当指定相反的关系。如果进行了更改,Core Data将使用此信息来确保对象图的一致性(请参阅”处理关系和对象图完整性“)。在这里看看更多的信息:http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html – 2009-12-16 22:02:26

2

经过同样的问题,我遇到了一系列的作者有同样的问题,并提出this solution的职位。他报告从6-7秒搜索时间到0.13到0.05秒之间的改善。

他的FTS数据集是79个文件(文件大小175k,3600离散标记,10000个参考)。我还没有尝试过他的解决方案,但认为我会尽快发布。另请参阅他的帖子的Part 2以获取有关该问题的文档,并参阅Part 1了解他的数据集文档。

+0

我用这个解决方案的问题是查询和关键字必须完全匹配。对于实时结果,您希望任何关键字前缀都与查询匹配。在这种情况下,不可能在谓词中使用对象而不是字符串。 – 2010-02-23 05:00:28

+0

试图自己实现这一点,并没有得到改善,可能是因为我正在使用包含[cd]。我放弃并开始使用sqlite3 fts。彼得,谢谢你的额外链接。我仅限于一个。 – jluckyiv 2010-03-16 05:49:05