2017-03-27 55 views
1

我的Hibernate Search分析器配置有一些問題。 我的一個索引實體(「醫院」)有一個字符串字段(「名稱」),可能包含長度爲1-40的值。我希望能夠通過只搜索一個字符來找到一個實體(因爲有可能醫院有單個字符名稱)。Hibernate搜索| ngram分析器與minGramSize 1

@Indexed(index = "HospitalIndex") 
@AnalyzerDef(name = "ngram", 
     tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class), 
     filters = { 
       @TokenFilterDef(factory = StandardFilterFactory.class), 
       @TokenFilterDef(factory = LowerCaseFilterFactory.class), 
       @TokenFilterDef(factory = NGramFilterFactory.class, 
         params = { 
           @Parameter(name = "minGramSize", value = "1"), 
           @Parameter(name = "maxGramSize", value = "40")}) 
     }) 
public class Hospital { 

     @Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO, analyzer = @Analyzer(definition = "ngram")) 
     private String name = ""; 
} 

如果我添加名爲「我的測試醫院」醫院Lucene索引看起來是這樣的:

1 name al 
1 name e 
1 name es 
1 name est 
1 name h 
1 name ho 
1 name hos 
1 name hosp 
1 name hospi 
1 name hospit 
1 name hospita 
1 name hospital 
1 name i 
1 name it 
1 name ita 
1 name ital 
1 name l 
1 name m 
1 name my 
1 name o 
1 name os 
1 name osp 
1 name ospi 
1 name ospit 
1 name ospita 
1 name ospital 
1 name p 
1 name pi 
1 name pit 
1 name pita 
1 name pital 
1 name s 
1 name sp 
1 name spi 
1 name spit 
1 name spita 
1 name spital 
1 name st 
1 name t 
1 name ta 
1 name tal 
1 name te 
1 name tes 
1 name test 
1 name y 
1 name a 

這是我如何建立和執行我的搜索查詢:

QueryBuilder hospitalQb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Hospital.class).get(); 
Query hospitalQuery = hospitalQb.keyword().onFields("name")().matching(searchString).createQuery(); 
javax.persistence.Query persistenceQuery = fullTextEntityManager.createFullTextQuery(hospitalQuery, Hospital.class); 
List<Hospital> results = persistenceQuery.getResultList(); 

問題是,同樣的ngram分析器也用於我的搜索查詢。因此,當我搜索「醫院」的例子時,我會找到名稱中包含「a」字符的所有醫院。 這是搜索查詢的外觀喜歡,當我打電話就可以了toString方法:

name:h name:ho name:hos name:hosp name:hospi name:hospit name:hospita name:hospital name:o name:os name:osp name:ospi name:ospit name:ospita name:ospital name:s name:sp name:spi name:spit name:spita name:spital name:p name:pi name:pit name:pita name:pital name:i name:it name:ita name:ital name:t name:ta name:tal name:a name:al name:l 

所以現在的問題是,沒有任何人知道一個更好的分析器配置或另一種方式構建解決問題的搜索查詢?

+1

Yoann的回答是正確的添加一些建議:不要使用這麼大的'maxGramSize':對於大多數用例來說,選擇3或4.您也可能想要使用多個@Field批註對同一個字段編制索引:給每個不同的名稱和一個不同的分析器,然後當你查詢它時,你將執行一個針對兩個字段的布爾查詢,每個字段都有其正確的分析器。 – Sanne

回答

4

您可以設置第二個分析儀,但它不具有NGRAM過濾器,然後重寫用於查詢分析器相同:

QueryBuilder hospitalQb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Hospital.class) 
    .overridesForField("name", "my_analyzer_without_ngrams") 
    .get(); 
// Then it's business as usual 

此外,如果要實現某種自動完成(foo*),而不是在字搜索(*foo*),您可能需要使用EdgeNGramFilterFactory而不是:它只會生成作爲索引令牌的前綴的ngrams。

+0

感謝您的幫助。這幾乎解決了這個問題,但有沒有可能爲所有字段覆蓋分析器?我有許多嵌入式索引實體具有相同的問題。所以我必須全部覆蓋它們(.overridesForField(「careUnits.name」....)也許有可能以編程方式加載「my_analyzer_without_ngrams」的實例並使用此實例構建搜索查詢? – Andre

+0

@Andre您可以嗎提供您正在使用的實際代碼?我只在您的原始問題中看到一個字段,所以我沒有看到問題是什麼,並且根據問題的性質,解決方案可能會有所不同。字段?多個查詢,每個都針對單個字段?其他? –

+0

在原始問題中,我試圖打破複雜性,在實際實現中我有更多的索引實體,這就是我在當下實現搜索的方式: [PastBin](https://pastebin.com/itx1Nh9E)。它以這種方式工作,但在我看來,所有字段的所有手動覆蓋都有點髒,所以如果你知道一個更好的方法來解決這個問題,我會很高興你的解決方案感謝您的幫助和時間。 – Andre