八、黑马程序员酒店demo之全文检索后端代码

文末附上项目的下载地址

1、项目代码结构

这里暂不将涉及消息中间件的类放进去。因为是采用的是ES8的RestAPI,所以和黑马程序员的demo有些差距,相关注释没有那么详细。
在这里插入图片描述

2、mapperd代码

package com.toto.es.hotel.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.toto.es.hotel.pojo.Hotel;

public interface HotelMapper extends BaseMapper<Hotel> {
}

3、Hotel代码

package com.toto.es.hotel.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("tb_hotel")
public class Hotel {
    @TableId(type = IdType.INPUT)
    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 longitude;
    private String latitude;
    private String pic;
}

4、HotelDoc代码

package com.toto.es.hotel.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;

@Data
@NoArgsConstructor
public class HotelDoc {
    private String 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 Double distance;
    private Boolean isAD; // 是否是广告
    private List<String> suggestion;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId().toString();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
        if(this.business.contains("/")){
            String[] arr = this.business.split("/");
            this.suggestion = new ArrayList<>();
            this.suggestion.add(this.brand);
            Collections.addAll(this.suggestion, arr);
        }else if(this.business.contains("、")){
            String[] arr = this.business.split("、");
            this.suggestion = new ArrayList<>();
            this.suggestion.add(this.brand);
            Collections.addAll(this.suggestion, arr);
        }else{
            this.suggestion = Arrays.asList(this.brand, this.business);
        }

    }
}

5、PageResult代码

package com.toto.es.hotel.pojo;

import lombok.Data;

import java.util.List;

@Data
public class PageResult {
    private Long total;
    private List<HotelDoc> hotels;

    public PageResult() {
    }

    public PageResult(Long total, List<HotelDoc> hotels) {
        this.total = total;
        this.hotels = hotels;
    }
}

6、RequestParams代码

package com.toto.es.hotel.pojo;

import lombok.Data;

@Data
public class RequestParams {
    private String key;
    private Integer page;
    private Integer size;
    private String sortBy;

    private String city;
    private String brand;
    private String starName;
    private String minPrice;
    private String maxPrice;
    private String location;
}

7、IHotelService代码

package com.toto.es.hotel.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.toto.es.hotel.pojo.Hotel;
import com.toto.es.hotel.pojo.PageResult;
import com.toto.es.hotel.pojo.RequestParams;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public interface IHotelService extends IService<Hotel> {
    PageResult search(RequestParams params);

    Map<String, List<String>> filters(RequestParams params);
    Map<String, List<String>> filters2();
    List<String> getsuggestions(String prefix);

    void insertById(Long id);

    void deleteById(Long id);
}

8、HotelService代码(基于ES8调整)

package com.toto.es.hotel.service.impl;


import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.mapping.GeoPointProperty;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.DeleteResponse;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggest;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.util.NamedValue;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.toto.es.hotel.mapper.HotelMapper;
import com.toto.es.hotel.pojo.Hotel;
import com.toto.es.hotel.pojo.HotelDoc;
import com.toto.es.hotel.pojo.PageResult;
import com.toto.es.hotel.pojo.RequestParams;
import com.toto.es.hotel.service.IHotelService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private ElasticsearchClient client;
    
    @Override
    public PageResult search(RequestParams params) {
        SearchResponse<HotelDoc> response = null;
        try {
            // 1.创建请求
            SearchRequest.Builder builder = new SearchRequest.Builder();
            // 2.构造请求参数
            buildBasicQuery(builder, params);
            // 3.发送请求
            response = client.search(b -> builder, HotelDoc.class);
            return this.handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Map<String, List<String>> filters(RequestParams params) {
        try {
            // 1.创建请求
            SearchRequest.Builder builder = new SearchRequest.Builder();
            // 2.构造请求参数
            buildBasicQuery(builder, params);
            buildAggQuery(builder);
            // 3.发送请求
            SearchResponse<Map> response = client.search(b -> builder, Map.class);
            // 4.1解析结果
            Map<String, List<String>> result = new HashMap<>();
            // 4.1.1 获取品牌聚合结果
            List<String> brandList = getAggByName("brandAgg", response);
            //result.put("品牌", brandList);
            result.put("brand", brandList);
            // 4.1.2 获取城市聚合结果
            List<String> cityList = getAggByName("cityAgg", response);
            //result.put("城市", cityList);
            result.put("city", cityList);
            // 4.1.3 获取星级聚合结果
            List<String> starList = getAggByName("starAgg", response);
            //result.put("星级", starList);
            result.put("starName", starList);
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private List<String> getAggByName(String aggName, SearchResponse<Map> response){
        Map<String, Aggregate> aggregations = response.aggregations();
        Aggregate brandAgg = aggregations.get(aggName);
        Buckets<StringTermsBucket> buckets = brandAgg.sterms().buckets();
        List<String> brandList = new ArrayList<>();
        for (StringTermsBucket b : buckets.array()) {
            brandList.add(b.key().stringValue());
            //System.err.println(b.key().stringValue() + " : " + b.docCount());
        }
        return brandList;
    }

    private void buildAggQuery(SearchRequest.Builder builder){

        builder.index("hotel").size(0);

        builder.aggregations("brandAgg", a -> a.terms(t -> t.field("brand").size(100)));

        builder.aggregations("cityAgg", a -> a.terms(t -> t.field("city").size(100)));

        builder.aggregations("starAgg", a -> a.terms(t -> t.field("starName").size(100)));
    }

    private void buildBasicQuery(SearchRequest.Builder builder, RequestParams params) {
        // 2.1 指定索引和分页
        String key = params.getKey();
        int page = params.getPage();
        int size = params.getSize();
        String location = params.getLocation();

        builder.index("hotel");
        builder.from((page - 1) * size).size(size);

        // 2.2 构造查询条件(复核查询)
        BoolQuery.Builder boolQuery = QueryBuilders.bool();
        if(key == null || "".equals(key)){
            MatchAllQuery.Builder allQuery = QueryBuilders.matchAll();
            //builder.query(query -> query.matchAll(matchall -> allQuery));
            boolQuery.must(must -> must.matchAll(ma -> allQuery));
        }else{
            MatchQuery.Builder machQuery = QueryBuilders.match();
            machQuery.field("all").query(key);
            boolQuery.must(must -> must.match(m -> machQuery));
        }

        // 城市条件
        if(params.getCity() != null && !"".equals(params.getCity())){
            TermQuery.Builder termQuery = QueryBuilders.term();
            termQuery.field("city").value(params.getCity());
            boolQuery.filter(filter -> filter.term(t -> termQuery));
        }
        // 品牌条件
        if(params.getBrand() != null && !"".equals(params.getBrand())){
            TermQuery.Builder termQuery = QueryBuilders.term();
            termQuery.field("brand").value(params.getBrand());
            boolQuery.filter(filter -> filter.term(t -> termQuery));
        }
        // 星级条件
        if(params.getStarName() != null && !"".equals(params.getStarName())){
            TermQuery.Builder termQuery = QueryBuilders.term();
            termQuery.field("starName").value(params.getStarName());
            boolQuery.filter(filter -> filter.term(t -> termQuery));
        }
        // 价格条件
        if(params.getMinPrice() != null && params.getMaxPrice() != null){
            RangeQuery.Builder rangeQuery = QueryBuilders.range();
            rangeQuery.field("price").gte(JsonData.fromJson(params.getMinPrice())).lte(JsonData.fromJson(params.getMaxPrice()));
            boolQuery.filter(filter -> filter.range(r -> rangeQuery));
        }

        // 排序条件
        if(params.getSortBy() != null && !"".equals(params.getSortBy())){
            String sortBy = params.getSortBy();
            if("score".equals(sortBy)){
                builder.sort(sort -> sort.field(f -> f.field("score").numericType(FieldSortNumericType.Double).order(SortOrder.Desc)));
            }else if("price".equals(sortBy)){
                builder.sort(sort -> sort.field(f -> f.field("price").numericType(FieldSortNumericType.Double).order(SortOrder.Desc)));
            }
        }

        // 我附近的排序
        if(location != null && !"".equals(location)){
            String[] latlon = location.split(",");
            builder.sort(sort -> sort.geoDistance(geo -> geo.field("location").location(l -> l.latlon(ll -> ll.lat(Double.parseDouble(latlon[0].replace(" ",""))).lon(Double.parseDouble(latlon[1].replace(" ",""))))).unit(DistanceUnit.Kilometers).order(SortOrder.Asc)));
        }
        // 算分控制,置顶
        Query functionScoreQuery = QueryBuilders.functionScore(fun->fun.query(q->q.bool(b -> boolQuery)).functions(fsFunctions -> fsFunctions.filter(f -> f.term(t -> t.field("isAD").value(true))).weight(10D)).boostMode(FunctionBoostMode.Multiply));
        //builder.query(query -> query.bool(mach -> functionScoreQuery));
        builder.query(functionScoreQuery);
    }

    private PageResult handleResponse(SearchResponse<HotelDoc> response) {
        // System.err.println(response.hits().total().value());
        long total = response.hits().total().value();
        List<HotelDoc> hotels = new ArrayList<>();
        for (Hit<HotelDoc> doc : response.hits().hits()) {
            HotelDoc hotelDoc = doc.source();
            List<FieldValue> sortList = doc.sort();
            if(ObjectUtils.isNotEmpty(sortList)){
                hotelDoc.setDistance(sortList.get(0).doubleValue());
            }
            hotels.add(hotelDoc);
        }
        return new PageResult(total, hotels);
    }

    @Override
    public Map<String, List<String>> filters2() {
        try {
            // 1.创建请求
            SearchRequest.Builder builder = new SearchRequest.Builder();
            // 2.构造请求参数
            buildAggQuery(builder);
            // 3.发送请求
            SearchResponse<Map> response = client.search(b -> builder, Map.class);
            // 4.1解析结果
            Map<String, List<String>> result = new HashMap<>();
            // 4.1.1 获取品牌聚合结果
            List<String> brandList = getAggByName("brandAgg", response);
            result.put("品牌", brandList);
            // 4.1.2 获取城市聚合结果
            List<String> cityList = getAggByName("cityAgg", response);
            result.put("城市", cityList);
            // 4.1.3 获取星级聚合结果
            List<String> starList = getAggByName("starAgg", response);
            result.put("星级", starList);
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<String> getsuggestions(String prefix) {
        List<String> suggestions = new ArrayList<>();
        try {
            SearchResponse<Map> response = client.search(builder ->
                            builder.index("hotel")
                                    .suggest(s -> s.suggesters("suggestions",
                                            suggest -> suggest.prefix(prefix).completion(c -> c.field("suggestion").skipDuplicates(true).size(10)))),
                    Map.class);
            // 获取联想建议
            Map suggestMap = response.suggest();
            if (suggestMap != null) {
                List<?> suggestionList = (List<?>) suggestMap.get("suggestions");
                if (suggestionList != null) {
                    for (Object suggestionInfo : suggestionList) {
                        if (suggestionInfo instanceof Suggestion) {
                            Suggestion suggestion = (Suggestion) suggestionInfo;
                            if (suggestion.isCompletion()) {
                                CompletionSuggest completionSuggest = suggestion.completion();
                                List<CompletionSuggestOption> options = completionSuggest.options();
                                for (CompletionSuggestOption option : options) {
                                    suggestions.add(option.text());
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return suggestions;
    }

    @Override
    public void insertById(Long id) {
        try {
            Hotel hotel = getById(id);
            HotelDoc hotelDoc = new HotelDoc(hotel); // 使用实体,也可使用HashMap,还可以使用json
            IndexResponse response = client.index(builder -> builder.index("hotel").id(hotelDoc.getId()).document(hotelDoc));
            System.err.println(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteById(Long id) {
        try {
            DeleteResponse response = client.delete(builder -> builder.index("hotel").id(id.toString()));
            System.err.println(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

9、HotelController代码

package com.toto.es.hotel.web;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.toto.es.hotel.pojo.Hotel;
import com.toto.es.hotel.pojo.PageResult;
import com.toto.es.hotel.pojo.RequestParams;
import com.toto.es.hotel.service.IHotelService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("hotel")
public class HotelController {

    @Resource
    private IHotelService hotelService;

    @RequestMapping("/admin")
    public String login(){
        return "admin";
    }

    @GetMapping("/{id}")
    public Hotel queryById(@PathVariable("id") Long id){
        return hotelService.getById(id);
    }

    @PostMapping("/list")
    public PageResult hotelList(@RequestBody RequestParams params) throws IOException {
        return hotelService.search(params);
    }

    @PostMapping("/filters")
    public Map<String, List<String>> hotelfilters(@RequestBody RequestParams params) throws IOException {
        return hotelService.filters(params);
    }

    @GetMapping("/suggestion")
    public List<String> getSuggestions(@RequestParam("key") String prefix){
        return hotelService.getsuggestions(prefix);
    }

    @PostMapping
    public void saveHotel(@RequestBody Hotel hotel){
        hotelService.save(hotel);
    }

    @PutMapping()
    public void updateById(@RequestBody Hotel hotel){
        if (hotel.getId() == null) {
            throw new InvalidParameterException("id不能为空");
        }
        hotelService.updateById(hotel);
    }

    @DeleteMapping("/{id}")
    public void deleteById(@PathVariable("id") Long id) {
        hotelService.removeById(id);
    }

}

10、HotelDemoApplication代码(启动类)

package com.toto.es.hotel;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Slf4j
@MapperScan(basePackages = "com.toto.es.hotel.mapper")
@SpringBootApplication
public class HotelDemoApplication {

    public static void main(String[] args) throws UnknownHostException {
        ConfigurableApplicationContext application = SpringApplication.run(HotelDemoApplication.class,args);
        Environment env = application.getEnvironment();
        String ip = InetAddress.getLocalHost().getHostAddress();
        String port = env.getProperty("server.port");
        log.info("\n----------------------------------------------------------\n\t" +
                //"ElasticSearch Demo: \thttp://localhost" + ip + ":" + port + "\n" +
                "ElasticSearch Demo: \thttp://localhost:" + port + "\n" +
                "----------------------------------------------------------");
        openE("http://"+ ip +":" + port + "");
    }

    private static void openE(String url) {
        String runCmd = "cmd   /c   start " + url;
        Runtime run = Runtime.getRuntime();
        try {
            run.exec(runCmd);
        } catch (Exception e) {
            System.out.println("启动项目自动打开浏览器失败");
        }
    }

    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ElasticsearchClient slasticsearchClient() {
        BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
        credsProv.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "elastic"));

        RestClient restClient = RestClient.builder(HttpHost.create("http://127.0.0.1:9200"))
                .setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv))
                .build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }
}

11、application.yaml代码

server:
  port: 8089
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:23306/totograin?useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /
logging:
  level:
    com.toto: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
  type-aliases-package: com.toto.es.hotel.pojo

12、logback.xml代码

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                %d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="consoleLog" />
    </root>
</configuration>

项目下载地址

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Grain322

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值