2012-01-30 191 views
5

我試圖使用Apache Lucene製作可搜索的電話/本地商業目錄。Lucene:多詞詞組作爲搜索詞

我有街道名稱,企業​​名稱,電話號碼等字段。我遇到的問題是,當我嘗試通過街道搜索街道名稱有多個詞(如'新月'),沒有結果被返回。但是,如果我只用一個詞搜索,例如'新月',我就可以得到我想要的所有結果。

我用下面的索引數據:

String LocationOfDirectory = "C:\\dir\\index"; 

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_34); 
Directory Index = new SimpleFSDirectory(LocationOfDirectory); 

IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE.34, analyzer); 
IndexWriter w = new IndexWriter(index, config); 


Document doc = new Document(); 
doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Analyzed); 

w.add(doc); 
w.close(); 

我搜索這樣的工作:

int numberOfHits = 200; 
String LocationOfDirectory = "C:\\dir\\index"; 
TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true); 
Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory)); 
IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory); 

WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent"); 

searcher.search(q, collector); 
ScoreDoc[] hits = collector.topDocs().scoreDocs; 

我試圖交換通配符查詢短語查詢,先用整字符串,然後將字符串分割到空白區域,然後用下面的BooleanQuery包裝它們:

String term = "the crescent"; 
BooleanQuery b = new BooleanQuery(); 
PhraseQuery p = new PhraseQuery(); 
String[] tokens = term.split(" "); 
for(int i = 0 ; i < tokens.length ; ++i) 
{ 
    p.add(new Term("Street", tokens[i])); 
} 
b.add(p, BooleanClause.Occur.MUST); 

但是,這沒有奏效。我嘗試使用KeywordAnalyzer而不是StandardAnalyzer,但其他所有類型的搜索都停止了。我嘗試用其他字符(+和@)替換空格,並將查詢轉換爲和從此表單中轉換,但這仍然無效。我認爲它不起作用,因爲+和@是沒有索引的特殊字符,但我似乎無法找到任何字符的列表。

我開始有點生氣了,有人知道我在做什麼錯嗎?

感謝, 裏克

+0

特殊字符可以在這裏找到:http://lucene.apache.org/core/3_5_0/queryparsersynta x.html#N10180。 – Oliver 2016-05-27 10:53:46

回答

5

我發現我嘗試生成一個查詢,而無需使用的QueryParser是行不通的,所以我不再試圖創建自己的查詢和使用的QueryParser來代替。所有這一切我在網上看到了recomendations的表現,你應該在你的索引過程中使用的QueryParser使用相同的分析,所以我用一個StandardAnalyzer打造的QueryParser。

這適用於本示例,因爲StandardAnalyzer在索引過程中從街道「新月」中刪除單詞「the」,因此我們無法搜索它,因爲它不在索引中。

但是,如果我們選擇搜索「Grove Road」,那麼我們在開箱即用功能方面存在問題,即查詢將返回包含「Grove」或「Road」的所有結果」。這很容易通過設置QueryParser來解決,因此它的默認操作是AND而不是OR。

最後,正確的解決方案是以下幾點:

int numberOfHits = 200; 
String LocationOfDirectory = "C:\\dir\\index"; 
TopScoreDocCollector collector = TopScoreDocCollector.create(numberOfHits, true); 
Directory directory = new SimpleFSDirectory(new File(LocationOfDirectory)); 
IndexSearcher searcher = new IndexSearcher(IndexReader.open(directory); 

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_35); 

//WildcardQuery q = new WildcardQuery(new Term("Street", "the crescent"); 
QueryParser qp = new QueryParser(Version.LUCENE_35, "Street", analyzer); 
qp.setDefaultOperator(QueryParser.Operator.AND); 

Query q = qp.parse("grove road"); 

searcher.search(q, collector); 
ScoreDoc[] hits = collector.topDocs().scoreDocs; 
+1

從街道名稱中刪除停用詞是不正確的。想想像[Both Street]這樣的名字(http://g.co/maps/r5rnc)。我相信你可以找到更生動的例子。爲什麼要刪除一些沒有意義的東西? – 2012-02-01 12:02:42

11

你之所以沒有得到你的文件後面是建立索引時您正在使用StandardAnalyzer,該令牌轉換爲小寫並移除停止詞。因此,爲您的示例獲取索引的唯一術語是「新月」。但是,通配符查詢不會被分析,因此'the'被包含爲查詢的必需部分。在你的場景中,詞組查詢也是一樣的。

KeywordAnalyzer可能不太適合您的用例,因爲它將整個字段內容視爲單個標記。您可以使用SimpleAnalyzer作爲街道字段 - 它將拆分所有非字母字符的輸入,然後將其轉換爲小寫字母。您也可以考慮使用WhitespaceAnalyzerLowerCaseFilter。您需要嘗試不同的選項並找出最適合您的數據和用戶的選項。

此外,您可以如果需要變更分析儀,用於該領域的突破其他搜索每場使用不同的分析儀(例如用PerFieldAnalyzerWrapper)。

0

如果你想要一個確切的話街道匹配,你可以設置字段「街」 NOT_ANALYZED,不會過濾停止單詞「the」。

doc.add(new Field("Street", "the crescent", Field.Store.YES, Field.Index.Not_Analyzed); 
+1

這不是一個好的解決方案 - 這樣,您需要在查詢中始終包含'the'以獲得此結果。 – 2012-02-01 12:05:37

+0

@Artur Nowak:投票你的答案。一個合適的分析儀是關鍵。 – 2012-02-02 02:18:58

0

無需使用任何Analyzer這裏的堂妹Hibernate的隱式使用StandardAnalyzer這將拆分基於white spaces所以這裏的解決方案被設置的話在AnalyzeNO它會自動執行Multi Phrase Search

@Column(name="skill") 
    @Field(index=Index.YES, analyze=Analyze.NO, store=Store.NO) 
    @Analyzer(definition="SkillsAnalyzer") 
    private String skill;