使用Elasticsearch查詢歷史熱點消息汉嗽,搜索的內(nèi)容包括:根據(jù)關(guān)鍵詞搜索,可以多個關(guān)鍵詞稳析;根據(jù)熱點標題查找歷史熱度值變化撵孤。
搜索框DTO
用來接收前端傳遞的搜索框內(nèi)容竭望。
@Data
public class SearchKeywords {
@NotEmpty
private String keywords;
@NotEmpty
private String relation;
@NotEmpty
@NotNull
private String type;
}
定義SeachData
接收從ES返回的數(shù)據(jù)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SearchData {
private String titleText;
private String titleKeyword;
private Integer rank;
private Integer hotNumber;
private String timestamp;
private Integer uId;
}
服務(wù)接口
public interface SearchService {
/**
* 根據(jù)標題查找歷史信息
* @param title 標題
* @param type 哪個表
* @return 歷史信息
*/
public List<SearchData> searchByName(String title, String type);
/**
* 根據(jù)關(guān)鍵詞查找
* @param keyword 關(guān)鍵詞
* @param type 表名
* @param relation 關(guān)鍵詞之間的關(guān)系
* @return 歷史信息
*/
public List<SearchData> searchByKeyword(String keyword, String type, String relation);
/**
* 根據(jù)uId查找內(nèi)容詳情
* @param type 熱榜名稱
* @param uId 唯一鍵
* @return 詳情
*/
public List<SearchDetail> searchDetail(String type, int uId);
}
包括兩個查詢內(nèi)容邪码,還包含了一個根據(jù)uId查找歷史消息咬清,uId在數(shù)據(jù)庫中建立了索引旧烧。
服務(wù)實現(xiàn)
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private EsUtil esUtil;
@Autowired
private SearchDetailMapper searchDetailMapper;
@Override
public List<SearchData> searchByName(String title, String type) {
// 創(chuàng)建bool query
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
// 查找title_keyword中有查找的關(guān)鍵詞
boolQueryBuilder.filter(QueryBuilders.termQuery("title_keyword", title));
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(boolQueryBuilder);
// 根據(jù)時間倒序
builder.sort("timestamp", SortOrder.ASC);
// 取前30個
builder.size(30);
return esUtil.search(type+"_data", builder, SearchData.class);
}
@Override
public List<SearchData> searchByKeyword(String keyword, String type, String relation) {
// 創(chuàng)建simple query string
SimpleQueryStringBuilder simpleQueryStringBuilder = new SimpleQueryStringBuilder(keyword);
// 根據(jù)title_text匹配
simpleQueryStringBuilder.field("title_text");
// 關(guān)鍵詞關(guān)系有和與或
if("OR".equals(relation) || "or".equals(relation)){
simpleQueryStringBuilder.defaultOperator(Operator.OR);
}else {
simpleQueryStringBuilder.defaultOperator(Operator.AND);
}
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(simpleQueryStringBuilder);
builder.sort("timestamp", SortOrder.DESC);
// 找出歷史排序最高的一條
CollapseBuilder collapseBuilder = new CollapseBuilder("title_keyword");
InnerHitBuilder innerHitBuilder = new InnerHitBuilder("top_hot");
innerHitBuilder.setSize(1);
SortBuilder sortBuilder = SortBuilders.fieldSort("hot_number").order(SortOrder.DESC);
innerHitBuilder.addSort(sortBuilder);
collapseBuilder.setInnerHits(innerHitBuilder);
builder.collapse(collapseBuilder);
builder.size(30);
return esUtil.search(type+"_data", builder, SearchData.class);
}
@Override
public List<SearchDetail> searchDetail(String type, int uId) {
return searchDetailMapper.queryDetail(type, uId);
}
}
EsUtil
ES實現(xiàn)工具類
@Component
public class EsUtil {
@Value("${es.host}")
private String host;
@Value("${es.port}")
private int port;
@Value("${es.scheme}")
private String scheme;
private static RestHighLevelClient client = null;
@PostConstruct
public void init() {
try {
if (client != null) {
client.close();
}
client = new RestHighLevelClient(RestClient.builder(new HttpHost(host, port, scheme)));
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
/**
* Description: 搜索
*
* @param index index
* @param builder 查詢參數(shù)
* @param c 結(jié)果類對象
* @return java.util.ArrayList
* @author fanxb
* @date 2019/7/25 13:46
*/
public <T> List<T> search(String index, SearchSourceBuilder builder, Class<T> c) {
SearchRequest request = new SearchRequest(index);
request.source(builder);
try {
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHit[] hits = response.getHits().getHits();
List<T> res = new ArrayList<>(hits.length);
for (SearchHit hit : hits) {
res.add(JSON.parseObject(hit.getSourceAsString(), c));
}
return res;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Controller
@Controller
@RequestMapping("/search")
public class searchController {
private static Logger logger = LoggerFactory.getLogger(IndexController.class);
@Resource
SearchService searchService;
@RequestMapping(value = "/searchTitle", method = RequestMethod.GET)
public String searchTitle(@RequestParam(value = "title") String title, @RequestParam(value = "type") String type ,ModelMap map){
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = userDetails.getUsername();
logger.info("用戶名=" + name + "&行為=歷史熱度查看:" + title + "|" + type);
map.addAttribute("titleData", title);
List<String> time = new ArrayList<>();
List<Integer> hotNumber = new ArrayList<>();
List<SearchData> hotSpots = searchService.searchByName(title, type);
for (SearchData hotSpot: hotSpots){
time.add(hotSpot.getTimestamp().replace('T', ' '));
hotNumber.add(hotSpot.getHotNumber());
}
map.addAttribute("timeData", time);
map.addAttribute("hotNumberData", hotNumber);
return "search/searchTitle";
}
@RequestMapping(value = "/searchDetail", method = RequestMethod.GET)
@ResponseBody
public SearchDetail searchDetail(@RequestParam(value = "uId") int uId, @RequestParam(value = "type") String type ,ModelMap map){
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = userDetails.getUsername();
logger.info("用戶名=" + name + "&行為=詳情查看:" + uId + "|" + type);
List<SearchDetail> searchDetails = searchService.searchDetail(type, uId);
return searchDetails.get(0);
}
@PostMapping(value = "/searchKeywords")
public String searchKeywords(@ModelAttribute("searchKeywords") @Valid SearchKeywords searchKeywords, BindingResult result, Model model){
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = userDetails.getUsername();
logger.info("用戶名=" + name + "&行為=關(guān)鍵詞查找:" + searchKeywords.getKeywords() + "|" + searchKeywords.getType() + "|" + searchKeywords.getRelation());
if (result.hasErrors()){
return "search/searchKeywords";
}
String type = getTypeName(searchKeywords.getType());
List<SearchData> searchHotSpotData = searchService.searchByKeyword(searchKeywords.getKeywords(), type, searchKeywords.getRelation());
model.addAttribute("searchHotSpotData", searchHotSpotData);
model.addAttribute("dataType", type);
return "search/searchKeywords";
}
@GetMapping(value = "/searchKeywords")
public String toSearchKeywords(Model model){
model.addAttribute("searchKeywords", new SearchKeywords());
return "search/searchKeywords";
}
private String getTypeName(String type) {
switch (type){
case "微博":
return "weibo";
case "爬客":
return "niuke";
case "虎撲":
return "hupu";
case "掘金":
return "juejin";
default:
return "weibo";
}
}
}