我使用 spring-data-elasticsearch 5.1.1 创建了一个 Spring Boot 应用程序。当我在数据库上执行查询时,我得到了正确的结果,但使用 API 却没有,我不明白为什么。
我有一个 Elasticsearch 数据库,其索引“用户”配置如下:
{
"mappings": {
"properties": {
"id": {
"type": "long"
},
"country": {
"type": "keyword"
},
"firstname": {
"type": "keyword"
},
"lastname": {
"type": "keyword"
}
}
}
当我执行查询时:GET http://localhost:9200/_sql?
{
"query": "SELECT * FROM users WHERE firstname LIKE 'Micka_l' and country = 'FR' LIMIT 100"
}
我找到了用户 Mickael。
当我修改查询时:
{
"query": "SELECT * FROM users WHERE firstname LIKE 'Mick_l' and country = 'FR' LIMIT 100"
}
我没有得到任何结果,因为我删除了“Mick_l”中的一个字母。
通过执行查询:GET http://localhost:9200/_sql/translate,我检索了生成的查询以在我的 Spring Boot 服务中使用:
public List<UserDTO> searchUsers(SearchDTO searchDTO) {
String country = searchDTO.getCountry();
String firstname = searchDTO.getFirstname();
StringQuery queryString = new StringQuery("{\"bool\": {\"must\": [{\"term\": {\"country\": " +
"{\"value\": \"" + country + "\"}}},{\"wildcard\": {\"firstname\": {\"wildcard\": \"" + firstname + "\"," +
"\"boost\": 1.0}}}],\"boost\": 1.0}}");
SearchHits<User> searchHits = searchOperations.search(queryString, User.class);
List<User> userList = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
return userList.stream().map((user) -> UserMapper.MAPPER.mapToDTO(user)).collect(Collectors.toList());
}
}
此查询允许我按名字搜索用户,如下所示:
发布http://localhost:8080/api/user/search
{
"country": "FRA",
"firstname": "Micka_l"
}
返回:迈克尔
但是:
{
"country": "FRA",
"firstname": "Mick_l"
}
还返回:Mickael,而我需要搜索区分大小写。为什么会有“?”表现得像“*”?
我尝试了几种使用 JPA 的方法:
List<User> findByCountryAndFirstnameLike(country, firstname)
还有@Query:
@Query("{\"bool\": {\"must\": [{\"term\": {\"country\": \"{\"value\": \" ?0 \"}}},{\"wildcard\": {\"firstname\": {\"wildcard\": \" ?1 \","\"boost\": 1.0}}}],\"boost\": 1.0}}")
List<User> findByCountryAndFirstnameLike(country, firstname)
行为是一样的。
您测试过这种行为吗?
我尝试了 NativeSearchQueryBuilder() 的最后一种方法,但无法完成配置:
public List<UserDTO> searchUsers(SearchDTO searchDTO) {
String country = searchDTO.getCountry();
String firstname = searchDTO.getFirstname();
TermQuery termQuery = new TermQuery.Builder().field("country").value(country).build();
WildcardQuery wildcardQuery = new WildcardQuery.Builder().value(firstname).caseInsensitive(true).boost(1.0F).build();
// Doesn't Work:
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(new BoolQuery.Builder()
.must(t -> t.term(termQuery))
.must(w -> w.wildcard(wildcardQuery))
).build();
SearchHits<User> searchHits = searchOperations.search(searchQuery, User.class);
List<User> userList = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
return userList.stream().map((user) -> UserMapper.MAPPER.mapToDTO(user)).collect(Collectors.toList());
}
}
我不确定这种方法是否可以解决使用“?”时的意外行为。
首先,生成 Elasticsearch 查询的第一种方法是让您容易遭受代码注入攻击。我希望您不要在生产代码中做这样的事情。不管怎样,你的问题有很多——SQL、JQuery、Spring Data,它似乎无处不在。那么让我们从后端开始。在elasticsearch中,一切都按预期进行:
DELETE test
PUT test
{
"mappings": {
"properties": {
"id": {
"type": "long"
},
"country": {
"type": "keyword"
},
"firstname": {
"type": "keyword"
},
"lastname": {
"type": "keyword"
}
}
}
}
POST test/_bulk?refresh
{"index":{}}
{"firstname":"Mickael", "lastname": "Carreira", "country": "PT"}
{"index":{}}
{"firstname":"Mickael", "lastname": "Landreau", "country": "FR"}
// Returns one record
GET test/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"country": {
"value": "FR"
}
}
},
{
"wildcard": {
"firstname": {
"wildcard": "Micka?l",
"boost": 1
}
}
}
],
"boost": 1
}
}
}
// Returns no records
GET test/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"country": {
"value": "FR"
}
}
},
{
"wildcard": {
"firstname": {
"wildcard": "Mick?l",
"boost": 1
}
}
}
],
"boost": 1
}
}
}
现在让我们弄清楚您的代码生成了哪个查询以及哪个部分不起作用。
顺便说一句,
"boost": 1
没有做任何事情,所以我们可以将其简化为
// Returns one record
GET test/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"country": {
"value": "FR"
}
}
},
{
"wildcard": {
"firstname": {
"wildcard": "Micka?l"
}
}
}
]
}
}
}