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