2015-05-08 61 views
2

我有一個全文搜索與hibernate搜索和lucene工作,因爲我可以在特定字段中成功搜索給定的模型實體。然而,我並不是一次搜索一種類型的實體,而是希望實現一種「通用」搜索,其中同時搜索不同的實體類型,將搜索短語與每種不同實體類型的適當字段進行匹配,然後根據搜索條件排列結果,而不考慮實體類型。Hibernate搜索/ Lucene - 使用單個查詢搜索並排名不相關的實體

因此,例如,讓我們說,我有不同的實體,Foo和Bar

@Entity 
@Indexed 
@AnalyzerDef(
    name="fulltext", 
    [email protected](factory=StandardTokenizerFactory.class), 
    filters={ 
    @TokenFilterDef(factory=LowerCaseFilterFactory.class), 
    @TokenFilterDef(factory=SnowballPorterFilterFactory.class, 
     params={@Parameter(name="language", value="English") }) 
    } 
) 
public class Foo { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Integer fooId; 

    @Column 
    @Field 
    @Analyzer(definition="fulltext") 
    private String fieldA; 

    ... 

@Entity 
@Indexed 
public class Bar { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Integer barId; 

    @Column 
    @Field 
    @Analyzer(definition="fulltext") 
    private String fieldB; 

    @Column 
    @Field 
    @Analyzer(definition="fulltext") 
    private String fieldC; 

    ... 

所以我想搜索 「一些文本」 和匹配了Foo.fieldA,和酒吧。 fieldB和/或Bar.fieldC

當前類型搜索我已經工作的是專用於特定實體,例如:

fullTextSession = Search.getFullTextSession(hibernateSession); 
Query query = fullTextSession.createFullTextQuery(
       fullTextSession 
        .getSearchFactory() 
        .buildQueryBuilder() 
        .forEntity(Foo.class) 
        .get() 
        .keyword() 
        .onFields("fieldA") 
        .matching("some text") 
        .createQuery(), 
       Foo.class); 
query.list() // gets ranked list of Foo entities matching "some text" 

顯然,上述Lucene的查詢是特定於富實體,甚至Foo.fieldA

那麼,是不是可以修改Lucene的查詢還包括酒吧結果,在Bar.fieldB匹配和Bar.fieldC字段?

我知道fullTextSession.createFullTextQuery(fulltextSession, Class...)方法也會接受Bar.class,來返回Bar結果,但是我不知道如何修改實際的查詢來在Bar實體上搜索一下。


另一種方式我想解決這個是做單獨的查詢,一個是富實體,一個酒吧的實體,然後合併兩個結果集,並通過「匹配相關性得分」排序他們 - 但我無法找到如何獲得此分數的結果,無論是!

編輯 上述方法將可能無法正常工作 - 原來,你就可以用預測的結果,但the docs狀態的得分,從單獨的查詢分數無法進行有意義的比較:

FullTextQuery.SCORE:返回查詢中的文檔分數。對於給定的查詢,得分可以方便地將一個結果與另一個結果進行比較,但在比較不同查詢的結果時無用。


道歉,如果我在這裏覆蓋良好的踐踏地面,但我一直在尋找,顯然是在錯誤的地方,幾個小時就這個問題和不能找到文檔中任何有幫助的,這是令人沮喪,因爲我認爲這是Lucene的一個相當常見的用例。

+0

通過文檔挖掘,我發現[這](http://lucene.apache.org/core/3_0_3/api/all/org/apache /lucene/search/Query.html#combine(org.apache.lucene.search.Query [])) - 你嘗試使用Query.combine()嗎?我不是Lucene的專家,所以如果這是愚蠢的,請忽略它。 – CptBartender

+0

@CptBartender我看了一下,看起來好像是可能用於OR兩個查詢的東西,但無論如何,我在[Lucene 4.10.x](https://lucene.apache.org/core/4_10_0/core /org/apache/lucene/search/Query.html?is-external=true),並且此方法不再存在! – davnicwil

回答

3

您可以編寫兩個查詢,並使用Occur.SHOULD通過BooleanQuery進行組合。然後使用createFullTextQuery(booleanQuery, Foo.class, Bar.class);來搜索這兩種實體。

+0

謝謝,我一直在尋找使用布爾查詢,並想知道是否解決問題的正確方法。我試了一下,它的工作原理! – davnicwil

0

受哈代的回答啓發,我用了一個帶有兩個子句的布爾查詢,它們都帶有一個Occur.SHOULD,它們有效地作爲OR。這會導致查詢所需的行爲。

下面的代碼:

... 

fullTextSession = Search.getFullTextSession(hibernateSession); 
String searchPhrase = "some text"; 

org.apache.lucene.search.Query fooQuery = 
    fullTextSession 
    .getSearchFactory() 
    .buildQueryBuilder() 
    .forEntity(Foo.class) 
    .get() 
    .keyword() 
    .onFields("fieldA") 
    .matching(searchPhrase) 
    .createQuery(); 

org.apache.lucene.search.Query barQuery = 
    fullTextSession 
    .getSearchFactory() 
    .buildQueryBuilder() 
    .forEntity(Bar.class) 
    .get() 
    .keyword() 
    .onFields("fieldB", "fieldC") 
    .matching(searchPhrase) 
    .createQuery(); 

BooleanQuery query = new BooleanQuery(); 
query.add(new BooleanClause(fooQuery, BooleanClause.Occur.SHOULD)); 
query.add(new BooleanClause(barQuery, BooleanClause.Occur.SHOULD)); 

Query hibernateQuery = 
     fullTextSession.createFullTextQuery(query, Foo.class, Bar.class); 

...