Поиск в спящем режиме: поиск по любой части поля без потери содержимого поля при индексации - PullRequest
0 голосов
/ 10 мая 2019

Я хотел бы иметь возможность найти объект на основе любой части его проиндексированных полей, а поля не должны терять содержимое при индексации.

Допустим, у меня есть следующий пример класса сущности:

@Entity
public class E {
    private String f;
    // ...
}

И если значение f в одной сущности равно "This is a nice field!", я бы хотел найти его по любому из следующих запросов:

  • "это"
  • "а"
  • "IC"
  • "!"
  • «Это хорошее поле!»

Самое очевидное решение - аннотировать сущность следующим образом:

@Entity
@Indexed
@AnalyzerDef(name = "a",
        tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
        filters = @TokenFilterDef(factory = LowerCaseFilterFactory.class)
)
@Analyzer(definition = "a")
public class E {
    @Field
    private String f;
    // ...
}

А затем выполните поиск следующим образом:

String queryString;
// ...
org.apache.lucene.search.Query query = queryBuilder
        .keyword()
        .wildcard()
        .onField("f")
        .matching("*" + queryString.toLowerCase() + "*")
        .createQuery();

Но в документации указано, что в целях производительности рекомендуется, чтобы запрос также не начинался с? или *.

Так что, как я понимаю, этот метод неэффективен.

Другая идея состоит в том, чтобы использовать n-грамм, как это:

@Entity
@Indexed
@AnalyzerDef(name = "a",
        tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
        filters = {
                @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                @TokenFilterDef(factory = NGramFilterFactory.class,
                        params = {
                                @Parameter(name = "minGramSize", value = "1"),
                                @Parameter(name = "maxGramSize", value = E.MAX_LENGTH)
                        })
        }
)
@Analyzer(definition = "a")
public class E {
    static final String MAX_LENGTH = "42";
    @Field
    private String f;
    // ...
}

И создавать запросы следующим образом:

String queryString;
// ...
org.apache.lucene.search.Query query = queryBuilder
                .keyword()
                .onField("f")
                .ignoreAnalyzer()
                .matching(queryString.toLowerCase())
                .createQuery();

На этот раз не используются подстановочные запросы, и анализатор в запросе игнорируется. Я не уверен, является ли игнорирование анализатора хорошим или плохим, но он работает с игнорируемым анализатором.

Другим возможным решением было бы использовать WhitespaceTokenizerFactory вместо KeywordTokenizerFactory при использовании n-грамм, затем разделить queryString пробелами и объединить поиск для каждой подстроки, используя MUST . В этом подходе, как я понимаю, я получу намного меньше построенных n-грамм, если длина строки, содержащейся в f, равна E.MAX_LENGTH, что должно быть хорошо для производительности. И я также смогу найти ранее описанную сущность, например, по запросу "hi ield" . И это было бы идеально.

Так, как лучше всего решить мою проблему? Или все мои идеи плохи?

P.S. Следует ли игнорировать анализатор в запросах при использовании n-грамм?

1 Ответ

1 голос
/ 13 мая 2019

Другим возможным решением было бы использование WhitespaceTokenizerFactory вместо KeywordTokenizerFactory при использовании n-грамм, затем разделить queryString на пробелы и объединить поиски для каждой подстроки, используя MUST.В этом подходе, как я понимаю, я получу намного меньше построенных n-грамм, если длина строки, содержащейся в f, равна E.MAX_LENGTH, что должно быть хорошо для производительности.И я также смогу найти ранее описанную сущность, например, с помощью запроса «hi ield».И это было бы идеально.

Это более или менее идеальное решение, за исключением одного: вы не должны игнорировать анализатор при запросах.Вам нужно определить другой анализатор без фильтра ngram, но с токенайзером, фильтром строчных букв и т. Д., И явно указать Hibernate Search на использование этого анализатора во время запроса.

Другие решения слишком дороги, либо в операциях ввода-вывода и ЦП во время запроса (первое решение), либо в пространстве хранения (второе решение).Обратите внимание, что это третье решение может все еще быть довольно дорогим в пространстве для хранения, в зависимости от значения E.MAX_LENGTH.Обычно рекомендуется, чтобы разница между minGramSize и maxGramSize составляла только одну или две, чтобы избежать индексации слишком большого количества граммов.

Просто определите другой анализатор, назовите его как-то вроде "ngram_query" икогда вам нужно построить запрос, создайте построитель запросов следующим образом:

    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(EPCAsset.class)
        .overridesForField( "f" /* name of the field */, "ngram_query" )
        .get();

Затем создайте свой запрос как обычно.

Обратите внимание, что если вы используете Hibernate Search, чтобы нажать индексСхема и анализаторы для Elasticsearch, вам придется использовать хак для того, чтобы анализатор только для запросов выдвигался: по умолчанию передаются только те анализаторы, которые фактически используются во время индексации.См https://discourse.hibernate.org/t/cannot-find-the-overridden-analyzer-when-using-overridesforfield/1043/4

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...