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);
}

 

posted @ 2019-12-23 15:27  门罗的魔术师  阅读(1421)  评论(0编辑  收藏  举报