2010-01-07 92 views
0

給定文件{「富」,「酒吧」,「巴茲」},我想用SpanNearQuery與標記{「巴茲」,「額外」}Lucene的SpanNearQuery部分匹配

但是,這不能匹配。

我該如何解決這個問題?

樣品測試(使用Lucene 2.9.1),結果如下:

  • givenSingleMatch - PASS
  • givenTwoMatches - PASS
  • givenThreeMatches - PASS
  • givenSingleMatch_andExtraTerm - FAIL

...

import org.apache.lucene.analysis.standard.StandardAnalyzer; 
import org.apache.lucene.document.Document; 
import org.apache.lucene.document.Field; 
import org.apache.lucene.index.IndexReader; 
import org.apache.lucene.index.IndexWriter; 
import org.apache.lucene.index.Term; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.TopDocs; 
import org.apache.lucene.search.spans.SpanNearQuery; 
import org.apache.lucene.search.spans.SpanQuery; 
import org.apache.lucene.search.spans.SpanTermQuery; 
import org.apache.lucene.store.RAMDirectory; 
import org.apache.lucene.util.Version; 
import org.junit.After; 
import org.junit.Assert; 
import org.junit.Before; 
import org.junit.Test; 

import java.io.IOException; 

public class SpanNearQueryTest { 

    private RAMDirectory directory = null; 

    private static final String BAZ = "baz"; 
    private static final String BAR = "bar"; 
    private static final String FOO = "foo"; 
    private static final String TERM_FIELD = "text"; 

    @Before 
    public void given() throws IOException { 
     directory = new RAMDirectory(); 
     IndexWriter writer = new IndexWriter(
       directory, 
       new StandardAnalyzer(Version.LUCENE_29), 
       IndexWriter.MaxFieldLength.UNLIMITED); 

     Document doc = new Document(); 
     doc.add(new Field(TERM_FIELD, FOO, Field.Store.NO, Field.Index.ANALYZED)); 
     doc.add(new Field(TERM_FIELD, BAR, Field.Store.NO, Field.Index.ANALYZED)); 
     doc.add(new Field(TERM_FIELD, BAZ, Field.Store.NO, Field.Index.ANALYZED)); 

     writer.addDocument(doc); 
     writer.commit(); 
     writer.optimize(); 
     writer.close(); 
    } 

    @After 
    public void cleanup() { 
     directory.close(); 
    } 

    @Test 
    public void givenSingleMatch() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, FOO)) 
       }, Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 

    @Test 
    public void givenTwoMatches() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, FOO)), 
         new SpanTermQuery(new Term(TERM_FIELD, BAR)) 
       }, Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 

    @Test 
    public void givenThreeMatches() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, FOO)), 
         new SpanTermQuery(new Term(TERM_FIELD, BAR)), 
         new SpanTermQuery(new Term(TERM_FIELD, BAZ)) 
       }, Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 

    @Test 
    public void givenSingleMatch_andExtraTerm() throws IOException { 

     SpanNearQuery spanNearQuery = new SpanNearQuery(
       new SpanQuery[] { 
         new SpanTermQuery(new Term(TERM_FIELD, BAZ)), 
         new SpanTermQuery(new Term(TERM_FIELD, "EXTRA")) 
       }, 
       Integer.MAX_VALUE, false); 

     TopDocs topDocs = new IndexSearcher(IndexReader.open(directory)).search(spanNearQuery, 100); 

     Assert.assertEquals("Should have made a match.", 1, topDocs.scoreDocs.length); 
    } 
} 
+0

注意:所有令牌都在單個字段中。感謝丹本指出缺少的信息。 – 2010-01-07 22:13:01

回答

5

SpanNearQuery可讓您找到彼此之間的距離在一定範圍內的術語。

例(從http://www.lucidimagination.com/blog/2009/07/18/the-spanquery/):

說,我們要內道格的5個 位置找到Lucene的,具有以下 Lucene的(爲了事項)道格 - 您可以使用 以下SpanQuery:

new SpanNearQuery(new SpanQuery[] { 
    new SpanTermQuery(new Term(FIELD, "lucene")), 
    new SpanTermQuery(new Term(FIELD, "doug"))}, 
    5, 
    true); 

alt text http://www.lucidimagination.com/blog/wp-content/uploads/2009/07/spanquery-dia1.png

在這個示例文本,Lucene是內 3道格

,但對你的榜樣,我能看到的唯一的比賽是你的查詢和目標文件都有「CD」(我想提出假設所有這些術語都在單個字段中)。在這種情況下,您不需要使用任何特殊的查詢類型。使用標準機制,您將獲得一些非零權重,這是基於它們在同一個字段中包含相同術語的事實。

編輯3 - 響應最新的評論,答案是,你不能使用SpanNearQuery做比其預定的,這是找出是否一個文檔中的多個條款以外的任何內一定會出現彼此的地點數目。我無法確定您的具體用例/預期結果(隨意發佈),但在最後一種情況下,如果您只想知道是否存在(「BAZ」,「EXTRA」)中的一個或多個該文件,BooleanQuery將工作得很好。

編輯4 - 現在你已經發布了你的用例,我明白你想要做什麼。以下是您可以這樣做的方法:使用上述的BooleanQuery組合您想要的個人詞彙以及SpanNearQuery,並在SpanNearQuery上設置一個提升。

因此,以文本形式查詢看起來像:

BAZ OR EXTRA OR "BAZ EXTRA"~100^5 

(作爲一個例子 - 這將匹配包含任何「BAZ」或「EXTRA」的所有文檔,而是一個更高的分數分配給文件,其中術語「BAZ」和「EXTRA」出現在彼此的100個位置之內;根據你的喜好調整位置並提升,這個例子來自Solr食譜,因此它可能不會在Lucene中解析,或者可能導致不希望的結果。在下一節中,我將向您展示如何使用API​​來構建它)。

以編程方式,您將構造如下:

Query top = new BooleanQuery(); 

// Construct the terms since they will be used more than once 
Term bazTerm = new Term("Field", "BAZ"); 
Term extraTerm = new Term("Field", "EXTRA"); 

// Add each term as "should" since we want a partial match 
top.add(new TermQuery(bazTerm), BooleanClause.Occur.SHOULD); 
top.add(new TermQuery(extraTerm), BooleanClause.Occur.SHOULD); 

// Construct the SpanNearQuery, with slop 100 - a document will get a boost only 
// if BAZ and EXTRA occur within 100 places of each other. The final parameter means 
// that BAZ must occur before EXTRA. 
SpanNearQuery spanQuery = new SpanNearQuery(
           new SpanQuery[] { new SpanTermQuery(bazTerm), 
               new SpanTermQuery(extraTerm) }, 
           100, true); 

// Give it a boost of 5 since it is more important that the words are together 
spanQuery.setBoost(5f); 

// Add it as "should" since we want a match even when we don't have proximity 
top.add(spanQuery, BooleanClause.Occur.SHOULD); 

希望有幫助!在未來,試着首先發布你期望得到的結果 - 即使對你來說很明顯,它可能不會給讀者,並且明確地說可以避免必須來回多次。

+0

解釋距離的在線圖像是一個很好的接觸。 – Brian 2010-01-07 20:56:57

+0

這就是我最初的想法。但是,相關文檔不會從我的搜索中返回。 – 2010-01-07 22:11:37

+0

也許你可以發佈一些代碼來展示你如何搜索? – danben 2010-01-07 22:40:13