spring-boot整合elasticsearch,以及常用功能 中文分詞高亮,按照地理位置排序

  1. 引入pom
  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  </dependency>
  1. application.yml 添加es的相關配置
spring:
  data:
    elasticsearch:
      cluster-nodes: localhost:9300
      cluster-name: es_cluster
  1. 創建實體類,
    • 中文分詞,需要設置@Document裏面的 type = "article",在對應的字段上添加@Field(analyzer = "ik_max_word", searchAnalyzer = "ik_max_word", type = FieldType.Text)註解
    • 根據地理位置排序,需要添加@GeoPointField註解GeoPoint類型的對象
@Data
@Document(indexName = "address", type = "article")
public class Address implements Serializable {
    @Id
    private String id;

    @Field(analyzer = "ik_max_word", searchAnalyzer = "ik_max_word", type = FieldType.Text)
    private String location;

    @GeoPointField
    private GeoPoint geoPoint;

    private String geo;

    public GeoPoint getGeoPoint(){
        String[] split = geo.split(",");
        return new GeoPoint(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
    }
}

  1. elasticsearch常用的一些操作
    @Test
    public void save() {
        Address address = new Address();
        address.setId(UUID.randomUUID().toString());
        address.setLocation("崑山市前進西路1801號");
        address.setGeo("31.386311,120.916435");
        addressDocumentRepository.save(address);
    }
    /**
     * 批量保存數據
     */
    @Test
    public void saveAll() throws IOException {
        File geo = ResourceUtils.getFile("classpath:geo.json");
        String content = new String(Files.readAllBytes(geo.toPath()));
        List<Address> list = JSONUtil.toList(JSONUtil.parseArray(content), Address.class);
        addressDocumentRepository.saveAll(list);
    }
    /**
     * 獲取所有數據
     */
    @Test
    public void findAll() {
        Iterable<Address> all = addressDocumentRepository.findAll();
        for (Address address : all) {
            System.out.println(JSONUtil.toJsonStr(address));
        }
    }
    /**
     * 分頁查詢
     */
    @Test
    public void page() {
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.must(QueryBuilders.termQuery("location", "周莊"));
        Pageable pageable = PageRequest.of(0, 5);
        Page<Address> search = addressDocumentRepository.search(qb, pageable);
        System.out.println("==" + search.getTotalElements());
        System.out.println("==" + search.getTotalPages());
        search.getContent().forEach(address -> System.out.println(JSONUtil.toJsonStr(address)));
    }
    @Test
    public void ikSearch() {
        String searchField = "location";
        // 構造查詢條件,使用標準分詞器.
        QueryBuilder matchQuery = QueryBuilders.boolQuery()
                .must(QueryBuilders.multiMatchQuery("崑山", searchField).analyzer("ik_max_word")
                        .operator(Operator.OR));

        int pageNo = 1;
        int pageSize = 10;

        // 設置高亮,使用默認的highlighter高亮器
        HighlightBuilder highlightBuilder = createHighlightBuilder(searchField);

        // 設置查詢字段
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch("address")
                .setQuery(matchQuery)
                .highlighter(highlightBuilder)
                .setFrom((pageNo - 1) * pageSize)
                // 設置一次返回的文檔數量,最大值:10000
                .setSize(pageNo * pageSize)
                .get();

        // 返回搜索結果
        SearchHits hits = response.getHits();

        List<Address> hitList = getHitList(hits);

        System.out.println(hits.getTotalHits());
        hitList.forEach(address -> {
            System.out.println(JSONUtil.toJsonStr(address));
        });
    }

    /**
     * 構造高亮器
     */
    private HighlightBuilder createHighlightBuilder(String... fieldNames) {
        // 設置高亮,使用默認的highlighter高亮器
        HighlightBuilder highlightBuilder = new HighlightBuilder()
                // .field("productName")
                .preTags("<span style='color:red'>")
                .postTags("</span>");

        // 設置高亮字段
        for (String fieldName : fieldNames) {
            highlightBuilder.field(fieldName);
        }

        return highlightBuilder;
    }


    /**
     * 處理高亮結果
     */
    private List<Address> getHitList(SearchHits hits) {
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String, Object> map;
        for (SearchHit searchHit : hits) {
            // 處理源數據
            map = new HashMap<>(searchHit.getSourceAsMap());
            // 處理高亮數據
            Map<String, Object> hitMap = new HashMap<>();
            searchHit.getHighlightFields().forEach((k, v) -> {
                StringBuilder high = new StringBuilder();
                for (Text text : v.getFragments()) {
                    high.append(text.string());
                }
                hitMap.put(v.getName(), high.toString());
            });
            map.putAll(hitMap);
            list.add(map);
        }
        return JSON.parseArray(JSON.toJSONString(list), Address.class);
    }

    /**
     * 按照位置遠近搜索
     */
    @Test
    public void sortByLocation() {
        Pageable pageable = PageRequest.of(0, 20);
        //搜索字段爲 location
        GeoDistanceQueryBuilder geoBuilder = new GeoDistanceQueryBuilder("geoPoint");
        geoBuilder.point(31.186245, 121.037991);//指定從哪個位置搜索
        geoBuilder.distance(100, DistanceUnit.KILOMETERS);//指定搜索多少km

        //距離排序
        GeoDistanceSortBuilder sortBuilder = new GeoDistanceSortBuilder("geoPoint", 31.186245, 121.037991);
        sortBuilder.order(SortOrder.ASC);//升序
        sortBuilder.unit(DistanceUnit.METERS);

        //構造查詢器
        NativeSearchQueryBuilder qb = new NativeSearchQueryBuilder()
                .withPageable(pageable)
                .withFilter(geoBuilder)
                .withSort(sortBuilder);

        //可添加其他查詢條件
        //qb.must(QueryBuilders.matchQuery("address", address));
        Page<Address> page = addressDocumentRepository.search(qb.build());
        List<Address> list = page.getContent();
        list.forEach(l -> {
            double calculate = GeoDistance.PLANE.calculate(l.getGeoPoint().getLat(), l.getGeoPoint().getLon(), 31.186245, 121.037991, DistanceUnit.METERS);
            System.out.println(JSONUtil.toJsonStr(l));
            System.out.println("距離" + (int) calculate + "m");
        });

    }

    @Test
    public void delIndex() {
        elasticsearchTemplate.deleteIndex("address");
    }

    @Test
    public void deleteAll() {
        addressDocumentRepository.deleteAll();
    }

    /**
     * 通過ClassLoader獲取resources下文件
     */
    @Test
    public void test2() throws IOException {
        String fileName = "geo.json";
        ClassLoader classLoader = EsTest.class.getClassLoader();
        File file = new File(Objects.requireNonNull(classLoader.getResource(fileName)).getFile());
        String content = new String(Files.readAllBytes(file.toPath()));
        System.out.println(content);
    }


    /**
     * 通過ResourceUtils獲取resources下文件
     */
    @Test
    public void test1() throws IOException {
        File geo = ResourceUtils.getFile("classpath:geo.json");
        String content = new String(Files.readAllBytes(geo.toPath()));
        System.out.println(content);
    }
  1. 問題總結
    在多模塊情況下,會掃描不到bean,需要添加一個註解
@EnableElasticsearchRepositories(basePackages = "com.good.xxx.xxx")

文章用到的項目源碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章