最近一段时间负责公司的ElasticSearch搜索部分,遇到了很多问题,由于在集成ES时,考虑数据持久化的简便,采用了Spring家族的spring-data-elasticsearch,它将原生的ES的API封装并提供了自己的一套接口,事实证明还是用ES官方API有权威,风险低!
废话不多说了,今天又踩了一个大坑,前台传来数据(包含空格)到ES,查询报错。如果使用原生的API是可以识别并默认转换空格以及特殊字符的,研究了一天,总结出了三种解决方案:
第一种:放弃spring-data-elasticsearch,脱胎换骨,换成原生的API或者bbos(bbos对ES的匹配支持非常高,推荐使用!)
第二种:在查询时不采用ElasticSearchRepository,换成ElasticSearchTemplate实现
第三种:在项目中将特殊字符过滤掉,将空格替换掉
因为公司的项目底层都采用的ElasticSearchRepository,改起来成本太高,所以放弃了第一种方式。
使用第二种方式改进:(需对dsl语法有所了解)
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;@Override
public List<ESProject> listESProjectsByIdAndName(List<String> ids, String name) {SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery().must(matchQuery("name", name)).should(termsQuery("id", ids))).build();List<ESProject> esProjectList = elasticsearchTemplate.queryForList(searchQuery, ESProject.class);return esProjectList;
}
这种方式底层直接操作官方API,可以自动识别并转换特殊字符和空格!
使用第三种方式改进:(推荐)
// escape the special char for nickName
String nickName = QueryParser.escape(nickName).trim().replace(" ","");
以上方式使用的是org.apache.lucene包下QueryParser类的escape方法,该方法用来针对一些特殊字符进行转义,我们深拔源码来看看!
/*** Returns a String where those characters that QueryParser* expects to be escaped are escaped by a preceding <code>\</code>.*/public static String escape(String s) {StringBuilder sb = new StringBuilder();for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);// These characters are part of the query syntax and must be escapedif (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'|| c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'|| c == '*' || c == '?' || c == '|' || c == '&' || c == '/') {sb.append('\\');}sb.append(c);}return sb.toString();}
可以看出它是将字符串按char级别的颗粒进行分割,loop所有的char,在特殊字符前添加转义符号,最终完成字符串的转义。
最后推荐大家对DSL(elasticsearch 查询语句)深入了解一下,在操作ES原生API会更容易!