Spring Data Elasticsearch通配符查询中'?'的异常行为排查
问题原因及解决办法
原因分析
Spring Data Elasticsearch 5.1.1的查询解析逻辑和Elasticsearch原生SQL存在核心差异:
- Elasticsearch原生SQL的
LIKE语句会直接映射为regexp查询,其中?严格匹配单个任意字符。 - 而Spring Data Elasticsearch的默认查询方式(方法名
Like查询、未指定类型的StringQuery、常规@Query注解),要么会自动给查询参数前后添加通配符(如%xxx%转成ES的*xxx*),要么误用了不支持单字符通配符的查询类型(如match),导致你传入的?被错误解析为匹配任意长度字符的*。另外,如果在@Query中未明确指定使用ES SQL或wildcard查询,Spring Data的查询解析器也会干扰通配符的语义。
解决办法
1. 用NativeSearchQueryBuilder构建精确的wildcard查询
直接通过ES的WildcardQueryBuilder指定单字符通配符?,完全绕过Spring Data的自动解析:
import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.WildcardQueryBuilder; import java.util.List; import java.util.stream.Collectors; // 注入ElasticsearchOperations实例 private final ElasticsearchOperations elasticsearchOperations; public List<User> getUsersWithSingleCharMatch(String pattern) { WildcardQueryBuilder wildcardQuery = QueryBuilders.wildcardQuery("username", pattern); NativeSearchQuery query = new NativeSearchQueryBuilder() .withQuery(wildcardQuery) .build(); return elasticsearchOperations.search(query, User.class).stream() .map(SearchHit::getContent) .collect(Collectors.toList()); }
调用时传入"a?c",ES会严格匹配a开头、c结尾且中间只有一个字符的username。
2. 在@Query中直接使用DSL的wildcard查询
通过@Query编写ES DSL,明确指定wildcard查询,避免Spring Data的解析干扰:
import org.springframework.data.elasticsearch.annotations.Query; import org.springframework.data.repository.Repository; import java.util.List; public interface UserRepository extends Repository<User, String> { @Query("{\"wildcard\": {\"username\": \"?0\"}}") List<User> findByUsernameSingleCharMatch(String pattern); }
传入"a?c"即可得到符合单字符匹配规则的结果。
3. 在@Query中使用Elasticsearch SQL语法
如果习惯用SQL的LIKE语法,需在@Query中指定使用ES SQL,并开启nativeQuery=true,这样会直接调用ES的SQL接口,和你手动执行SQL的行为完全一致:
public interface UserRepository extends Repository<User, String> { @Query(value = "sql SELECT * FROM users WHERE username LIKE ?0", nativeQuery = true) List<User> findByUsernameLikeSql(String pattern); }
4. 修正方法名查询的使用方式
Spring Data的findByUsernameLike方法默认会给参数前后自动添加%(对应ES的*),如果你不需要前后通配符,应该改用findByUsernameStartingWith/findByUsernameEndingWith,或者手动传入包含?的精确模式,同时确保方法名对应的查询类型是wildcard而非其他:
// 手动传入带?的模式,避免自动添加前后通配符 List<User> findByUsername(String pattern);
调用时传入"a?c",可通过Spring Data的日志验证生成的DSL是否为wildcard查询。
内容的提问来源于stack exchange,提问作者gcbox999
相关产品推荐
相关产品推荐

