Hibernate Search: How to use wildcards correctly?

橙三吉。 提交于 2019-12-28 03:12:28

问题


I have the following query to search patients by full name, for an specific medical center:

MustJunction mj = qb.bool().must(qb.keyword()
    .onField("medicalCenter.id")
    .matching(medicalCenter.getId())
    .createQuery());
for(String term: terms)
    if(!term.equals(""))
       mj.must(qb.keyword()
       .onField("fullName")
       .matching(term+"*")
       .createQuery());

And it is working perfectly, but only if the user types the full firstname and/or lastname of the patient.

However I would like to make if work even if the user types a part of the firstname or lastname.

For example, if there's a patient called "Bilbo Baggins" I would like the search to find him, when the user types "Bilbo Baggins, "Bilbo, "Baggins", or even if he only types "Bil" or "Bag"

To achieve this I modified the above query as follows:

MustJunction mj = qb.bool().must(qb.keyword()
    .onField("medicalCenter.id")
    .matching(medicalCenter.getId())
    .createQuery());
for(String term: terms)
    if(!term.equals(""))
       mj.must(qb.keyword()
       .wildcard()
       .onField("fullName")
       .matching(term+"*")
       .createQuery());

Note how I added the wildcard() function before the call to onField()

However, this breaks the search and returns no results. What am I doing wrong?


回答1:


Short answer: don't use wildcard queries, use a custom analyzer with an EdgeNGramAnalyzerFactory. Also, don't try to analyze the query yourself (that's what you did by splitting the query into terms): Lucene will do it much better (with a WhitespaceTokenizerFactory, an ASCIIFoldingFilterFactory and a LowercaseFilterFactory in particular).

Long answer:

Wildcard queries are useful as quick and easy solutions to one-time problems, but they are not very flexible and reach their limits quite quickly. In particular, as @femtoRgon mentioned, these queries are not analyzed, so an uppercase query won't match a lowercase name, for instance.

The classic solution to most problems in the Lucene world is to use specially-crafted analyzers at index time and query time (not necessarily the same). In your case, you will want to use this kind of analyzer when indexing:

@AnalyzerDef(name = "edgeNgram",
    tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
    filters = {
            @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class), // Replace accented characeters by their simpler counterpart (è => e, etc.)
            @TokenFilterDef(factory = LowerCaseFilterFactory.class), // Lowercase all characters
            @TokenFilterDef(
                    factory = EdgeNGramFilterFactory.class, // Generate prefix tokens
                    params = {
                            @Parameter(name = "minGramSize", value = "1"),
                            @Parameter(name = "maxGramSize", value = "10")
                    }
            )
    })

And this kind when querying:

@AnalyzerDef(name = "edgeNGram_query",
    tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
    filters = {
            @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class), // Replace accented characeters by their simpler counterpart (è => e, etc.)
            @TokenFilterDef(factory = LowerCaseFilterFactory.class) // Lowercase all characters
    })

The index analyzer will transform "Mauricio Ubilla Carvajal" to this list of tokens:

  • m
  • ma
  • mau
  • maur
  • mauri
  • mauric
  • maurici
  • mauricio
  • u
  • ub
  • ...
  • ubilla
  • c
  • ca
  • ...
  • carvajal

And the query analyzer will turn the query "mau UB" into ["mau", "ub"], which will match the indexed name (both tokens are present in the index).

Note that you'll obviously have to assign the analyzer to the field. For the indexing part, it's done using the @Analyzer annotation. For the query part, you'll have to use overridesForField on the query builder as shown here



来源:https://stackoverflow.com/questions/46899947/hibernate-search-how-to-use-wildcards-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!