项目配置见上一篇
https://blog.csdn.net/weixin_44145478/article/details/103972887
HouseInfoController
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* @program: es
* @create: 2020-01-14 16:11
**/
@Api(value = "house", description = "租房数据")
@RestController
@RequestMapping("/houseInfo")
public class HouseInfoController {
@Autowired
private HouseInfoService houseInfoService;
@RequestMapping(value = "/searchWarehouseInfo", method = RequestMethod.POST)
@ApiOperation(value = "搜索租房基本信息", httpMethod = "POST")
public Result<PagedResult<HouseInfoResp>> searchWarehouseInfo(@RequestBody @Valid SearchReqVo searchReqVo) {
return Result.success(houseInfoService.searchWarehouseInfoList(searchReqVo));
}
@RequestMapping(value = "/updateAllIndex", method = RequestMethod.GET)
@ApiOperation(value = "更新所有租房信息索引", httpMethod = "GET")
public Result<String> updateAllIndex() {
return Result.success(houseInfoService.updateAllIndex());
}
@RequestMapping(value = "/deleteAllIndex", method = RequestMethod.GET)
@ApiOperation(value = "删除所有租房信息索引", httpMethod = "GET")
public Result<String> deleteAllIndex() {
return Result.success(houseInfoService.deleteAllIndex());
}
}
HouseInfoService
public interface HouseInfoService {
PagedResult<HouseInfoResp> searchWarehouseInfoList(SearchReqVo searchReqVo);
String updateAllIndex();
String deleteAllIndex();
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @program: es
* @create: 2020-01-14 16:10
**/
@Service
public class HouseInfoServiceImpl implements HouseInfoService {
private static final Logger logger = LoggerFactory.getLogger(HouseInfoServiceImpl.class);
@Autowired
HouseInfoIndexService houseInfoIndexService;
@Autowired
HouseInfoMapperExt houseInfoMapperExt;
@Override
public PagedResult<HouseInfoResp> searchWarehouseInfoList(SearchReqVo searchReqVo) {
return houseInfoIndexService.search(searchReqVo);
}
@Override
public String updateAllIndex() {
int successCount = 0;
int failCount = 0;
//查询所有 houseInfo数据
//List<HouseInfo> warehouseInfos = houseInfoMapperExt.selectAll();
List<HouseInfo> warehouseInfos;
if (!CollectionUtils.isEmpty(warehouseInfos)) {
for (int i = 0; i < warehouseInfos.size(); i++) {
HouseInfo warehouseInfo = warehouseInfos.get(i);
HouseInfoDto houseInfoDto = new HouseInfoDto();
BeanUtils.copyProperties(warehouseInfo, houseInfoDto);
boolean isSuccess = houseInfoIndexService.index(houseInfoDto);
if (isSuccess) {
++successCount;
} else {
++failCount;
}
}
}
logger.info("全量更新索引完成, 成功:" + successCount + ", fail:" + failCount);
return "成功:" + successCount + ", fail:" + failCount;
}
@Override
public String deleteAllIndex() {
boolean success = houseInfoIndexService.deleteAll();
if (success) {
return "成功";
} else {
return "失败";
}
}
}
ElasticSearchService (共用创建索引,索引,关键词使用分词等)
import com.sf.es.enums.ElasticSearchEnum;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Service
public class ElasticSearchService {
private static final Logger logger = LoggerFactory.getLogger(ElasticSearchService.class);
@Autowired
private RestHighLevelClient restHighLevelClient;
public boolean index(ElasticSearchEnum elasticSearchEnum, String id, String jsonString) {
String methodName = "创建索引,index:" + elasticSearchEnum.getIndex() + ",id:" + id;
try {
IndexRequest request = new IndexRequest()
.index(elasticSearchEnum.getIndex())
.id(id)
.source(jsonString, XContentType.JSON);
IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
logger.info(methodName + "-response:{}", indexResponse);
if (indexResponse != null && indexResponse.status() != null && (RestStatus.CREATED == indexResponse.status() || RestStatus.OK == indexResponse.status())) {
return true;
} else {
return false;
}
} catch (IOException e) {
logger.error(methodName + "-异常", e);
return false;
}
}
public boolean deleteAll(ElasticSearchEnum elasticSearchEnum) {
String methodName = "删除全部索引, index:" + elasticSearchEnum.getIndex();
try {
DeleteIndexRequest request = new DeleteIndexRequest(elasticSearchEnum.getIndex());
restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
return true;
} catch (Exception e) {
logger.error(methodName + "-异常", e);
return false;
}
}
public boolean delete(ElasticSearchEnum elasticSearchEnum, String id) {
String methodName = "删除索引, index:" + elasticSearchEnum.getIndex() + ", id:" + id;
try {
DeleteRequest request = new DeleteRequest()
.index(elasticSearchEnum.getIndex())
.id(id);
DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
logger.info(methodName + "-response:{}", response);
if (response != null && response.status() != null && RestStatus.OK == response.status()) {
return true;
} else {
return false;
}
} catch (Exception e) {
logger.error(methodName + "-异常", e);
return false;
}
}
public List<String> analyze(ElasticSearchEnum elasticSearchEnum, String keyword, String tokenizer) {
List<String> analyzeKeywords = new ArrayList<>();
try {
AnalyzeRequest analyzeRequest = new AnalyzeRequest(elasticSearchEnum.getIndex()).tokenizer(tokenizer).text(keyword);
AnalyzeResponse response = restHighLevelClient.indices().analyze(analyzeRequest, RequestOptions.DEFAULT);
List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
for (AnalyzeResponse.AnalyzeToken token : tokens) {
analyzeKeywords.add(token.getTerm());
}
} catch (IOException e) {
logger.error("关键字分词异常, keyword:{}", keyword, e);
}
return analyzeKeywords;
}
}
HouseInfoIndexService (租房索引方法,更新索引,查询,删除等方法)
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* describe:
* @date 2019/12/30 20:32
*/
@Service
public class HouseInfoIndexService {
private static final Logger logger = LoggerFactory.getLogger(HouseInfoIndexService.class);
@Autowired
private ElasticSearchService elasticSearchService;
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* @param houseInfoDto
* @return
*/
public boolean index(HouseInfoDto houseInfoDto) {
if (houseInfoDto == null || houseInfoDto.getId() == null) {
return false;
}
String methodName = "创建租房信息索引, id:" + houseInfoDto.getId();
try {
return elasticSearchService.index(ElasticSearchEnum.STORAGE_SERVICE, String.valueOf(houseInfoDto.getId()), JSON.toJSONString(houseInfoDto));
} catch (Exception e) {
logger.error(methodName + "-异常", e);
return false;
}
}
/**
* 搜索租房信息
*
* @param searchReqVo
* @return
*/
public PagedResult<HouseInfoResp> search(SearchReqVo searchReqVo) {
PagedResult<HouseInfoResp> houseInfoRespPagedResult= new PagedResult<>();
String methodName = "搜索租房信息, keyword:" + searchReqVo.getKeyword() + ", pageNo:" + searchReqVo.getPageNo() + ", pageSize:" + searchReqVo.getPageSize() + ", sort:" + searchReqVo.getSort();
try {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//最低面积
if (null != searchReqVo.getUsableAreaFrom() && 1 == searchReqVo.getUsableAreaFrom().compareTo(BigDecimal.ZERO)) {
boolQueryBuilder.must(QueryBuilders.rangeQuery(WarehouseIndexKeyEnum.USABLEAREA.getKey()).gte(searchReqVo.getUsableAreaFrom()));
}
//最高面积
if (null != searchReqVo.getUsableAreaTo() && 1 == searchReqVo.getUsableAreaTo().compareTo(BigDecimal.ZERO)) {
boolQueryBuilder.must(QueryBuilders.rangeQuery(WarehouseIndexKeyEnum.USABLEAREA.getKey()).lte(searchReqVo.getUsableAreaTo()));
}
//最低价格
if (null != searchReqVo.getPriceFrom() && 1 == searchReqVo.getPriceFrom().compareTo(BigDecimal.ZERO)) {
boolQueryBuilder.must(QueryBuilders.rangeQuery(WarehouseIndexKeyEnum.PRICE.getKey()).gte(searchReqVo.getPriceFrom()));
}
//最高价格
if (null != searchReqVo.getPriceTo() && 1 == searchReqVo.getPriceTo().compareTo(BigDecimal.ZERO)) {
boolQueryBuilder.must(QueryBuilders.rangeQuery(WarehouseIndexKeyEnum.PRICE.getKey()).lte(searchReqVo.getPriceTo()));
}
// //关键词搜索
// if (StringUtils.isNotEmpty(searchReqVo.getKeyword())) {
// boolQueryBuilder.must(QueryBuilders.multiMatchQuery(searchReqVo.getKeyword(),
// WarehouseIndexKeyEnum.WAREHOUSE_NAME.getKey(),
// WarehouseIndexKeyEnum.ADDRESS.getKey()).minimumShouldMatch("100%"));
// }
// 关键字搜索
if (StringUtils.isNotEmpty(searchReqVo.getKeyword())) {
Map<String, Float> fieldsBoosts = new HashMap<>(2);
fieldsBoosts.put( WarehouseIndexKeyEnum.WAREHOUSE_NAME.getKey(), 9f);
fieldsBoosts.put( WarehouseIndexKeyEnum.ADDRESS.getKey(), 5f);
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(searchReqVo.getKeyword()).fields(fieldsBoosts).minimumShouldMatch(getMiniMumShouldMatch(searchReqVo.getKeyword())));
}
searchSourceBuilder.query(boolQueryBuilder);
//排序
Integer sort = searchReqVo.getSort();
if (0 == sort || 1 == sort) {
//searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
searchSourceBuilder.sort(new FieldSortBuilder(WarehouseIndexKeyEnum.USABLEAREA.getKey()).order(SortOrder.DESC));
} else if (2 == sort) {
//searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
searchSourceBuilder.sort(new FieldSortBuilder(WarehouseIndexKeyEnum.USABLEAREA.getKey()).order(SortOrder.ASC));
} else if (3 == sort) {
//searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
searchSourceBuilder.sort(new FieldSortBuilder(WarehouseIndexKeyEnum.PRICE.getKey()).order(SortOrder.DESC));
} else if (4 == sort) {
//searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
searchSourceBuilder.sort(new FieldSortBuilder(WarehouseIndexKeyEnum.PRICE.getKey()).order(SortOrder.ASC));
}
// 分页设置
Integer pageSize = searchReqVo.getPageSize();
Integer pageNo = searchReqVo.getPageNo();
if (pageSize != null && pageSize > 0) {
searchSourceBuilder.size(pageSize);
if (pageNo != null && pageNo > 0) {
searchSourceBuilder.from(pageSize * (pageNo - 1));
}
}
// 设置查询索引范围
searchRequest.indices(ElasticSearchEnum.STORAGE_SERVICE.getIndex());
// 向搜索请求对象中设置搜索源
searchRequest.source(searchSourceBuilder);
// 执行搜索,向ES发起http请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
if (RestStatus.OK.equals(searchResponse.status())) {
// 搜索结果
SearchHits hits = searchResponse.getHits();
// 搜索总数量
long total = hits.getTotalHits().value;
// 得到匹配度文档
SearchHit[] searchHits = hits.getHits();
List<HouseInfoResp> houseInfoResps = new ArrayList<>();
// 日期格式化对象
for (SearchHit hit : searchHits) {
HouseInfoDto houseInfoDto = JSON.parseObject(hit.getSourceAsString(), HouseInfoDto.class);
HouseInfoResp respVo = new HouseInfoResp();
respVo.setId(houseInfoDto.getId());
respVo.setName(houseInfoDto.getName());
respVo.setUsableArea(houseInfoDto.getUsableArea());
respVo.setPrice(houseInfoDto.getPrice());
respVo.setAddress(houseInfoDto.getAddress());
houseInfoResps.add(respVo);
}
houseInfoRespPagedResult.setDataList(houseInfoResps);
houseInfoRespPagedResult.setPageNo(pageNo);
houseInfoRespPagedResult.setPageSize(pageSize);
houseInfoRespPagedResult.setTotal(total);
houseInfoRespPagedResult.setPages((total + pageSize - 1) / pageSize);
}
} catch (Exception e) {
logger.error(methodName + "-异常", e);
}
return houseInfoRespPagedResult;
}
/**
* 删除所有索引,包括mapping(慎用)
* @return
*/
public boolean deleteAll() {
return elasticSearchService.deleteAll(ElasticSearchEnum.STORAGE_SERVICE);
}
/**
* 通过索引主键ID删除索引
* @param id
* @return
*/
public boolean delete(Long id) {
return elasticSearchService.delete(ElasticSearchEnum.STORAGE_SERVICE, String.valueOf(id));
}
public String getMiniMumShouldMatch (String keyword) {
List<String> keywords = elasticSearchService.analyze(ElasticSearchEnum.STORAGE_SERVICE, keyword, "ik_smart");
if (!CollectionUtils.isEmpty(keywords) && keywords.size() > 2) {
return "75%";
}
return "100%";
}
}
实体类等
public class HouseInfo {
private Long id;
private String name;
private Long usableArea;
private BigDecimal price;
private String picture;
private String address;
private String currentFloor;
//set get....
public class HouseInfoDto {
private Long id;
private String name;
private Long usableArea;
private BigDecimal price;
private String picture;
private String address;
private String currentFloor;
// set get
}
@ApiModel("搜索参数请求VO")
public class SearchReqVo extends PageBaseReqVo {
@ApiModelProperty(value = "排序方式,1:面积从高到底 2:面积从低到高 3:价格从高到低 4:价格从低到高")
private Integer sort = 0;
@ApiModelProperty(value = "最低面积")
private BigDecimal usableAreaFrom;
@ApiModelProperty(value = "最高面积")
private BigDecimal usableAreaTo;
@ApiModelProperty(value = "最低价格")
private BigDecimal priceFrom;
@ApiModelProperty(value = "最高价格")
private BigDecimal priceTo;
@ApiModelProperty(value = "关键词")
private String keyword;
// set get ....
}
public enum WarehouseIndexKeyEnum {
WAREHOUSE_ID("id", "房屋ID"),
WAREHOUSE_NAME("name", "房屋名称"),
USABLEAREA("usableArea", "可用面积"),
PRICE("price", "价格"),
ADDRESS("address", "房屋详细地址"),
ICON("icon", "房屋图片路径");
private String key;
private String desc;
WarehouseIndexKeyEnum(String key, String desc) {
this.key = key;
this.desc = desc;
}
public String getKey() {
return key;
}
public String getDesc() {
return desc;
}
}
public enum ElasticSearchEnum {
STORAGE_SERVICE("storage","租房");
private String index;
private String desc;
ElasticSearchEnum(String index, String desc) {
this.index = index;
this.desc = desc;
}
//set get ...
}