本节主要内容是使用ES客户端链接: Java High Level REST Client操作ES(推荐使用该客户端)。
<!-- ES 客户端 -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.9.1</version>
</dependency>
<!-- ES 版本 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.9.1</version>
</dependency>
spring:
elasticsearch:
rest:
uris: 127.0.0.1:9200
read-timeout: 60s
address
put http://localhost:9200/address/_mapping
{
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"address": {
"type": "text"
},
"location": {
"type": "keyword"
},
"type": {
"type": "keyword"
},
"geom": {
"type": "geo_shape"
},
"timestamp": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
PUT address/_docs/1000
{
"name": "spring开发",
"address":"北京市朝阳区和平街",
"type":"餐饮",
"location":"121.392496,31.245827",
"geom": {
"type": "point",
"coordinates": [121.392496,31.245827]
},
"timestamp":"2021-04-25 19:11:35"
}
// 搜索全部记录
@Test
public void testSearchAll() throws IOException, ParseException {
// 搜索请求对象
SearchRequest searchRequest = new SearchRequest("address");
// 指定类型
searchRequest.types("_doc");
// 搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 搜索方式
// matchAllQuery搜索全部
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
// 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
searchSourceBuilder.fetchSource(new String[]{"name","address","location","timestamp"},new String[]{});
// 向搜索请求对象中设置搜索源
searchRequest.source(searchSourceBuilder);
// 执行搜索,向ES发起http请求
SearchResponse searchResponse = client.search(searchRequest);
// 搜索结果
SearchHits hits = searchResponse.getHits();
// 匹配到的总记录数
long totalHits = hits.getTotalHits();
// 得到匹配度高的文档
SearchHit[] searchHits = hits.getHits();
// 日期格式化对象
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for(SearchHit hit:searchHits){
// 文档的主键
String id = hit.getId();
// 源文档内容
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
// 日期
Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
System.out.println(name);
}
}
Term Query为精确查询,在搜索时会整体匹配关键字,不再将关键字分词
DSL:
{
"query": {
"term": { // 查询的方式为 term 精确查询
"name": "spring" // 查询的字段为 name 关键字是 spring
}
}
}
java api:
// 搜索方式
// termQuery 精确查询
searchSourceBuilder.query(QueryBuilders.termQuery("name", "spring开发"));
MatchQuery 即全文检索,会对关键字进行分词后匹配词条。
query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用
逗号分隔也可以不用
operator:设置查询的结果取交集还是并集,并集用 or, 交集用 and
DSL:
{
"query": {
"match": {
"description": {
"query": "spring开发",
"operator": "or"
}
}
}
}
java api
// matchQuery全文检索
searchSourceBuilder.query(QueryBuilders.matchQuery("name", "Spring开发框架"));
MultiQuery可以通过 fields 属性来设置多个域联合查找。
DSL:
{
"query": {
"multi_match": {
"query": "Spring开发框架",
"minimum_should_match": "80%",
"fields": ["name", "description"]
}
}
}
java api:
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring开发框架", "name", "address").minimumShouldMatch("80%"));
注:“spring开发框架”会被分为三个词:spring、开发、框架
设置"minimum_should_match": "80%"表示,三个词在文档的匹配占比为80%,即3*0.8=2.4,向下取整得2,表示至少有两个词在文档中要匹配成功。
提升boost:
在多域联合查询的时候,可以通过 boost 来设置某个域在计算得分时候的比重,比重越高的域当他符合条件时计算的得分越高,相应的该记录也更靠前。通过在 fields 中给相应的字段用 ^权重倍数来实现。
DSL:
"fields": ["name^10", "address"]
java链式编程:
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring开发框架", "name", "address").field("name", 10)); // 设置 name 10倍权重
布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来,有三个可选的参数:
must: 文档必须匹配must所包括的查询条件,相当于 “AND”
should: 文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
must_not: 文档不能匹配must_not所包括的该查询条件,相当于“NOT”
DSL:
{
"query": {
"bool": { // 布尔查询
"must": [ // 查询条件 must 表示数组中的查询方式所规定的条件都必须满足
{
"multi_match": {
"query": "spring框架",
"minimum_should_match": "50%",
"fields": [
"name^10",
"description"
]
}
},
{
"term": {
"type": "餐饮"
}
}
]
}
}
}
java api:
// 首先构造多关键字查询条件
MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring开发框架", "name", "address").field("name", 10);
// 然后构造精确匹配查询条件
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("type", "201002");
// 组合两个条件,组合方式为 must 全满足
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(matchQueryBuilder);
boolQueryBuilder.must(termQueryBuilder);
// 将查询条件封装给查询对象
searchSourceBuilder.query(boolQueryBuilder);
对于es关键字或单词的查询我们可以借助QueryBuilders.wildcardQuery方法来操作,只需要指定es中对应的列和要查询的内容即可:
boolQueryBuilder.must(QueryBuilders.wildcardQuery("name", "spring"+"*"));
例如:
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100));
// 设置查询的起始位置,默认是0
sourceBuilder.from(0);
// 设置查询结果的页大小,默认是10
sourceBuilder.size(10);
定义过滤器查询,是在原本查询结果的基础上对数据进行筛选,因此省略了重新计算的分的步骤,效率更高。并且方便缓存。推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用,过滤器在布尔查询中使用。
boolQueryBuilder.filter(QueryBuilders.termQuery("type", "餐饮"));
SearchSourceBuilder允许增加一或多个排序参数SortBuilder,有四个具体实现FieldSortBuilder, ScoreSortBuilder, GeoDistanceSortBuilder 和 ScriptSortBuilder。
注:支持对 keyword、date、float 等类型添加排序,text类型的字段不允许排序。
// 默认排序。根据_score倒序
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
// 根据_id升序
sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.ASC));
searchBuilder.sort("type", SortOrder.DESC);
// 高亮查询
@Test
public void testHighLight() throws IOException, ParseException {
// 搜索请求对象
SearchRequest searchRequest = new SearchRequest("ysx_course");
// 指定类型
searchRequest.types("doc");
// 搜索源构建对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 搜索方式
// 首先构造多关键字查询条件
MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架", "name", "address").field("name", 10);
// 添加条件到布尔查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(matchQueryBuilder);
// 将查询条件封装给查询对象
searchSourceBuilder.query(boolQueryBuilder);
// ***********************
// 高亮查询
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags(""); // 高亮前缀
highlightBuilder.postTags(""); // 高亮后缀
highlightBuilder.fields().add(new HighlightBuilder.Field("name")); // 高亮字段
// 添加高亮查询条件到搜索源
searchSourceBuilder.highlighter(highlightBuilder);
// ***********************
// 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
searchSourceBuilder.fetchSource(new String[]{"name","address","timestamp"},new String[]{});
// 向搜索请求对象中设置搜索源
searchRequest.source(searchSourceBuilder);
// 执行搜索,向ES发起http请求
SearchResponse searchResponse = client.search(searchRequest);
// 搜索结果
SearchHits hits = searchResponse.getHits();
// 匹配到的总记录数
long totalHits = hits.getTotalHits();
// 得到匹配度高的文档
SearchHit[] searchHits = hits.getHits();
// 日期格式化对象
soutData(searchHits);
}
根据查询结果的数据结构来获取高亮的数据,替换原有的数据:
private void soutData(SearchHit[] searchHits) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (SearchHit hit : searchHits) {
// 文档的主键
String id = hit.getId();
// 源文档内容
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String name = (String) sourceAsMap.get("name");
// 获取高亮查询的内容。如果存在,则替换原来的name
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if( highlightFields != null ){
HighlightField nameField = highlightFields.get("name");
if(nameField!=null){
Text[] fragments = nameField.getFragments();
StringBuffer stringBuffer = new StringBuffer();
for (Text str : fragments) {
stringBuffer.append(str.string());
}
name = stringBuffer.toString();
}
}
String type= (String) sourceAsMap.get("type");
// 日期
Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp"));
System.out.println(name);
}
}