SpringBoot 2.X 整合 Elasticsearch

SpringBoot 整合 Elasticsearch

官方文档介绍

https://spring.io/projects/spring-data-elasticsearch
image.png

ElasticSearch安装版本,项目集成版本对应关系图


image.png

Spring Data Elasticsearch

Spring Data Elasticsearch是Spring提供的一种以Spring Data风格来操作数据存储的方式,它可以避免编写大量的样板代码。

常用注解

@Document 标示映射到Elasticsearch文档上的领域对象

@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface Document {

    /**
     * 索引库名次,mysql中数据库的概念
     * Name of the Elasticsearch index.
     * <ul>
     * <li>Lowercase only</li>
     * <li><Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, #/li>
     * <li>Cannot start with -, _, +</li>
     * <li>Cannot be . or ..</li>
     * <li>Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit
     * faster)</li>
     * </ul>
     */
    String indexName();

    /**
     * Use server-side settings when creating the index.
     */
    boolean useServerConfiguration() default false;

    /**
    *默认分片数
     * Number of shards for the index {@link #indexName()}. Used for index creation. <br/>
     * With version 4.0, the default value is changed from 5 to 1 to reflect the change in the default settings of
     * Elasticsearch which changed to 1 as well in Elasticsearch 7.0.
     */
    short shards() default 1;

    /**
    * 默认副本数量
     * Number of replicas for the index {@link #indexName()}. Used for index creation.
     */
    short replicas() default 1;

    /**
     * Refresh interval for the index {@link #indexName()}. Used for index creation.
     */
    String refreshInterval() default "1s";

    /**
     * Index storage type for the index {@link #indexName()}. Used for index creation.
     */
    String indexStoreType() default "fs";

    /**
     * Configuration whether to create an index on repository bootstrapping.
     */
    boolean createIndex() default true;

    /**
     * Configuration of version management.
     */
    VersionType versionType() default VersionType.EXTERNAL;
}

@Id 表示是文档的id,文档可以认为是mysql中表行的概念

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Id {
}

@Field

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@Inherited
public @interface Field {
    //文档中字段的类型
    FieldType type() default FieldType.Auto;
    //是否建立倒排索引
    boolean index() default true;

    DateFormat format() default DateFormat.none;

    String pattern() default "";
    //是否进行存储
    boolean store() default false;

    boolean fielddata() default false;

    String searchAnalyzer() default "";
    //分词器名次
    String analyzer() default "";

    String normalizer() default "";

    String[] ignoreFields() default {};

    boolean includeInParent() default false;

    String[] copyTo() default {};
}
为文档自动指定元数据类型
public enum FieldType {
    Text,//会进行分词并建了索引的字符类型
    Integer,
    Long,
    Date,
    Float,
    Double,
    Boolean,
    Object,
    Auto,//自动判断字段类型
    Nested,//嵌套对象类型
    Ip,
    Attachment,
    Keyword;//不会进行分词建立索引的类型

    private FieldType() {
    }
}



在SpringBoot data Elasticsearch 操作方式有2种:一种是继承ElasticsearchRepository接口,另一种是ElasticsearchTemplate.

第一种方式:继承ElasticsearchRepository<T, ID extends Serializable>接口
image.png

image.png

先创建实体类

/**
 * 搜索商品的信息
 */
@Data
@EqualsAndHashCode(callSuper = false)
//indexName 索引库名次,mysql中数据库的概念
//type 类型
//shards 默认分片数
//replicas 默认副本数量
@Document(indexName = "pms",type = "product",shards = 1,replicas = 0)
public class EsProduct implements Serializable {
    private static final long serialVersionUID = -1L;

    @Id
    private Long id;
    @Field(type = FieldType.Keyword)
    private String productSn;
    private Long brandId;
    @Field(type = FieldType.Keyword)
    private String brandName;
    @Field(type = FieldType.Keyword)
    private String productCategoryName;
    private String pic;
    @Field(analyzer = "ik_max_word",type = FieldType.Text)
    private String name;
    @Field(analyzer = "ik_max_word",type = FieldType.Text)
    private String subTitle;
    @Field(analyzer = "ik_max_word",type = FieldType.Text)
    private String keywords;
    private BigDecimal price;
    private Integer sort;
    @Field(type =FieldType.Nested)
    private List<EsProductAttributeValue> attrValueList;
...get set 方法

继承ElasticsearchRepository<T, ID extends Serializable>接口

/**
 * 搜索商品ES操作类
 *
 * 在接口中直接指定 **查询方法名** 称便可查询,无需进行实现,
 * 如商品表中有商品名称、标题和关键字,
 * 直接定义以下查询,就可以对这三个字段进行全文搜索。
 */
public interface EsProductRepository extends ElasticsearchRepository<EsProduct,Long> {

    /**
     * 搜索查询
     * @param name  商品名称
     * @param subTitle  商品标题
     * @param keywords  商品关键字
     * @param page  分页信息
     * @return
     * 必须按提示命名
     */
    Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);
    /**
     * "match_all": {}
     * @param name
     * @param pageable
     * @return
     */
//    @Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
    Page<EsProduct> findByName(String name,Pageable pageable);

}

EsProductService 实现类

/**
 * EsProductService 实现类
 */
@Service
public class EsProductServiceImpl implements EsProductService {
    private static final Logger logger = LoggerFactory.getLogger(EsProductServiceImpl.class);

    @Autowired
    private EsProductDao productDao;

    @Autowired
    private EsProductRepository productRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public int importAll() {
        //数据库查询所有商品信息
        List<EsProduct> esProductList = productDao.getAllEsProductList(null);
        logger.info("查询所有商品:{}",esProductList.toString());
        //把所有的商品信息放入es中
        Iterable<EsProduct> esProductIterable = productRepository.saveAll(esProductList);
        Iterator<EsProduct> iterator = esProductIterable.iterator();
        int result =0;
        while (iterator.hasNext()) {
            result++;
            iterator.next();
        }
        //放入了多少条
        return result;
    }

    @Override
    public void delete(Long id) {
        productRepository.deleteById(id);
    }

    @Override
    public EsProduct create(Long id) {
        EsProduct result = null;
        List<EsProduct> esProductList = productDao.getAllEsProductList(id);
        //从数据库中查询商品,把商品放入es中
        if (esProductList.size() > 0){
            EsProduct esProduct = esProductList.get(0);
            result = productRepository.save(esProduct);
        }
        return result;
    }

    @Override
    public void delete(List<Long> ids) {
        //org.springframework.util.CollectionUtils;
        if ( !CollectionUtils.isEmpty(ids) ){
            List<EsProduct> esProductList = new ArrayList<>();
            for (Long id:ids){
                EsProduct esProduct = new EsProduct();
                esProduct.setId(id);
                esProductList.add(esProduct);
            }
            productRepository.deleteAll(esProductList);
        }
    }

    @Override
    public Page<EsProduct> search(String keyword, Integer pageNum, Integer pageSize) {
        // 分页
        Pageable pageable = PageRequest.of(pageNum,pageSize);
        //
//        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.matchQuery("name",keyword))
//                .must(QueryBuilders.matchQuery("subTitle",keyword))
//                .must(QueryBuilders.matchQuery("keywords",keyword));

        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("name",keyword))
                .must(QueryBuilders.matchQuery("subTitle",keyword))
                .must(QueryBuilders.matchQuery("keywords",keyword));

//        return productRepository.search(queryBuilder,pageable);
        return productRepository.findByNameOrSubTitleOrKeywords(keyword,keyword,keyword,pageable);
    }

    @Override
    public List<EsProduct> searchAll() {
        Iterable<EsProduct> iterable = productRepository.findAll();
        Iterator<EsProduct> iterator = iterable.iterator();
        List<EsProduct> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }
        return list;
    }

    @Override
    public Page<EsProduct> searchByName(String keyword, Integer pageNum, Integer pageSize) {
        // 分页
        Pageable pageable = PageRequest.of(pageNum,pageSize);

        return productRepository.findByName(keyword,pageable);
    }

}


第二种方式:ElasticsearchTemplate

org.springframework.data.elasticsearch.core.ElasticsearchTemplate

image.png

创建数据

org.springframework.data.elasticsearch.core.query.IndexQuery;
//创建单条数据
public String index(IndexQuery query) {}
//创建单条数据
public <T> boolean createIndex(Class<T> clazz) {}
//批量创建数据
public void bulkIndex(List<IndexQuery> queries) {}
    public void addAll( EsProduct esProduct ){
        IndexQuery indexQuery = new IndexQueryBuilder()
                .withId(esProduct.getProductSn())
                .withObject(esProduct)
                .build();
        //创建单条数据
        elasticsearchTemplate.index(indexQuery);
        elasticsearchTemplate.createIndex(EsProduct.class);
        //批量创建数据
        List<IndexQuery> indexQueryList = new ArrayList<>();
        indexQueryList.add(new IndexQueryBuilder().withId("")
                .withIndexName("索引名称").withObject(esProduct).build());
        indexQueryList.add(new IndexQueryBuilder().withId("")
                .withIndexName("索引名称").withObject(esProduct).build());
        elasticsearchTemplate.bulkIndex(indexQueryList);
    }

删除数据

//根据类对象删除
public <T> boolean deleteIndex(Class<T> clazz) {}
// 根据索引名删除
public boolean deleteIndex(String indexName) {}
//
//org.springframework.data.elasticsearch.core.query.DeleteQuery
//org.elasticsearch.index.query.QueryBuilder
public <T> void delete(DeleteQuery deleteQuery, Class<T> clazz) {}

更新数据

//单个更新
//org.springframework.data.elasticsearch.core.query.UpdateQuery;
//org.elasticsearch.action.update.UpdateRequest;
//org.springframework.data.elasticsearch.core.query.UpdateQueryBuilder
public UpdateResponse update(UpdateQuery query) {}
// 批量更新
public void bulkUpdate(List<UpdateQuery> queries) {}

查询

//
//org.springframework.data.elasticsearch.core.query.SearchQuery;
//org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
//org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
public <T> List<String> queryForIds(SearchQuery query) {}
//
public <T> List<T> queryForList(StringQuery query, Class<T> clazz){}
//
public <T> T queryForObject(StringQuery query, Class<T> clazz) {}
//
public <T> AggregatedPage<T> queryForPage(SearchQuery query, Class<T> clazz) {}
//
//org.springframework.data.elasticsearch.core.query.CriteriaQuery ;
//org.springframework.data.elasticsearch.core.query.Criteria;
public <T> List<T> queryForList(CriteriaQuery query, Class<T> clazz) {}

示例

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryStringQuery("小米"))
                .withPageable(new PageRequest(0, 20))
                .build();
        List<EsProduct> list = elasticsearchTemplate.queryForList(searchQuery, EsProduct.class);

//排序
        Pageable pageable= new PageRequest(0, 20,new Sort(Sort.Direction.DESC, "name"));
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryStringQuery("华为"))
                .withPageable(pageable)
                .build();
        Page<EsProduct> list = elasticsearchTemplate.queryForPage(searchQuery, EsProduct.class);

//模糊查找
        Pageable pageable = new PageRequest(0, 10);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("name", "苹果"))
                .withPageable(pageable)
                .build();
        List<EsProduct> list = elasticsearchTemplate.queryForList(searchQuery, EsProduct.class);
//Term全等查询
        Pageable pageable = new PageRequest(0, 10);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.termQuery("name", "华为"))
                .withPageable(pageable)
                .build();
        List<EsProduct> list = elasticsearchTemplate.queryForList(searchQuery, EsProduct.class);

//组合查询
        QueryBuilder filterQuery = QueryBuilders
                .boolQuery()
                .filter(QueryBuilders.termQuery("name", "小米"))
                .filter(QueryBuilders.termQuery("author", "雷军"));
        SearchQuery searchQuerys = new NativeSearchQueryBuilder().withQuery(filterQuery).build();
        List<EsProduct> lists = elasticsearchTemplate.queryForList( searchQuerys, EsProduct.class);

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容