Я хотел бы иметь возможность найти объект на основе любой части его проиндексированных полей, а поля не должны терять содержимое при индексации.
Допустим, у меня есть следующий пример класса сущности:
@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-грамм?