springboot結合elasticsearch使用的一些例子


 

1.ElasticsearchTemplate的基本使用

package com.test.controller;

import com.test.dto.ProductDTO;
import com.test.entity.Product;
import com.test.utils.BeanUtils;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.elasticsearch.index.query.QueryBuilders.*;

/**
 * 復雜的操作使用 elasticsearchTemplate
 * 查詢具體組合可以查看 # QueryBuilders 類
 *
 * @author test
 * @date 2018/7/2.
 */
@RestController
@RequestMapping("/temp/product")
public class ProductTemplateController {

    private final ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    public ProductTemplateController(ElasticsearchTemplate elasticsearchTemplate) {
        this.elasticsearchTemplate = elasticsearchTemplate;
    }


    /**
     * 查詢所有
     */
    @GetMapping
    public Object findAll( ) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(matchAllQuery()).build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }

    /**
     * 分頁查詢
     */
    @GetMapping("/page")
    public Object findPage(@RequestParam(required = false, defaultValue = "1") Integer page,
                           @RequestParam(required = false, defaultValue = "10") Integer size) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(matchAllQuery())
                .withPageable(
                        PageRequest.of(page - 1, size, Sort.Direction.ASC, "price"))  //頁碼是從0 開始
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }

    /**
     * 單獨的排序
     */
    @GetMapping("/sort")
    public Object findSort( ) {
        FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort("price").order(SortOrder.ASC);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(matchAllQuery())
                .withSort(fieldSortBuilder)
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }

    /**
     * 根據id查詢
     */
    @GetMapping("/{id}")
    public Object findById(@PathVariable String id) {
        GetQuery getQuery = new GetQuery();
        getQuery.setId(id);
        return elasticsearchTemplate.queryForObject(getQuery, Product.class);
    }

    /**
     * 根據名稱查詢
     * 模糊查詢
     * <p>
     * {
     * id: "2",
     * name: "牙",
     * desc: "string",
     * price: 0,
     * producer: "string",
     * tags: [
     * "string"
     * ]
     * },
     * {
     * id: "1",
     * name: "牙膏",
     * desc: "string",
     * price: 0,
     * producer: "string",
     * tags: [
     * "string"
     * ]
     * },
     * {
     * id: "3",
     * name: "膏",
     * desc: "string",
     * price: 0,
     * producer: "string",
     * tags: [
     * "string"
     * ]
     * }
     * <p>
     * 查詢牙膏,會查詢出來所有
     */
    @GetMapping("/find-by-name")
    public Object findByName(@RequestParam String name) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(matchQuery("name", name)).build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }


    /**
     * 單字段包含所有輸入
     * 這里只會查詢出 包含整個name字段的,也就是牙膏只會查詢出上面的黑人牙膏
     */
    @GetMapping("/contain")
    public Object contain(@RequestParam String name) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchQuery("name", name)
                                .operator(Operator.AND)    //默認使用的是or
                                .minimumShouldMatch("100%")        //匹配率
                )
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }

    /**
     * 指定查詢的字段
     */
    @GetMapping("/source-filter")
    public Object sourceFilter( ) {

        //只顯示名字和價格
        String[] include = {"name", "price"};

        FetchSourceFilter fetchSourceFilter = new FetchSourceFilter(include, null);   //兩個參數分別是要顯示的和不顯示的

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withSourceFilter(fetchSourceFilter)
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }

    /**
     * bool查詢
     */
    @GetMapping("/bool")
    public Object bool( ) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withFilter(
                        boolQuery()
                                .must(matchQuery("price", 40))
                )
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }


    /**
     * filter查詢
     */
    @GetMapping("/filter")
    public Object filter(@RequestParam String filed, @RequestParam String filedName,
                         @RequestParam(required = false, defaultValue = "price") String rangeName,
                         @RequestParam(required = false, defaultValue = "0") String range) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withFilter(
                        boolQuery()
                                .must(matchQuery(filed, filedName))
                                .filter(
                                        rangeQuery(rangeName)
                                                .gte(range)
                                )
                )
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }


    /**
     * phrase查詢
     */
    @GetMapping("/phrase")
    public Object phrase(@RequestParam String filed, @RequestParam String filedName) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchPhraseQuery(filed, filedName)
                )
                .build();
        return elasticsearchTemplate.queryForList(searchQuery, Product.class);
    }

    /**
     * highlight查詢
     */
    @GetMapping("/highlight")
    public Object highlight(@RequestParam String filed, @RequestParam String filedName) {

        //高亮顯示的詞必須是查詢的詞
        final List<HighlightBuilder.Field> fields = new HighlightBuilder().field(filed).fields();

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchPhraseQuery(filed, filedName)
                )
                .withHighlightFields(fields.toArray(new HighlightBuilder.Field[0]))
                .build();

        //如果不想使用分頁的寫法
        return elasticsearchTemplate.query(searchQuery, (ResultsExtractor<Object>) response -> {
            List<ProductDTO> chunk = new ArrayList<>();
            for (SearchHit searchHit : response.getHits()) {
                //沒有數據
                if (response.getHits().getHits().length <= 0) {
                    return null;
                }
                //hit轉換成bean
                ProductDTO productDTO = BeanUtils.mapToBean(searchHit.getSourceAsMap(), new ProductDTO());
                productDTO.setHighlighted(searchHit.getHighlightFields().get("name").fragments()[0].toString());
                chunk.add(productDTO);
            }
            return chunk;
        });

//        return elasticsearchTemplate.queryForPage(searchQuery, Product.class, new SearchResultMapper() {
//            @Override
//            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
//                List<ProductDTO> chunk = new ArrayList<>();
//                for (SearchHit searchHit : response.getHits()) {
//                    if (response.getHits().getHits().length <= 0) {
//                        return null;
//                    }
//                    //hit轉換成bean
//                    ProductDTO productDTO = BeanUtils.mapToBean(searchHit.getSourceAsMap(), new ProductDTO());
//                    productDTO.setHighlighted(searchHit.getHighlightFields().get("name").fragments()[0].toString());
//                    chunk.add(productDTO);
//                }
//                if (chunk.size() > 0) {
//                    return new AggregatedPageImpl<>((List<T>) chunk);
//                }
//                return null;
//            }
//        });
    }

    /**
     * 根據id刪除
     */
    @DeleteMapping("/{id}")
    public void deleteById(@PathVariable String id) {
        elasticsearchTemplate.delete(Product.class, id);
    }


    /**
     * 根據指定字段刪除
     * 不是完全匹配,類似模糊匹配
     */
    @DeleteMapping("/delete-by-name/{name}")
    public void deleteByName(@PathVariable String name) {
        DeleteQuery deleteQuery = new DeleteQuery();
        deleteQuery.setQuery(termQuery("name", name));    //類似模糊刪除
        elasticsearchTemplate.delete(deleteQuery, Product.class);
    }

    /**
     * 更新
     */
    @PutMapping
    public void update(@RequestBody Product product) {
        IndexRequest indexRequest = new IndexRequest();
        Map<String, Object> map = BeanUtils.beanToMap(product);
        indexRequest.source(map, XContentType.JSON);    //更新整個實體
//        indexRequest.source("name",product.getName());  //更新指定的字段
        UpdateQuery updateQuery = new UpdateQueryBuilder().withId(product.getId())
                .withClass(Product.class).withIndexRequest(indexRequest).build();
        elasticsearchTemplate.update(updateQuery);
    }


    /**
     * 批量更新
     */
    @PutMapping("/batch")
    public void updateBatch(@RequestBody List<Product> products) {
        List<UpdateQuery> queries = new ArrayList<>();

        for (Product product : products) {
            IndexRequest indexRequest = new IndexRequest();
            Map<String, Object> map = BeanUtils.beanToMap(product);
            indexRequest.source(map, XContentType.JSON);    //更新整個實體
            UpdateQuery updateQuery = new UpdateQueryBuilder().withId(product.getId())
                    .withClass(Product.class).withIndexRequest(indexRequest).build();

            queries.add(updateQuery);
        }
        elasticsearchTemplate.bulkUpdate(queries);

    }
}

Product類:

package com.test.entity;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.Serializable;
import java.util.List;

/**
 * @author test
 * @date 2018/7/2.
 */
@Data
@Document(indexName = "goods", type = "product")   //要加,不然報空指針異常
public class Product implements Serializable {

    @Id
    private String id;

    private String name;

    private String desc;

    private int price;

    private String producer;

    /**
     * 聚合這些操作用單獨的數據結構(fielddata)緩存到內存里了,需要單獨開啟
     * 要么這里設置
     * 要着通過rest配置
     * PUT goods/_mapping/product
     * {
     *   "properties": {
     *     "tags": {
     *       "type":     "text",
     *       "fielddata": true
     *     }
     *   }
     * }
     */
    @Field(fielddata = true, type = FieldType.Text)
    private List<String> tags;

}

ProductDTO類

package com.test.dto;

import com.test.entity.Product;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author test
 * @date 2018/7/2.
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class ProductDTO extends Product {

    private String highlighted;
}

 

BeanUtils類

package com.test.utils;

import com.google.common.collect.Maps; import org.springframework.cglib.beans.BeanMap; import java.util.Map; /** * @author test * @date 2018/7/2. */ public class BeanUtils { /** * 將對象裝換為map */ public static <T> Map<String, Object> beanToMap(T bean) { Map<String, Object> map = Maps.newHashMap(); if (bean != null) { BeanMap beanMap = BeanMap.create(bean); for (Object key : beanMap.keySet()) { map.put(key + "", beanMap.get(key)); } } return map; } /** * 將map裝換為javabean對象 */ public static <T> T mapToBean(Map<String, Object> map, T bean) { BeanMap beanMap = BeanMap.create(bean); beanMap.putAll(map); return bean; } }

 

 

2.ElasticsearchTemplate的統計用法

package com.test.controller;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;

/**
 * 練習題
 * 需求1: 對名稱中包含 牙膏 的商品,計算每個tag下的商品數量
 * 需求2:先分組,再算每組的平均值,計算每個tag下的商品的平均價格
 * 需求3: 計算每個tag下的商品的平均價格,並且按照平均價格降序排序
 * 需求4: 按照指定的價格范圍區間進行分組,然后在每組內再按照tag進行分組,最后再計算每組的平均價格
 *
 * @author test
 * @date 2018/7/2.
 */
@RestController
@RequestMapping("/practice")
public class PracticeController {

    private final ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    public PracticeController(ElasticsearchTemplate elasticsearchTemplate) {
        this.elasticsearchTemplate = elasticsearchTemplate;
    }

    @GetMapping("/four")
    public Object four( ) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchAllQuery()
                )
                .addAggregation(
                        AggregationBuilders.range("range_by_price")
                                .field("price")
                                .addRange(0, 10)
                                .addRange(10, 20)
                                .addRange(20, 30)
                                .addRange(30, 100)
                                .subAggregation(
                                        AggregationBuilders.terms("group_by_tags")
                                                .field("tags")
                                                //.order(Terms.Order.aggregation("price", false))
                                                .subAggregation(
                                                        AggregationBuilders.avg("price").field("price"))
                                )
                )
                .build();
        return elasticsearchTemplate.query(searchQuery, SearchResponse::toString);
    }


    @GetMapping("/three")
    public Object three( ) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchAllQuery()
                )
                .addAggregation(
                        AggregationBuilders.terms("group_by_tags")
                                .field("tags")
                                //.order(Terms.Order.aggregation("price", false))
                                .subAggregation(
                                        AggregationBuilders.avg("price").field("price"))
                )
                .build();
        return elasticsearchTemplate.query(searchQuery, SearchResponse::toString);
    }


    @GetMapping("/two")
    public Object two( ) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchAllQuery()
                )
                .addAggregation(
                        AggregationBuilders.terms("group_by_tags")
                                .field("tags")
                                .subAggregation(
                                        AggregationBuilders.avg("price").field("price"))
                )
                .build();
        //這里直接返回es原封結果到前端,實際根據需求取值
        return elasticsearchTemplate.query(searchQuery, SearchResponse::toString);

    }


    @GetMapping("/one")
    public Object one(@RequestParam String name) {
        Map<String, Long> map = new HashMap<>();

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(
                        matchQuery("name", name)
                )
                .addAggregation(
                        AggregationBuilders.terms("all_tags")
                                .field("tags")
                )
                .build();

        Aggregations aggregations = elasticsearchTemplate.query(searchQuery, SearchResponse::getAggregations);

        Aggregation aggregation = aggregations.asMap().get("all_tags");
        if (aggregation == null) {
            return null;
        }

        StringTerms modelTerms = (StringTerms) aggregation;

        for (Terms.Bucket actionTypeBucket : modelTerms.getBuckets()) {
            //actionTypeBucket.getKey().toString()聚合字段的相應名稱,actionTypeBucket.getDocCount()相應聚合結果
            map.put(actionTypeBucket.getKey().toString(), actionTypeBucket.getDocCount());
        }
        return map;

    }


}

 

3.繼承Repository的用法

package com.test.controller;

import com.test.entity.Product;
import com.test.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 使用productRepository 做基本的增刪改查
 *
 * @author test
 * @date 2018/7/2.
 */
@RestController
@RequestMapping("/base/product")
public class ProductBaseController {

    private final ProductRepository productRepository;

    @Autowired
    public ProductBaseController(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @PostMapping
    public void add(@RequestBody Product product) {
        productRepository.save(product);
    }

    @GetMapping
    public Object findAll( ) {
        return productRepository.findAll();      //根據自己的需求然后再做數據處理
    }

    @GetMapping("/{id}")
    public Object findById(@PathVariable String id) {
        return productRepository.findById(id);
    }

    @GetMapping("/find-by-name/{name}")
    public Object findByName(@PathVariable String name) {
        return productRepository.findByName(name);
    }

    @DeleteMapping("/{id}")
    public void deleteById(@PathVariable String id) {
        productRepository.deleteById(id);
    }


    @DeleteMapping("/delete-by-name/{name}")
    public void deleteByName(@PathVariable String name) {
        productRepository.deleteByName(name);
    }
}

 

ProductRepository類:

package com.test.repository;

import com.test.entity.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;

import java.util.List;

/**
 * @author test
 * @date 2018/7/2.
 */
public interface ProductRepository extends ElasticsearchCrudRepository<Product, String> {

    void deleteByName(String name);

    List<Product> findByName(String name);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM