2009-11-21 114 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