【微服务全家桶】-实用篇-4.2-ES-下篇
1 DSL查询语法
1.1 DSL Query基本语法
1.1.1 查询所有match_all
#查询所有
GET /hotel/_search
{
"query": {
"match_all": {}
}
}
1.1.2 全文检索查询
1.1.2.1 match查询
# match查询
GET /hotel/_search
{
"query": {
"match": {
"all": "外滩"
}
}
}
只有两个匹配的
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 4.9931827,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "60487",
"_score" : 4.9931827,
"_source" : {
"address" : "黄浦路199号",
"brand" : "君悦",
"business" : "外滩地区",
"city" : "上海",
"id" : 60487,
"location" : "31.245409, 121.492969",
"name" : "上海外滩茂悦大酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/2Swp2h1fdj9zCUKsk63BQvVgKLTo_w200_h200_c1_t0.jpg",
"price" : 689,
"score" : 44,
"starName" : "五星级"
}
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "432335",
"_score" : 4.2893023,
"_source" : {
"address" : "唐山路145号",
"brand" : "7天酒店",
"business" : "北外滩地区",
"city" : "上海",
"id" : 432335,
"location" : "31.252585, 121.498753",
"name" : "7天连锁酒店(上海北外滩国际客运中心地铁站店)",
"pic" : "https://m2.tuniucdn.com/filebroker/cdn/res/c1/ba/c1baf64418437c56617f89840c6411ef_w200_h200_c1_t0.jpg",
"price" : 249,
"score" : 35,
"starName" : "二钻"
}
}
]
}
}
扩大到”外滩如家“
# match查询
GET /hotel/_search
{
"query": {
"match": {
"all": "外滩"
}
}
}
先是准确匹配外滩如家,再是外滩然后如家
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 32,
"relation" : "eq"
},
"max_score" : 4.9931827,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "60487",
"_score" : 4.9931827,
"_source" : {
"address" : "黄浦路199号",
"brand" : "君悦",
"business" : "外滩地区",
"city" : "上海",
"id" : 60487,
"location" : "31.245409, 121.492969",
"name" : "上海外滩茂悦大酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/2Swp2h1fdj9zCUKsk63BQvVgKLTo_w200_h200_c1_t0.jpg",
"price" : 689,
"score" : 44,
"starName" : "五星级"
}
},
。。。
1.1.2.2 mult_match
允许同时查询多个字段,查询字段越多,性能越差
# match查询
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "外滩如家",
"fields": ["brand","name","business"]
}
}
}
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 32,
"relation" : "eq"
},
"max_score" : 4.620212,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "434082",
"_score" : 4.620212,
"_source" : {
"address" : "复兴东路260号",
"brand" : "如家",
"business" : "豫园地区",
"city" : "上海",
"id" : 434082,
"location" : "31.220706, 121.498769",
"name" : "如家酒店·neo(上海外滩城隍庙小南门地铁站店)",
"pic" : "https://m.tuniucdn.com/fb2/t1/G6/M00/52/B6/Cii-U13eXLGIdHFzAAIG-5cEwDEAAGRfQNNIV0AAgcT627_w200_h200_c1_t0.jpg",
"price" : 392,
"score" : 44,
"starName" : "二钻"
}
},
。。。
1.1.3 精确查询
精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:
1.1.3.1 term查询
#term查询
GET /hotel/_search
{
"query":{
"term": {
"city": {
"value": "上海"
}
}
}
}
有83个精确匹配的
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 83,
"relation" : "eq"
},
"max_score" : 0.88342106,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "36934",
"_score" : 0.88342106,
"_source" : {
"address" : "静安交通路40号",
"brand" : "7天酒店",
"business" : "四川北路商业区",
"city" : "上海",
"id" : 36934,
"location" : "31.251433, 121.47522",
"name" : "7天连锁酒店(上海宝山路地铁站店)",
"pic" : "https://m.tuniucdn.com/fb2/t1/G1/M00/3E/40/Cii9EVkyLrKIXo1vAAHgrxo_pUcAALcKQLD688AAeDH564_w200_h200_c1_t0.jpg",
"price" : 336,
"score" : 37,
"starName" : "二钻"
}
},
。。。
1.1.3.2 range查询
gte 大于等于 lte小于等于
#range查询
GET /hotel/_search
{
"query":{
"range": {
"price": {
"gte": 350,
"lte": 360
}
}
}
}
查询价格大于等于350小于等于360
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 5,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "395434",
"_score" : 1.0,
"_source" : {
"address" : "东三环北路东方路1号",
"brand" : "希尔顿",
"business" : "燕莎/朝阳公园商业区",
"city" : "北京",
"id" : 395434,
"location" : "39.952703, 116.462387",
"name" : "北京希尔顿酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/3fwNbKGhk6XCrkdVyxwhC5uGpLVy_w200_h200_c1_t0.jpg",
"price" : 350,
"score" : 45,
"starName" : "五星级"
}
},
。。。
1.1.4 地理查询
1.1.4.1 geo_bounding_box查询
1.1.4.2 geo_distance查询
#geo_bounding_box查询
GET /hotel/_search
{
"query": {
"geo_distance":{
"distance":"5km",
"location":"31.21,121.5"
}
}
}
5km范围内只有13家
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 13,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "56201",
"_score" : 1.0,
"_source" : {
"address" : "东方路838号",
"brand" : "万怡",
"business" : "浦东陆家嘴金融贸易区",
"city" : "上海",
"id" : 56201,
"location" : "31.226031, 121.525801",
"name" : "上海齐鲁万怡大酒店",
"pic" : "https://m.tuniucdn.com/fb2/t1/G6/M00/52/B6/Cii-TF3eXKeIJeN7AASiKHbTtx4AAGRegDSBzMABKJA111_w200_h200_c1_t0.jpg",
"price" : 873,
"score" : 44,
"starName" : "四星级"
}
},
。。。
1.1.5 复合查询
1.1.5.1 相关性算分
BM25不会受词频影响更大
1.1.5.2 Function score Query
案例
#function_score查询
GET /hotel/_search
{
"query": {
"function_score": {
"query": {
"match":{"all":"上海外滩"}},
"functions": [
{
"filter": {"term":{"brand":"7天酒店"}},
"weight":"100"
}
],
"boost_mode":"sum"
}
}
}
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : 104.2893,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "432335",
"_score" : 104.2893,
"_source" : {
"address" : "唐山路145号",
"brand" : "7天酒店",
"business" : "北外滩地区",
"city" : "上海",
"id" : 432335,
"location" : "31.252585, 121.498753",
"name" : "7天连锁酒店(上海北外滩国际客运中心地铁站店)",
"pic" : "https://m2.tuniucdn.com/filebroker/cdn/res/c1/ba/c1baf64418437c56617f89840c6411ef_w200_h200_c1_t0.jpg",
"price" : 249,
"score" : 35,
"starName" : "二钻"
}
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "60487",
"_score" : 5.9931827,
"_source" : {
"address" : "黄浦路199号",
"brand" : "君悦",
"business" : "外滩地区",
"city" : "上海",
"id" : 60487,
"location" : "31.245409, 121.492969",
"name" : "上海外滩茂悦大酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/2Swp2h1fdj9zCUKsk63BQvVgKLTo_w200_h200_c1_t0.jpg",
"price" : 689,
"score" : 44,
"starName" : "五星级"
}
},
1.1.5.3 BooleanQuery
案例
2 搜索结果处理
2.1 排序
elasticsearch支持对搜索结果排序,默认是根据相关度算分(score)来排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
2.1.1 评价降序,价格升序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": {
"order": "desc"
}
},
{
"price": {
"order": "asc"
}
}
]
}
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 201,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "2060618247",
"_score" : null,
"_source" : {
"address" : "粤海街道后海社区后海第二统建楼商业裙楼第二层B",
"brand" : "汉庭",
"business" : "海岸城/后海",
"city" : "深圳",
"id" : 2060618247,
"location" : "22.507276, 113.931251",
"name" : "汉庭酒店(深圳海岸城店)",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/TBoXdgEx5Yjc2HobeC3fPWWnSJi_w200_h200_c1_t0.jpg",
"price" : 562,
"score" : 49,
"starName" : "二钻"
},
"sort" : [
49,
562
]
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "1951709780",
"_score" : null,
"_source" : {
"address" : "福海街道宝安大道 6259号",
"brand" : "万怡",
"business" : "深圳国际会展中心商圈",
"city" : "深圳",
"id" : 1951709780,
"location" : "22.678611, 113.805695",
"name" : "深圳同泰万怡酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/3oUfktphxMAWq9hUxD9uqdjRdZGB_w200_h200_c1_t0.jpg",
"price" : 617,
"score" : 48,
"starName" : "五钻"
},
"sort" : [
48,
617
]
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "2056105938",
"_score" : null,
"_source" : {
"address" : "新华东街289号2号楼",
"brand" : "希尔顿",
"business" : "果园环岛/通州区",
"city" : "北京",
"id" : 2056105938,
"location" : "39.908805, 116.659748",
"name" : "北京通州北投希尔顿酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/NGKdpec3tZJNUUNWJ5pd67Cp5AY_w200_h200_c1_t0.png",
"price" : 1068,
"score" : 48,
"starName" : "五钻"
},
"sort" : [
48,
1068
]
},
。。。
2.1.2 位置坐标升序排序
找到 121.61,31.03距离的酒店,升序排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance": {
"location": "31.03,121.61",
"order": "asc",
"unit":"km"
}
}
]
}
{
"took" : 43,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 201,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "2056298828",
"_score" : null,
"_source" : {
"address" : "沪南公路7688弄1号",
"brand" : "万豪",
"business" : "南汇/野生动物园",
"city" : "上海",
"id" : 2056298828,
"location" : "31.030053, 121.662943",
"name" : "上海中优城市万豪酒店",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/2gBATEyysyQWmw3wZL863HGdqjaq_w200_h200_c1_t0.jpg",
"price" : 1200,
"score" : 45,
"starName" : "五钻"
},
"sort" : [
5.044557687681683
]
},
sort就是距离的远近
2.2 分页
#分页
GET /hotel/_search
{
"query":{
"match_all": {}
},
"from": 0,
"size":2,
"sort": [
{
"price": {
"order": "asc"
}
}
]
}
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 201,
"relation" : "eq"
},
"max_score" : null,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "197837109",
"_score" : null,
"_source" : {
"address" : "布吉镇深惠路龙珠商城",
"brand" : "如家",
"business" : "布吉/深圳东站",
"city" : "深圳",
"id" : 197837109,
"location" : "22.602482, 114.123284",
"name" : "如家酒店·neo(深圳龙岗大道布吉地铁站店)",
"pic" : "https://m.tuniucdn.com/fb2/t1/G6/M00/25/58/Cii-TF3PFZOIA7jwAAKInGFN4xgAAEVbAGeP4AAAoi0485_w200_h200_c1_t0.jpg",
"price" : 127,
"score" : 43,
"starName" : "二钻"
},
"sort" : [
127
]
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "2316304",
"_score" : null,
"_source" : {
"address" : "龙岗街道龙岗墟社区龙平东路62号",
"brand" : "如家",
"business" : "龙岗中心区/大运新城",
"city" : "深圳",
"id" : 2316304,
"location" : "22.730828, 114.278337",
"name" : "如家酒店(深圳双龙地铁站店)",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/4AzEoQ44awd1D2g95a6XDtJf3dkw_w200_h200_c1_t0.jpg",
"price" : 135,
"score" : 45,
"starName" : "二钻"
},
"sort" : [
135
]
}
]
}
}
2.2.1 深度分页
2.3 高亮
#高亮
GET /hotel/_search
{
"query":{
"match":
{
"all":"如家"
}
},
"highlight":
{
"fields":
{
"name": {
"require_field_match": "false"
}
}
}
}
在es中,高亮必须用match进行匹配,且默认情况下,es搜索字段必须与高亮字段一致才会高亮,故"require_field_match": "false"
{
"took" : 89,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 30,
"relation" : "eq"
},
"max_score" : 2.4133554,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "200215365",
"_score" : 2.4133554,
"_source" : {
"address" : "虹梅路2971号",
"brand" : "如家",
"business" : "虹桥地区",
"city" : "上海",
"id" : 200215365,
"location" : "31.180968, 121.392415",
"name" : "如家酒店(上海虹桥漕河泾古北店)",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/2WPfVp6auQkYoHzAdSbxwHAtQFfa_w200_h200_c1_t0.jpg",
"price" : 189,
"score" : 44,
"starName" : "二钻"
},
"highlight" : {
"name" : [
"<em>如家</em>酒店(上海虹桥漕河泾古北店)"
]
}
},
3 RestClient查询文档
3.1 快速入门
@Test
void testMatchAll() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
request.source().query(QueryBuilders.matchAllQuery());
//2.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//3.解析结果
SearchHits searchHits= response.getHits();
//3.1 打印总记录数
long total = searchHits.getTotalHits().value;
//3.2 保存结果数组
SearchHit[] hits = searchHits.getHits();
//3.3 遍历结果
for (SearchHit hit : hits) {
String json= hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println(hotelDoc);
}
}
打印结果
HotelDoc(id=36934, name=7天连锁酒店(上海宝山路地铁站店), address=静安交通路40号, price=336, score=37, brand=7天酒店, city=上海, starName=二钻, business=四川北路商业区, location=31.251433, 121.47522, pic=https://m.tuniucdn.com/fb2/t1/G1/M00/3E/40/Cii9EVkyLrKIXo1vAAHgrxo_pUcAALcKQLD688AAeDH564_w200_h200_c1_t0.jpg)
HotelDoc(id=38609, name=速8酒店(上海赤峰路店), address=广灵二路126号, price=249, score=35, brand=速8, city=上海, starName=二钻, business=四川北路商业区, location=31.282444, 121.479385, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/DF/96/Cii-TFkx0ImIQZeiAAITil0LM7cAALCYwKXHQ4AAhOi377_w200_h200_c1_t0.jpg)
HotelDoc(id=38665, name=速8酒店上海中山北路兰田路店, address=兰田路38号, price=226, score=35, brand=速8, city=上海, starName=二钻, business=长风公园地区, location=31.244288, 121.422419, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/EF/86/Cii-Tlk2mV2IMZ-_AAEucgG3dx4AALaawEjiycAAS6K083_w200_h200_c1_t0.jpg)
。。。
request.source().query(QueryBuilders.matchAllQuery());
source提供了对查询结果的处理:query,sort,highligt,from,size这些
QueryBuilders提供了各种各样的查询:match、match all、boolquery、term、range这些
抽取静态方法handleResponse来处理响应结果
private static void handleResponse(SearchResponse response) {
//3.解析结果
SearchHits searchHits= response.getHits();
//3.1 打印总记录数
long total = searchHits.getTotalHits().value;
System.out.println("总记录数:"+total);
//3.2 保存结果数组
SearchHit[] hits = searchHits.getHits();
//3.3 遍历结果
for (SearchHit hit : hits) {
String json= hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(!CollectionUtils.isEmpty(highlightFields)){
HighlightField highlightField = highlightFields.get("name");
if(highlightField!=null){
//取出高亮数据的第一个
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
System.out.println(hotelDoc);
}
}
3.2 match、term…
3.2.1 全文查询
@Test
void testMatch() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//单字段匹配
request.source().query(QueryBuilders.matchQuery("all","如家"));
//多字段匹配
//request.source().query(QueryBuilders.multiMatchQuery("如家","name","brand","business"));
//2.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//3.解析结果
handleResponse(response);
}
总记录数:30
HotelDoc(id=200215365, name=如家酒店(上海虹桥漕河泾古北店), address=虹梅路2971号, price=189, score=44, brand=如家, city=上海, starName=二钻, business=虹桥地区, location=31.180968, 121.392415, pic=https://m.tuniucdn.com/fb3/s1/2n9c/2WPfVp6auQkYoHzAdSbxwHAtQFfa_w200_h200_c1_t0.jpg)
HotelDoc(id=234719711, name=如家酒店·neo(北京朝阳北路十里堡地铁站店), address=朝阳北路八里庄南里26号, price=378, score=47, brand=如家, city=北京, starName=二钻, business=国贸地区, location=39.922472, 116.501118, pic=https://m.tuniucdn.com/fb3/s1/2n9c/2rHdXNCmycnUxw99AniFC25ZDSfJ_w200_h200_c1_t0.jpg)
HotelDoc(id=434082, name=如家酒店·neo(上海外滩城隍庙小南门地铁站店), address=复兴东路260号, price=392, score=44, brand=如家, city=上海, starName=二钻, business=豫园地区, location=31.220706, 121.498769, pic=https://m.tuniucdn.com/fb2/t1/G6/M00/52/B6/Cii-U13eXLGIdHFzAAIG-5cEwDEAAGRfQNNIV0AAgcT627_w200_h200_c1_t0.jpg)
3.2.2 精确查询
//精确查询
@Test
void testTermRange() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//词条查询
request.source().query(QueryBuilders.termQuery("city","杭州"));
//范围查询
request.source().query(QueryBuilders.rangeQuery("price").gte(200).lte(300));
//2.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//3.解析结果
handleResponse(response);
}
总记录数:33
HotelDoc(id=38609, name=速8酒店(上海赤峰路店), address=广灵二路126号, price=249, score=35, brand=速8, city=上海, starName=二钻, business=四川北路商业区, location=31.282444, 121.479385, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/DF/96/Cii-TFkx0ImIQZeiAAITil0LM7cAALCYwKXHQ4AAhOi377_w200_h200_c1_t0.jpg)
HotelDoc(id=38665, name=速8酒店上海中山北路兰田路店, address=兰田路38号, price=226, score=35, brand=速8, city=上海, starName=二钻, business=长风公园地区, location=31.244288, 121.422419, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/EF/86/Cii-Tlk2mV2IMZ-_AAEucgG3dx4AALaawEjiycAAS6K083_w200_h200_c1_t0.jpg)
HotelDoc(id=38812, name=7天连锁酒店(上海漕溪路地铁站店), address=徐汇龙华西路315弄58号, price=298, score=37, brand=7天酒店, city=上海, starName=二钻, business=八万人体育场地区, location=31.174377, 121.442875, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/E0/0E/Cii-TlkyIr2IEWNoAAHQYv7i5CkAALD-QP2iJwAAdB6245_w200_h200_c1_t0.jpg)
3.2.3 复合查询
//复合查询
@Test
void testCompose() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//布尔查询
request.source().query(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("name","如家"))
.mustNot(QueryBuilders.rangeQuery("price").gte(400))
.filter(QueryBuilders.geoDistanceQuery("location")
.point(31.03,121.61)
.distance("50km")))
.sort("price", SortOrder.ASC);
//2.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//3.解析结果
handleResponse(response);
}
总记录数:10
HotelDoc(id=541619, name=如家酒店(上海莘庄地铁站龙之梦商业广场店), address=莘庄镇莘浜路172号, price=149, score=44, brand=如家, city=上海, starName=二钻, business=莘庄工业区, location=31.105797, 121.37755, pic=https://m.tuniucdn.com/fb3/s1/2n9c/3mKs3jETvJDj3dDdkRB9UyLLvPna_w200_h200_c1_t0.jpg)
HotelDoc(id=608374, name=如家酒店(上海浦东机场龙东大道合庆店), address=东川公路5863号, price=160, score=45, brand=如家, city=上海, starName=二钻, business=浦东机场核心区, location=31.237662, 121.718556, pic=https://m.tuniucdn.com/fb3/s1/2n9c/LUYxGGV4pzjKeN5a69K4deU8JD8_w200_h200_c1_t0.jpg)
HotelDoc(id=485775, name=如家酒店(上海闵行华东师范大学吴泾店), address=吴泾镇宝秀路977号, price=161, score=45, brand=如家, city=上海, starName=二钻, business=交大/闵行经济开发区, location=31.047135, 121.46224, pic=https://m.tuniucdn.com/fb3/s1/2n9c/V8pz15CkiMX5xYJRmbbp5zkKWJ8_w200_h200_c1_t0.jpg)
3.3 排序分页
//分页查询
@Test
void testPage() throws IOException {
int page=1;
int size=3;
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//词条查询
request.source().query(QueryBuilders.termQuery("city","上海"))
.from((page-1)*size).size(size);
//2.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//3.解析结果
handleResponse(response);
}
总记录数:83
HotelDoc(id=36934, name=7天连锁酒店(上海宝山路地铁站店), address=静安交通路40号, price=336, score=37, brand=7天酒店, city=上海, starName=二钻, business=四川北路商业区, location=31.251433, 121.47522, pic=https://m.tuniucdn.com/fb2/t1/G1/M00/3E/40/Cii9EVkyLrKIXo1vAAHgrxo_pUcAALcKQLD688AAeDH564_w200_h200_c1_t0.jpg)
HotelDoc(id=38609, name=速8酒店(上海赤峰路店), address=广灵二路126号, price=249, score=35, brand=速8, city=上海, starName=二钻, business=四川北路商业区, location=31.282444, 121.479385, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/DF/96/Cii-TFkx0ImIQZeiAAITil0LM7cAALCYwKXHQ4AAhOi377_w200_h200_c1_t0.jpg)
HotelDoc(id=38665, name=速8酒店上海中山北路兰田路店, address=兰田路38号, price=226, score=35, brand=速8, city=上海, starName=二钻, business=长风公园地区, location=31.244288, 121.422419, pic=https://m.tuniucdn.com/fb2/t1/G2/M00/EF/86/Cii-Tlk2mV2IMZ-_AAEucgG3dx4AALaawEjiycAAS6K083_w200_h200_c1_t0.jpg)
3.4 高亮
//高亮
@Test
void testHighlight() throws IOException {
int page=1;
int size=3;
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//词条查询
request.source().query(QueryBuilders.matchQuery("all","如家"))
.from((page-1)*size).size(size)
.highlighter(new HighlightBuilder().field("name")
.requireFieldMatch(false));
//2.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//3.解析结果-高亮解析需要重新解析
SearchHits searchHits= response.getHits();
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
//获取source
String json= hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
//获取高亮数据
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(!CollectionUtils.isEmpty(highlightFields)){
HighlightField highlightField = highlightFields.get("name");
if(highlightField!=null){
//取出高亮数据的第一个
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
System.out.println(hotelDoc);
}
}
4 黑马旅游
使用时使用localhost:8089
访问端口
4.1 完成搜索和分页
4.1.1 定义实体类接收前端参数
在pojo中定义RequestParams
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
}
4.1.2 定义controller接收前端参数
先在pojo中创建实体类PageResult
@Data
public class PageResult {
private Long total;
private List<HotelDoc> hotels;
public PageResult() {
this.total = 0L;
this.hotels = new ArrayList<>();
}
public PageResult(Long total, List<HotelDoc> hotels) {
this.total = total;
this.hotels = hotels;
}
}
再创建controller
@RestController
@RequestMapping("/hotel")
public class HotelController {
@Autowired
private IHotelService hotelService;
@PostMapping("/list")
//json风格用@RequestBody
public PageResult search(@RequestBody RequestParams params) {
return hotelService.search(params);
}
}
再实现 hotelService.search,因为后面发送请求会用到this.client = new RestHighLevelClient
,因此把他作为一个Bean注入到启动类中是最好的
@MapperScan("cn.itcast.hotel.mapper")
@SpringBootApplication
public class HotelDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HotelDemoApplication.class, args);
}
@Bean
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.204.129:9200")));
}
}
然后在IhotelService的实现类hotelService中可以直接注入RestHighLevelClient
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
private static PageResult handleResponse(SearchResponse response) {
//3.解析结果
SearchHits searchHits= response.getHits();
//3.1 打印总记录数
long total = searchHits.getTotalHits().value;
//3.2 保存结果数组
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> hotelDocs = new ArrayList<>();
//3.3 遍历结果
for (SearchHit hit : hits) {
String json= hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(!CollectionUtils.isEmpty(highlightFields)){
HighlightField highlightField = highlightFields.get("name");
if(highlightField!=null){
//取出高亮数据的第一个
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
hotelDocs.add(hotelDoc);
}
return new PageResult(total,hotelDocs);
}
@Override
//全文检索
public PageResult search(RequestParams params) {
try {
//1.准备Request
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
//2.1 query
String key= params.getKey();
if(key==null||"".equals(key)){
request.source().query(QueryBuilders.matchAllQuery());
}
else{
request.source().query(QueryBuilders.matchQuery("all",key));
}
//2.2 分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page-1)*size).size(size);
//2.3 排序
String sortBy = params.getSortBy();
if(sortBy!=null&&!"default".equals(sortBy)){
if("price".equals(sortBy)){
request.source().sort(sortBy, SortOrder.ASC);
}
else{
request.source().sort(sortBy, SortOrder.DESC);
}
}
//3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
4.2 添加过滤功能
4.2.1 扩展RequestParams类
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
private String brand;
private String starName;
private String minPrice;
private String maxPrice;
private String city;
}
4.2.2 修改search方法添加过滤条件
//构建基本查询
private static void buildBasicQuery(RequestParams params, SearchRequest request) {
//2.1 query
//构建BoolQuery
BoolQueryBuilder boolQuery =QueryBuilders.boolQuery();
String key= params.getKey();
if(key==null||"".equals(key)){
boolQuery.must(QueryBuilders.matchAllQuery());
}
else{
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
//条件过滤
//城市
if(params.getCity()!=null&&!"".equals(params.getCity())){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
//品牌
if(params.getBrand()!=null&&!"".equals(params.getBrand())){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
//星级
if(params.getStarName()!=null&&!"".equals(params.getStarName())){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
//价格
if(params.getMinPrice()!=null&&!"".equals(params.getMinPrice())){
boolQuery.filter(QueryBuilders.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice()));
}
request.source().query(boolQuery);
}
@Override
//全文检索
public PageResult search(RequestParams params) {
try {
//1.准备Request
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
buildBasicQuery(params, request);
//2.2 分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page-1)*size).size(size);
//2.3 排序
String sortBy = params.getSortBy();
if(sortBy!=null&&!"default".equals(sortBy)){
if("price".equals(sortBy)){
request.source().sort(sortBy, SortOrder.ASC);
}
else{
request.source().sort(sortBy, SortOrder.DESC);
}
}
//3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
4.3 搜索附近酒店
4.3.1 扩展RequestParams类
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
private String brand;
private String starName;
private String minPrice;
private String maxPrice;
private String city;
private String location;
}
4.3.2 修改排序条件
@Override
//全文检索
public PageResult search(RequestParams params) {
try {
//1.准备Request
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
buildBasicQuery(params, request);
//2.2 分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page-1)*size).size(size);
//2.3 排序
//地理排序,unit单位
if(params.getLocation()!=null&&!"".equals(params.getLocation())){
request.source().sort(SortBuilders
.geoDistanceSort("location",new GeoPoint(params.getLocation()))
.order(SortOrder.ASC)
.unit(DistanceUnit.KILOMETERS));
}
String sortBy = params.getSortBy();
if(sortBy!=null&&!"default".equals(sortBy)){
if("price".equals(sortBy)){
request.source().sort(sortBy, SortOrder.ASC);
}
else{
request.source().sort(sortBy, SortOrder.DESC);
}
}
//3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
还想要拿到具体举例,就要修改结果解析
//获取距离
Object[] sortValues = hit.getSortValues();
if(sortValues!=null&&sortValues.length>0){
hotelDoc.setDistance((Double) sortValues[0]);
}
//响应结果处理
private static PageResult handleResponse(SearchResponse response) {
//3.解析结果
SearchHits searchHits= response.getHits();
//3.1 打印总记录数
long total = searchHits.getTotalHits().value;
//3.2 保存结果数组
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> hotelDocs = new ArrayList<>();
//3.3 遍历结果
for (SearchHit hit : hits) {
String json= hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(!CollectionUtils.isEmpty(highlightFields)){
HighlightField highlightField = highlightFields.get("name");
if(highlightField!=null){
//取出高亮数据的第一个
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
//获取距离
Object[] sortValues = hit.getSortValues();
if(sortValues!=null&&sortValues.length>0){
hotelDoc.setDistance((Double) sortValues[0]);
}
hotelDocs.add(hotelDoc);
}
return new PageResult(total,hotelDocs);
}
我们返回的结果是HotelDoc,所以要新添加一个distance属性
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
private Object distance;
默认按距离进行排序
4.4 广告置顶
4.4.1 添加isAD字段
扩展HotelDoc类
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
private Object distance;
private Boolean isAD;
4.4.2 随机为酒店添加isAD字段
POST /hotel/_update/2051661320
{
"doc":{
"isAD":true
}
}
POST /hotel/_update/2048042240
{
"doc":{
"isAD":true
}
}
POST /hotel/_update/1997830708
{
"doc":{
"isAD":true
}
}
POST /hotel/_update/1734220581
{
"doc":{
"isAD":true
}
}
4.4.3 修改search方法
function_score
//算分控制
FunctionScoreQueryBuilder functionScoreQuery =
QueryBuilders.functionScoreQuery(
boolQuery,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAD", true),
ScoreFunctionBuilders.weightFactorFunction(1000)
)
}).boostMode(CombineFunction.SUM);
//构建基本查询
private static void buildBasicQuery(RequestParams params, SearchRequest request) {
//原始查询
//2.1 query
//构建BoolQuery
BoolQueryBuilder boolQuery =QueryBuilders.boolQuery();
String key= params.getKey();
if(key==null||"".equals(key)){
boolQuery.must(QueryBuilders.matchAllQuery());
}
else{
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
//条件过滤
//城市
if(params.getCity()!=null&&!"".equals(params.getCity())){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
//品牌
if(params.getBrand()!=null&&!"".equals(params.getBrand())){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
//星级
if(params.getStarName()!=null&&!"".equals(params.getStarName())){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
//价格
if(params.getMinPrice()!=null&&!"".equals(params.getMinPrice())){
boolQuery.filter(QueryBuilders.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice()));
}
//算分控制
FunctionScoreQueryBuilder functionScoreQuery =
QueryBuilders.functionScoreQuery(
boolQuery,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAD", true),
ScoreFunctionBuilders.weightFactorFunction(1000)
)
}).boostMode(CombineFunction.SUM);
request.source().query(functionScoreQuery);
}
5 深入es
5.1 数据聚合
参与聚合的字段必须是:keyword、数值、日期、布尔
5.1.1 Bucket聚合
POST /hotel/_search
{
"size": 0,
"aggs": {
"brandAggs": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}
因为不想看文档所以hits的数组是空
doc_count是品牌里的酒店个数,默认按统计数量排序
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 201,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"brandAggs" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 96,
"buckets" : [
{
"key" : "7天酒店",
"doc_count" : 30
},
{
"key" : "如家",
"doc_count" : 30
},
{
"key" : "皇冠假日",
"doc_count" : 17
},
{
"key" : "速8",
"doc_count" : 15
},
{
"key" : "万怡",
"doc_count" : 13
}
]
}
}
}
POST /hotel/_search
{
"size": 0,
"aggs": {
"brandAggs": {
"terms": {
"field": "brand",
"order": {
"_count": "asc"
},
"size": 5
}
}
}
}
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 201,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"brandAggs" : {
"doc_count_error_upper_bound" : -1,
"sum_other_doc_count" : 180,
"buckets" : [
{
"key" : "万丽",
"doc_count" : 2
},
{
"key" : "丽笙",
"doc_count" : 2
},
{
"key" : "君悦",
"doc_count" : 4
},
{
"key" : "豪生",
"doc_count" : 6
},
{
"key" : "维也纳",
"doc_count" : 7
}
]
}
}
}
POST /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 150,
"lte": 200
}
}
},
"size": 0,
"aggs": {
"brandAggs": {
"terms": {
"field": "brand",
"order": {
"_count": "desc"
},
"size": 5
}
}
}
}
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 12,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"brandAggs" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "如家",
"doc_count" : 9
},
{
"key" : "速8",
"doc_count" : 2
},
{
"key" : "汉庭",
"doc_count" : 1
}
]
}
}
}
5.1.2 Metric聚合
#metric聚合
POST /hotel/_search
{
"size":0,
"aggs": {
"brandAggs": {
"terms": {
"field": "brand",
"size": 5,
"order": {
"score_stats.avg": "desc"
}
},
"aggs":{
"score_stats":{
"stats": {
"field": "score"
}
}
}
}
}
}
对stats的结果进行排序,在bucket中进行排序,stats的结果通过.来对属性进行访问,score_stats.avg
{
"took" : 7,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 201,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"brandAggs" : {
"doc_count_error_upper_bound" : -1,
"sum_other_doc_count" : 166,
"buckets" : [
{
"key" : "万丽",
"doc_count" : 2,
"score_stats" : {
"count" : 2,
"min" : 46.0,
"max" : 47.0,
"avg" : 46.5,
"sum" : 93.0
}
},
{
"key" : "凯悦",
"doc_count" : 8,
"score_stats" : {
"count" : 8,
"min" : 45.0,
"max" : 47.0,
"avg" : 46.25,
"sum" : 370.0
}
},
{
"key" : "和颐",
"doc_count" : 12,
"score_stats" : {
"count" : 12,
"min" : 44.0,
"max" : 47.0,
"avg" : 46.083333333333336,
"sum" : 553.0
}
},
{
"key" : "丽笙",
"doc_count" : 2,
"score_stats" : {
"count" : 2,
"min" : 46.0,
"max" : 46.0,
"avg" : 46.0,
"sum" : 92.0
}
},
{
"key" : "喜来登",
"doc_count" : 11,
"score_stats" : {
"count" : 11,
"min" : 44.0,
"max" : 48.0,
"avg" : 46.0,
"sum" : 506.0
}
}
]
}
}
}
5.1.3 RestClient实现聚合
//聚合查询
@Test
void testAgg() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg")
.field("brand")
.size(5));
//3.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
handleResponse(response);
}
解析结果
Aggregations aggregations = response.getAggregations();
if(aggregations!=null){
System.out.println("聚合结果:");
//获取指定名称的聚合
Terms brandTerms = aggregations.get("brandAgg");
//获取桶
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
//遍历桶
for (Terms.Bucket bucket : buckets) {
System.out.println(bucket.getKeyAsString()+":"+bucket.getDocCount());
}
}
private static void handleResponse(SearchResponse response) {
//3.解析结果
SearchHits searchHits= response.getHits();
//3.1 打印总记录数
long total = searchHits.getTotalHits().value;
System.out.println("总记录数:"+total);
//3.2 保存结果数组
SearchHit[] hits = searchHits.getHits();
if (hits.length > 0) {
//3.3 遍历结果
for (SearchHit hit : hits) {
String json= hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(!CollectionUtils.isEmpty(highlightFields)){
HighlightField highlightField = highlightFields.get("name");
if(highlightField!=null){
//取出高亮数据的第一个
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}
System.out.println(hotelDoc);
}
}
Aggregations aggregations = response.getAggregations();
if(aggregations!=null){
System.out.println("聚合结果:");
//获取指定名称的聚合
Terms brandTerms = aggregations.get("brandAgg");
//获取桶
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
//遍历桶
for (Terms.Bucket bucket : buckets) {
System.out.println(bucket.getKeyAsString()+":"+bucket.getDocCount());
}
}
}
对聚合结果进行stats
@Test
void testStats() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg")
.field("brand")
.size(5).subAggregation(AggregationBuilders.stats("score_stats").field("score")));
//3.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
handleResponse(response);
}
解析结果
Aggregations aggregations = response.getAggregations();
if(aggregations!=null){
System.out.println("聚合结果:");
//获取指定名称的聚合
Terms brandTerms = aggregations.get("brandAgg");
//获取桶
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
//遍历桶
for (Terms.Bucket bucket : buckets) {
System.out.println(bucket.getKeyAsString()+":"+bucket.getDocCount());
Stats stats = bucket.getAggregations().get("score_stats");
if(stats!=null){
System.out.println("数目:"+stats.getCount());
System.out.println("最大值:"+stats.getMax());
System.out.println("最小值:"+stats.getMin());
System.out.println("平均值:"+stats.getAvg());
System.out.println("总和:"+stats.getSum());
}
}
}
5.1.4 案例
实现
@Override
public Map<String, List<String>> filters() {
//1.准备Request
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand"));
request.source().aggregation(AggregationBuilders.terms("starNameAgg").field("starName"));
request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city"));
//3.发送请求
try {
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
Aggregations aggregations = response.getAggregations();
Map<String, List<String>> filters = new HashMap<>();
Terms cityAgg = aggregations.get("cityAgg");
if(cityAgg!=null){
List<? extends Terms.Bucket> buckets = cityAgg.getBuckets();
List<String> cities = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
cities.add(bucket.getKeyAsString());
}
filters.put("城市",cities);
}
Terms brandAgg = aggregations.get("brandAgg");
if(brandAgg!=null){
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
List<String> brands = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
brands.add(bucket.getKeyAsString());
}
filters.put("品牌",brands);
}
Terms starNameAgg = aggregations.get("starNameAgg");
if(starNameAgg!=null){
List<? extends Terms.Bucket> buckets = starNameAgg.getBuckets();
List<String> starNames = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
starNames.add(bucket.getKeyAsString());
}
filters.put("星级",starNames);
}
return filters;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
在单元测试中测试一下
@SpringBootTest
class HotelDemoApplicationTests {
@Autowired
private IHotelService hotelService;
@Test
void contextLoads() {
Map<String, List<String>> filters = hotelService.filters();
System.out.println(filters);
}
}
输出成功
{品牌=[7天酒店, 如家, 皇冠假日, 速8, 万怡, 华美达, 和颐, 万豪, 喜来登, 希尔顿], 星级=[二钻, 五钻, 四钻, 五星级, 三钻, 四星级], 城市=[上海, 北京, 深圳]}
@Override
public Map<String, List<String>> filters(RequestParams params) {
//1.准备Request
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
//2.1 设置query信息
buildBasicQuery(params, request);
request.source().size(0);
request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand"));
request.source().aggregation(AggregationBuilders.terms("starNameAgg").field("starName"));
request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city"));
//3.发送请求
try {
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
Aggregations aggregations = response.getAggregations();
Map<String, List<String>> filters = new HashMap<>();
Terms cityAgg = aggregations.get("cityAgg");
if(cityAgg!=null){
List<? extends Terms.Bucket> buckets = cityAgg.getBuckets();
List<String> cities = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
cities.add(bucket.getKeyAsString());
}
filters.put("城市",cities);
}
Terms brandAgg = aggregations.get("brandAgg");
if(brandAgg!=null){
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
List<String> brands = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
brands.add(bucket.getKeyAsString());
}
filters.put("品牌",brands);
}
Terms starNameAgg = aggregations.get("starNameAgg");
if(starNameAgg!=null){
List<? extends Terms.Bucket> buckets = starNameAgg.getBuckets();
List<String> starNames = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
starNames.add(bucket.getKeyAsString());
}
filters.put("星级",starNames);
}
return filters;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
filters与list的基础查询条件一致,保持动态过滤
5.2 自动补全
py上传
测试
POST /_analyze
{
"text":["如家酒店真棒啊"],
"analyzer": "pinyin"
}
{
"tokens" : [
{
"token" : "ru",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 0
},
{
"token" : "rjjdzba",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 0
},
{
"token" : "jia",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 1
},
{
"token" : "jiu",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 2
},
{
"token" : "dian",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 3
},
{
"token" : "zhen",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 4
},
{
"token" : "bang",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 5
},
{
"token" : "a",
"start_offset" : 0,
"end_offset" : 0,
"type" : "word",
"position" : 6
}
]
}
5.2.1 自定义分词器
自定义分词器在mapping映射时才有用
// 自定义拼音分词器
PUT /test
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "my_analyzer"
}
}
}
}
POST /test/_analyze
{
"text":["如家酒店真棒啊"],
"analyzer": "my_analyzer"
}
{
"tokens" : [
{
"token" : "如家",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "rujia",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "rj",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "酒店",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "jiudian",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "jd",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "真棒",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "zhenbang",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "zb",
"start_offset" : 4,
"end_offset" : 6,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "啊",
"start_offset" : 6,
"end_offset" : 7,
"type" : "CN_CHAR",
"position" : 3
},
{
"token" : "a",
"start_offset" : 6,
"end_offset" : 7,
"type" : "CN_CHAR",
"position" : 3
}
]
}
5.2.2 健壮分词器
POST /test/_doc/1
{
"id": 1,
"name": "狮子"
}
POST /test/_doc/2
{
"id": 2,
"name": "虱子"
}
GET /test/_search
{
"query": {
"match": {
"name": "掉入狮子笼咋办"
}
}
}
{
"took" : 25,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 0.33425623,
"hits" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.33425623,
"_source" : {
"id" : 1,
"name" : "狮子"
}
},
{
"_index" : "test",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.3085442,
"_source" : {
"id" : 2,
"name" : "虱子"
}
}
]
}
}
都搜到狮子、虱子
// 自定义拼音分词器
PUT /test
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "my_analyzer",
"search_analyzer": "ik_smart"
}
}
}
}
这回只能搜到狮子了
{
"took" : 5,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.9530773,
"hits" : [
{
"_index" : "test",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.9530773,
"_source" : {
"id" : 1,
"name" : "狮子"
}
}
]
}
}
5.2.3 completion suggester查询
插入数组,既可以通过S进行补全,也可以通过W进行补全
创建自动补全的索引库
PUT test2
{
"mappings": {
"properties": {
"title":{
"type": "completion"
}
}
}
}
插入数据
POST test2/_doc
{
"title": ["Sony", "WH-1000XM3"]
}
POST test2/_doc
{
"title": ["SK-II", "PITERA"]
}
POST test2/_doc
{
"title": ["Nintendo", "switch"]
}
自动补全查询
POST /test2/_search
{
"suggest": {
"title_suggest": {
"text": "s",
"completion": {
"field": "title",
"skip_duplicates": true,
"size": 10
}
}
}
}
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"suggest" : {
"title_suggest" : [
{
"text" : "s",
"offset" : 0,
"length" : 1,
"options" : [
{
"text" : "SK-II",
"_index" : "test2",
"_type" : "_doc",
"_id" : "IhB-840Bh5MLZyMepJuZ",
"_score" : 1.0,
"_source" : {
"title" : [
"SK-II",
"PITERA"
]
}
},
{
"text" : "Sony",
"_index" : "test2",
"_type" : "_doc",
"_id" : "IRB-840Bh5MLZyMenJtW",
"_score" : 1.0,
"_source" : {
"title" : [
"Sony",
"WH-1000XM3"
]
}
},
{
"text" : "switch",
"_index" : "test2",
"_type" : "_doc",
"_id" : "IxB-840Bh5MLZyMerptN",
"_score" : 1.0,
"_source" : {
"title" : [
"Nintendo",
"switch"
]
}
}
]
}
]
}
}
5.2.4 酒店数据自动补全DSL
创建hotel索引库结构,completion_analyzer是用于补全阶段的分词器,自动补全的词语作为整体出现,所以是keyword,之后用拼音处理
PUT /hotel
{
"settings": {
"analysis": {
"analyzer": {
"text_anlyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
},
"completion_analyzer": {
"tokenizer": "keyword",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"id":{
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "text_anlyzer",
"search_analyzer": "ik_smart",
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword",
"copy_to": "all"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "text_anlyzer",
"search_analyzer": "ik_smart"
},
"suggestion":{
"type": "completion",
"analyzer": "completion_analyzer"
}
}
}
}
因为hotel中多了suggestion的字段,因此在poji中的HotelDoc也要添加
private List<String> suggestion;
因为我们希望在搜索品牌或者商圈的时候,有自动补全,因此在构造函数时,将其放入,对于存在多个商圈的,用、/分割
if(this.business.contains("/")||this.business.contains("、")){
this.suggestion=new ArrayList<>();
this.suggestion.add(this.brand);
//需要切割
String arr1[]=this.business.split("[、/]");
Collections.addAll(this.suggestion, arr1);
}
else{
this.suggestion= Arrays.asList(this.brand, this.business);
}
重新运行单元测试,批处理
@Test
void testBatchInsertIndexDocument2() throws IOException {
//1.创建BulkRequest对象
BulkRequest request = new BulkRequest();
//2.准备数据,添加到BulkRequest对象中
//查询所有酒店数据
hotelService.list().forEach(hotel -> {
//转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
//3.添加到BulkRequest对象中
request.add(new IndexRequest("hotel")
.id(hotel.getId().toString())
.source(JSON.toJSONString(hotelDoc), XContentType.JSON));
});
//4.发起请求
client.bulk(request, RequestOptions.DEFAULT);
}
添加完成后
GET /hotel/_search
{
"query": {
"match_all": {}
}
}
搜索到,字段suggestion多了
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "36934",
"_score" : 1.0,
"_source" : {
"address" : "静安交通路40号",
"brand" : "7天酒店",
"business" : "四川北路商业区",
"city" : "上海",
"id" : 36934,
"location" : "31.251433, 121.47522",
"name" : "7天连锁酒店(上海宝山路地铁站店)",
"pic" : "https://m.tuniucdn.com/fb2/t1/G1/M00/3E/40/Cii9EVkyLrKIXo1vAAHgrxo_pUcAALcKQLD688AAeDH564_w200_h200_c1_t0.jpg",
"price" : 336,
"score" : 37,
"starName" : "二钻",
"suggestion" : [
"7天酒店",
"四川北路商业区"
]
}
},
测试自动补全,不跳过重复的
#测试自动补全
POST /hotel/_search
{
"suggest": {
"title_suggest": {
"text": "rj",
"completion": {
"field": "suggestion",
"skip_duplicates": false,
"size": 10
}
}
}
}
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"suggest" : {
"title_suggest" : [
{
"text" : "rj",
"offset" : 0,
"length" : 2,
"options" : [
{
"text" : "如家",
"_index" : "hotel",
"_type" : "_doc",
"_id" : "415600",
"_score" : 1.0,
"_source" : {
"address" : "三间房乡褡裢坡村青年沟西侧558号",
"brand" : "如家",
"business" : "传媒大学/管庄地区",
"city" : "北京",
"id" : 415600,
"location" : "39.923212, 116.560023",
"name" : "如家酒店(北京朝阳北路传媒大学褡裢坡地铁站店)",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/3NezpxNZWQMdNXibwbMkQuAZjDyJ_w200_h200_c1_t0.jpg",
"price" : 259,
"score" : 47,
"starName" : "二钻",
"suggestion" : [
"如家",
"传媒大学",
"管庄地区"
]
}
},
5.2.5 酒店数据RestAPI解析
//自动补全
@Test
void testSuggestion() throws IOException {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
request.source().suggest(new SuggestBuilder().addSuggestion(
"title_suggest",
SuggestBuilders.completionSuggestion("suggestion")
.prefix("sj")
.size(10)
.skipDuplicates(true)));
//3.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
handleResponse(response);
}
对解析结果进行解析
Suggest suggestions = response.getSuggest();
if(suggestions!=null){
System.out.println("建议结果:");
CompletionSuggestion titleSuggest = suggestions.getSuggestion("title_suggest");
for(CompletionSuggestion.Entry.Option option :titleSuggest.getOptions()){
String text = option.getText().string();
System.out.println(text);
}
}
5.2.6 酒店数据自动补全
自动补全的请求,请求方式是GET
@GetMapping("/suggestion")
public List<String> getSuggetion(@RequestParam("key") String prefix) {
return hotelService.getSuggetion(prefix);
}
解析返回
@Override
public List<String> getSuggetion(String prefix) {
//1.准备Request对象
SearchRequest request = new SearchRequest("hotel");
List<String> suggestionsList = new ArrayList<>();
try {
//2.准备DSL
request.source().suggest(new SuggestBuilder().addSuggestion(
"title_suggest",
SuggestBuilders.completionSuggestion("suggestion")
.prefix(prefix)
.size(10)
.skipDuplicates(true)));
//3.发起请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.解析结果
Suggest suggestions = response.getSuggest();
if(suggestions!=null){
CompletionSuggestion titleSuggest = suggestions.getSuggestion("title_suggest");
for(CompletionSuggestion.Entry.Option option :titleSuggest.getOptions()){
String text = option.getText().string();
suggestionsList.add(text);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return suggestionsList;
}
5.3 数据同步
方案一较为简单,但是增加了耦合度,耗时大大增强。
方案二进行了解耦,但更依赖于MQ的可靠度
方案三依靠spring的binlog,完全进行解耦,引入新的中间件,实现比较复杂
5.3.1 利用mq使mysql和es同步
导入hotel-admin,修改application.yaml,并启动访问
server:
port: 8099
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/heima?useSSL=false
username: root
password: 123sjbsjb
driver-class-name: com.mysql.cj.jdbc.Driver
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
type-aliases-package: cn.itcast.hotel.pojo
测试其CRUD正常
5.3.2 设置队列
增和改在es中属于一类操作,故消息队列分为两类:增/改、删
所以hotel-demo作为消费者,hotel-admin作为生产者
5.3.2.1 引入依赖
因为hotel-demo作为消费者,所以要在hotel-demo引入mq的依赖,即引入spring的AMQP依赖
<!--spring amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
5.3.2.2 配置AMQP
还需要在application.yaml中配置AMQP-----》rabbitmq:
server:
port: 8089
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/heima?useSSL=false
username: root
password: 123sjbsjb
driver-class-name: com.mysql.cj.jdbc.Driver
rabbitmq:
host: 192.168.204.129
port: 5672
virtual-host: /
username: itcast
password: 123321
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
type-aliases-package: cn.itcast.hotel.pojo
5.3.2.3 声明队列交换机
交换机常量设置在常量区constants里,这里采用Direct Exchange方式的消息队列
//Direct exchange
public class MqConstants {
// 定义交换机
public static final String HOTEL_EXCHANGE = "hotel.topic";
// 监听新增和修改的队列
public static final String HOTEL_INSERT_QUEUE = "hotel.insert.queue";
// 监听删除的队列
public static final String HOTEL_DELETE_QUEUE = "hotel.delete.queue";
//RoutingKey是发送者指定的路由键,而BindingKey是接收者指定的路由键
// 新增和修改的路由键
public static final String HOTEL_INSERT_KEY = "hotel.insert";
// 删除的路由键
public static final String HOTEL_DELETE_KEY="hotel.delete";
}
既可以采用bean方式也可以采用注解方式,创建SPringRabbitListener,一定要加@Configuration,设置为配置类
@Configuration
public class SpringRabbitListener {
@RabbitListener(bindings =@QueueBinding(
value=@Queue(name=HOTEL_INSERT_QUEUE),
exchange=@Exchange(name=HOTEL_EXCHANGE,type= ExchangeTypes.DIRECT),
key={HOTEL_INSERT_KEY}
))
public void listenInsertQueue(String message){
//消息监听动作
}
@RabbitListener(bindings =@QueueBinding(
value=@Queue(name=HOTEL_DELETE_QUEUE),
exchange=@Exchange(name=HOTEL_EXCHANGE,type= ExchangeTypes.DIRECT),
key={HOTEL_DELETE_KEY}
))
public void listenDeleteQueue(String message){
//消息监听动作
}
}
5.3.2.4 在admin中完成消息发送
在admin引入AMQP依赖
<!--spring amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
同样需要配置AMQP的地址
server:
port: 8089
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/heima?useSSL=false
username: root
password: 123sjbsjb
driver-class-name: com.mysql.cj.jdbc.Driver
rabbitmq:
host: 192.168.204.129
port: 5672
virtual-host: /
username: itcast
password: 123321
消息发送是在增删改的时候发送消息,所以是在admin的controller中,首先要在controller中注入RabbitTamplate
@RestController
@RequestMapping("hotel")
public class HotelController {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private IHotelService hotelService;
增加的时候发送消息
@PostMapping
public void saveHotel(@RequestBody Hotel hotel){
hotelService.save(hotel);
rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_INSERT_KEY, hotel.getId());
}
因为mq是基于内存的,因此消息发个id就可以了
其他crud一样
@PutMapping()
public void updateById(@RequestBody Hotel hotel){
if (hotel.getId() == null) {
throw new InvalidParameterException("id不能为空");
}
hotelService.updateById(hotel);
rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_INSERT_KEY, hotel.getId());
}
@DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") Long id) {
hotelService.removeById(id);
rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_DELETE_KEY, id);
}
5.3.2.5 demo监听消息
在hotel-demo中监听消息,并更新es中数据,在5.3.2.3中完成监听操作
@Component
public class SpringRabbitListener {
@Autowired
private IHotelService hotelService;
@RabbitListener(bindings =@QueueBinding(
value=@Queue(name=HOTEL_INSERT_QUEUE),
exchange=@Exchange(name=HOTEL_EXCHANGE,type= ExchangeTypes.DIRECT),
key={HOTEL_INSERT_KEY}
))
//监听新增和修改的队列的动作
public void listenInsertQueue(Long id){
hotelService.insertById(id);
}
@RabbitListener(bindings =@QueueBinding(
value=@Queue(name=HOTEL_DELETE_QUEUE),
exchange=@Exchange(name=HOTEL_EXCHANGE,type= ExchangeTypes.DIRECT),
key={HOTEL_DELETE_KEY}
))
//监听删除的队列的动作
public void listenDeleteQueue(Long id){
hotelService.deleteById(id);
}
}
在IHotelService中声明,在其实现类中完成实现
@Override
public void insertById(Long id) {
try {
//根据ID查询
Hotel hotel = getById(id);
//转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
//1.创建Request对象
IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
//2.准备JSON文档
request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
//3.发起请求
client.index(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void deleteById(Long id) {
try {
//1.创建Request对象
DeleteRequest request = new DeleteRequest("hotel",id.toString());
//2.发起请求
client.delete(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
测试成功
5.4 es集群
5.4.1 搭建es集群
见CSDN中其他文章
5.4.2 集群职责及脑裂
5.4.3 分布式新增和查询流程
POST 请求http://192.168.204.129:9200/itcast/_doc/1(/3/5)
{
"title": "试着插入一条id=1/3/5"
}
查询文档
{
"query": {
"match_all":{}
}
}
{
"took": 171,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "试着插入一条id=5"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "试着插入一条id=3"
}
},
{
"_index": "itcast",
"_type": "_doc",
"_id": "1",
"_score": 1.0,
"_source": {
"title": "试着插入一条id=1"
}
}
]
}
}
在9201也查到3条,9202也查到三条
可以通过explain来看到查询结果到底在哪里
{
"query": {
"match_all":{}
},
"explain":true
}
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_shard": "[itcast][0]",
"_node": "p2axigL_Q_q9RmgOGB7nZQ",
"_index": "itcast",
"_type": "_doc",
"_id": "5",
"_score": 1.0,
"_source": {
"title": "试着插入一条id=5"
},
"_explanation": {
"value": 1.0,
"description": "*:*",
"details": []
}
},
{
"_shard": "[itcast][1]",
"_node": "vVKkHh2KSROW66yCwl4v8g",
"_index": "itcast",
"_type": "_doc",
"_id": "3",
"_score": 1.0,
"_source": {
"title": "试着插入一条id=3"
},
"_explanation": {
"value": 1.0,
"description": "*:*",
"details": []
}
},
分布式查询
5.4.4 故障转移
停到主节点
docker-compose stop es01
重新启动es01
docker-compose start es01