關(guān)鍵詞搜索
需求:根據(jù)文字搜索,也可以選擇標(biāo)簽搜索
思路:用bool查詢,先根據(jù)關(guān)鍵詞查詢?nèi)?,再根?jù)標(biāo)簽過濾。
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
@Override
public PageResult search(RequestParams params) throws IOException {
SearchRequest request = new SearchRequest("hotel");
// 關(guān)鍵字搜索
QueryBuilder query = buildBasicQuery(params);
request.source().query(query);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
return extracted(response);
}
private static QueryBuilder buildBasicQuery(RequestParams params) {
String key = params.getKey();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// query
// 關(guān)鍵字搜索
if ("".equals(key) || key==null){
boolQuery.must(QueryBuilders.matchAllQuery());
}else {
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
// 過濾條件
if (params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
if (!("".equals(params.getCity()) || params.getCity()==null)){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
if (!("".equals(params.getBrand()) || params.getBrand()==null)){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
if (!("".equals(params.getStarName()) || params.getStarName()==null)){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
return boolQuery;
}
private static PageResult extracted(SearchResponse response) {
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> list = Arrays.stream(hits).map(item -> {
String jsonStr = item.getSourceAsString();
Object[] sortValues = item.getSortValues();
HotelDoc hotelDoc = JSONObject.parseObject(jsonStr, HotelDoc.class);
return hotelDoc;
}).collect(Collectors.toList());
return new PageResult(total, list);
}
}
分頁排序
需求:實現(xiàn)分頁排序
思路:分頁跟排序是單獨的功能,可以根據(jù)選項排好序再分頁
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
private static Boolean isLocation = false;
@Override
public PageResult search(RequestParams params) throws IOException {
SearchRequest request = new SearchRequest("hotel");
// 關(guān)鍵字搜索
QueryBuilder query = buildBasicQuery(params);
request.source().query(query);
// 分頁
int page = params.getPage();
int size = params.getSize();
request.source().from((page-1)*size).size(size);
// 排序
if (!("default".equals(params.getSortBy()))){
FieldSortBuilder sortBy = SortBuilders.fieldSort(params.getSortBy());
if ("price".equals(params.getSortBy())){
sortBy.order(SortOrder.ASC);
}else {
sortBy.order(SortOrder.DESC);
}
request.source().sort(sortBy);
}
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
return extracted(response);
}
private static QueryBuilder buildBasicQuery(RequestParams params) {
String key = params.getKey();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// query
// 關(guān)鍵字搜索
if ("".equals(key) || key==null){
boolQuery.must(QueryBuilders.matchAllQuery());
}else {
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
// 過濾條件
if (params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
if (!("".equals(params.getCity()) || params.getCity()==null)){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
if (!("".equals(params.getBrand()) || params.getBrand()==null)){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
if (!("".equals(params.getStarName()) || params.getStarName()==null)){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
return boolQuery;
}
private static PageResult extracted(SearchResponse response) {
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> list = Arrays.stream(hits).map(item -> {
String jsonStr = item.getSourceAsString();
Object[] sortValues = item.getSortValues();
HotelDoc hotelDoc = JSONObject.parseObject(jsonStr, HotelDoc.class);
return hotelDoc;
}).collect(Collectors.toList());
return new PageResult(total, list);
}
}
距離顯示
要求:點擊獲取位置后,根據(jù)距離顯示酒店,且要顯示距離
思路:先判斷有沒有點擊地圖,如果攜帶了位置的請求就代表點擊了地圖,就需要根據(jù)坐標(biāo)查詢,且規(guī)定坐標(biāo)先查找,可以保證在sort里value[0]就是坐標(biāo)值
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
private static Boolean isLocation = false;
@Override
public PageResult search(RequestParams params) throws IOException {
SearchRequest request = new SearchRequest("hotel");
// 關(guān)鍵字搜索
QueryBuilder query = buildBasicQuery(params);
request.source().query(query);
// 分頁
int page = params.getPage();
int size = params.getSize();
request.source().from((page-1)*size).size(size);
// 排序
String location = params.getLocation();
// 坐標(biāo)排序
if (!("".equals(location) || location==null)){
GeoDistanceSortBuilder locationSort = SortBuilders.geoDistanceSort("location",new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS);
request.source().sort(locationSort);
FieldSortBuilder priceSort = SortBuilders.fieldSort("price").order(SortOrder.ASC);
request.source().sort(priceSort);
isLocation =true;
}
// price/defult/socre排序
if (!("default".equals(params.getSortBy()))){
FieldSortBuilder sortBy = SortBuilders.fieldSort(params.getSortBy());
if ("price".equals(params.getSortBy())){
sortBy.order(SortOrder.ASC);
}else {
sortBy.order(SortOrder.DESC);
}
request.source().sort(sortBy);
}
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
return extracted(response);
}
private static QueryBuilder buildBasicQuery(RequestParams params) {
String key = params.getKey();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// query
// 關(guān)鍵字搜索
if ("".equals(key) || key==null){
boolQuery.must(QueryBuilders.matchAllQuery());
}else {
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
// 過濾條件
if (params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
if (!("".equals(params.getCity()) || params.getCity()==null)){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
if (!("".equals(params.getBrand()) || params.getBrand()==null)){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
if (!("".equals(params.getStarName()) || params.getStarName()==null)){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
return boolQuery;
}
private static PageResult extracted(SearchResponse response) {
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> list = Arrays.stream(hits).map(item -> {
String jsonStr = item.getSourceAsString();
Object[] sortValues = item.getSortValues();
HotelDoc hotelDoc = JSONObject.parseObject(jsonStr, HotelDoc.class);
if (sortValues.length>0 && isLocation){
Object sortValue = sortValues[0];
hotelDoc.setDistance(sortValue);
}
return hotelDoc;
}).collect(Collectors.toList());
return new PageResult(total, list);
}
}
廣告置頂
要求:廣告置頂,有些廣告需要在展示搜索結(jié)果的時候排在前面
思路:利用functionScore來做,但是functionScore是用不到bool的只能用普通的查詢,在文檔中維護一個字段叫isAD,在functions中過濾出isAD的文檔,重新分配權(quán)重
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
@Autowired
private RestHighLevelClient client;
private static Boolean isLocation = false;
@Override
public PageResult search(RequestParams params) throws IOException {
SearchRequest request = new SearchRequest("hotel");
// 關(guān)鍵字搜索
QueryBuilder query = buildBasicQuery(params);
request.source().query(query);
// 分頁
int page = params.getPage();
int size = params.getSize();
request.source().from((page-1)*size).size(size);
// 排序
String location = params.getLocation();
if (!("".equals(location) || location==null)){
GeoDistanceSortBuilder locationSort = SortBuilders.geoDistanceSort("location",new GeoPoint(location)).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS);
request.source().sort(locationSort);
FieldSortBuilder priceSort = SortBuilders.fieldSort("price").order(SortOrder.ASC);
request.source().sort(priceSort);
isLocation =true;
}
if (!("default".equals(params.getSortBy()))){
FieldSortBuilder sortBy = SortBuilders.fieldSort(params.getSortBy());
if ("price".equals(params.getSortBy())){
sortBy.order(SortOrder.ASC);
}else {
sortBy.order(SortOrder.DESC);
}
request.source().sort(sortBy);
}
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
return extracted(response);
}
private static QueryBuilder buildBasicQuery(RequestParams params) {
String key = params.getKey();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// query
// 關(guān)鍵字搜索
if ("".equals(key) || key==null){
boolQuery.must(QueryBuilders.matchAllQuery());
}else {
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
// 過濾條件
if (params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
if (!("".equals(params.getCity()) || params.getCity()==null)){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
if (!("".equals(params.getBrand()) || params.getBrand()==null)){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
if (!("".equals(params.getStarName()) || params.getStarName()==null)){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 廣告置頂
return QueryBuilders.functionScoreQuery(boolQuery, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAD",true),
ScoreFunctionBuilders.weightFactorFunction(10)
)
}).boostMode(CombineFunction.MULTIPLY);
}
private static PageResult extracted(SearchResponse response) {
SearchHits searchHits = response.getHits();
long total = searchHits.getTotalHits().value;
SearchHit[] hits = searchHits.getHits();
List<HotelDoc> list = Arrays.stream(hits).map(item -> {
String jsonStr = item.getSourceAsString();
Object[] sortValues = item.getSortValues();
HotelDoc hotelDoc = JSONObject.parseObject(jsonStr, HotelDoc.class);
if (sortValues.length>0 && isLocation){
Object sortValue = sortValues[0];
hotelDoc.setDistance(sortValue);
}
return hotelDoc;
}).collect(Collectors.toList());
return new PageResult(total, list);
}
}
標(biāo)簽聚合
需求:從后臺文檔中獲取實際數(shù)據(jù),傳入前端頁面顯示,且會動態(tài)變化,比如:選擇了上海,就展示在上海的品牌,而不在上海的品牌就不顯示
思路:先根據(jù)用戶條件搜索文檔,按照類別聚合
@Override
public Map<String, List<String>> filters(RequestParams requestParams){
Map<String, List<String>> listMap = null;
try {
SearchRequest request = new SearchRequest("hotel");
// 查詢條件構(gòu)建
searchAggs(request,requestParams);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
Aggregations aggregations = response.getAggregations();
listMap = new HashMap<>();
listMap.put("city",getList(aggregations, "getCity"));
listMap.put("brand",getList(aggregations, "getBrand"));
listMap.put("starName",getList(aggregations, "getStarName"));
} catch (IOException e) {
throw new RuntimeException(e);
}
return listMap;
}
private void searchAggs(SearchRequest request,RequestParams requestParams) {
QueryBuilder queryBuilder = buildBasicQuery(requestParams);
request.source().query(queryBuilder);
// 查詢品牌
request.source().aggregation(
AggregationBuilders
.terms("getBrand")
.field("brand")
.size(20));
// 查詢城市
request.source().aggregation(
AggregationBuilders
.terms("getCity")
.field("city")
.size(20));
// 查詢星級
request.source().aggregation(
AggregationBuilders
.terms("getStarName")
.field("starName")
.size(20));
}
private List<String> getList(Aggregations aggregations, String name) {
Terms getBrand = aggregations.get(name);
List<String> list = new ArrayList<>();
for (Terms.Bucket bucket : getBrand.getBuckets()) {
String key = bucket.getKeyAsString();
list.add(key);
}
return list;
}
private QueryBuilder buildBasicQuery(RequestParams params) {
String key = params.getKey();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// query
// 關(guān)鍵字搜索
if ("".equals(key) || key==null){
boolQuery.must(QueryBuilders.matchAllQuery());
}else {
boolQuery.must(QueryBuilders.matchQuery("all",key));
}
// 過濾條件
if (params.getMinPrice()!=null && params.getMaxPrice()!=null){
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
}
if (!("".equals(params.getCity()) || params.getCity()==null)){
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
if (!("".equals(params.getBrand()) || params.getBrand()==null)){
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
if (!("".equals(params.getStarName()) || params.getStarName()==null)){
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 廣告置頂
return QueryBuilders.functionScoreQuery(boolQuery, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
QueryBuilders.termQuery("isAD",true),
ScoreFunctionBuilders.weightFactorFunction(10)
)
}).boostMode(CombineFunction.MULTIPLY);
}
自動補全
需求:要求用戶輸入拼音,可以根據(jù)輸入的拼音自動補全,例如:輸入s自動補全 三,四,蛇
思路:利用分詞器,在新增文檔的時候,根據(jù)拼音分詞器把品牌,地址做拆分,用戶輸入受字母的時候,后臺根據(jù)首字符檢索字段的拼音,要設(shè)計好拼音分詞器的拆分條件
/**
* 自動補全
* @param key 補全前綴
* @return 補全數(shù)組
*/
@Override
public List<String> suggestion(String key) {
List<String> collect;
try {
SearchRequest request = new SearchRequest("hotel");
request.source().suggest(new SuggestBuilder()
.addSuggestion("mySuggestion",
SuggestBuilders
.completionSuggestion("suggestion")
.prefix(key)
.skipDuplicates(true)
.size(10)));
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
CompletionSuggestion mySuggestion = response.getSuggest().getSuggestion("mySuggestion");
List<CompletionSuggestion.Entry.Option> options = mySuggestion.getOptions();
collect = options.stream().map(item -> item.getText().string()).collect(Collectors.toList());
System.err.println(collect);
} catch (IOException e) {
throw new RuntimeException(e);
}
return collect;
}
構(gòu)建索引庫比較關(guān)鍵,這里把用不到的字段省略了,不然太冗余了
#hotel
PUT /hotel
{
"mappings":{
"properties":{
"address":{
"type": "keyword",
"copy_to": "all"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword",
"copy_to": "all"
},
"name":{
"type": "text",
"analyzer": "text_analyzere",
"search_analyzer": "ik_smart",
"copy_to": "all"
},
# all字段,用戶確認搜索的時候用到的。analyzer代表新增文檔的時候,按照text_analyzere這個分詞器,根據(jù)最大粒度拆分,且用到了py拼音分詞器。
# search_analyzer,用戶確認搜索的時候用這個分詞器去拆分用戶的輸入信息
"all":{
"type": "text",
"analyzer": "text_analyzere",
"search_analyzer": "ik_max_word"
},
#suggestion專門為自動補全做的字段,類型只能是completion,completion是一個數(shù)組,數(shù)組內(nèi)的字段只能是keyword不能拆分,因為如果拆分的話就有很多個值出來,keyword保證不可拆分,那么在同一個文檔內(nèi)這個suggestion是固定的,比如completion由品牌跟地址組成,品牌=如家,地址=北京,那這個分詞器拆出來只能是:rujia/rj,beijing/bj,
"suggestion":{
"type": "completion",
"analyzer": "completion_analyzere"
}
}
},
"settings": {
"analysis": {
"analyzer": {
#text的分詞器,新增文檔的時候有些text字段可能搜索的時候需要用到。
"text_analyzere":{
"tokenizer":"ik_max_word",
"filter":"py"
},
#completion的分詞器
"completion_analyzere":{
"tokenizer":"keyword",
"filter":"py"
}
},
"filter": {
"type": "pinyin", //類型
"keep_full_pinyin": false,//當(dāng)啟用這個選項,如: 劉德華 >[ liu , de , hua ),默認值:真的
"keep_joined_full_pinyin": true,//當(dāng)啟用此選項時,例如: 劉德華 >[ liudehua ],默認:false
"keep_original": true,//當(dāng)啟用此選項時,將保留原始輸入,默認值:false
"limit_first_letter_length": 16,//set first_letter結(jié)果的最大長度,默認值:16
"remove_duplicated_term": true,//當(dāng)此選項啟用時,重復(fù)項將被刪除以保存索引,例如: de的 > de ,默認:false,注:職位相關(guān)查詢可能會受到影響
"none_chinese_pinyin_tokenize" :false //非中國字母分解成單獨的拼音詞如果拼音,默認值:true,如:liu , de , hua , a , li , ba , ba , 13 , zhuang , han ,注意: keep_none_chinese 和 keep_none_chinese_together 應(yīng)該啟用
}
}
}
}
數(shù)據(jù)同步
利用mq實現(xiàn)數(shù)據(jù)同步
生產(chǎn)者文章來源:http://www.zghlxwxcb.cn/news/detail-408471.html
@PostMapping
public void saveHotel(@RequestBody Hotel hotel){
Long i = 1L;
hotel.setId(i);
hotelService.save(hotel);
String exchangeName = "topic.hotel";
String key = "hotel.insert";
rabbitTemplate.convertAndSend(exchangeName,key,hotel.getId());
System.err.println("發(fā)送成功--->新增");
}
消費者文章來源地址http://www.zghlxwxcb.cn/news/detail-408471.html
@Bean
public Queue queueInsert(){
return new Queue("topic.insert.queue",true);
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topic.hotel");
}
@Bean
public Queue queueDelete(){
return new Queue("topic.delete.queue",true);
}
@Bean
public Binding bindingTopicBuilder(TopicExchange topicExchange,Queue queueInsert){
return BindingBuilder.bind(queueInsert).to(topicExchange).with("hotel.insert");
}
@Bean
public Binding bindingTopicBuilder2(TopicExchange topicExchange,Queue queueDelete){
return BindingBuilder.bind(queueDelete).to(topicExchange).with("hotel.delete");
}
@Component
public class ListenMq {
@Autowired
private IHotelService hotelService;
@RabbitListener(queues = "topic.insert.queue")
public void listenInsert(Long id){
hotelService.updateById(id);
}
@RabbitListener(queues = "topic.delete.queue")
public void listenDelete(Long id){
hotelService.deleteById(id);
}
}
@Override
public void updateById(Long id) {
try {
IndexRequest request = new IndexRequest("hotel").id(id.toString());
Hotel hotel = this.getById(id);
HotelDoc hotelDoc = new HotelDoc(hotel);
String jsonString = JSON.toJSONString(hotelDoc);
request.source(jsonString, XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
System.err.println("新增一條數(shù)據(jù)");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
到了這里,關(guān)于ES實戰(zhàn) | 黑馬旅游案例的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!