分布式全文搜索引擎——Elasticsearch

1.安装Elasticsearch

  a.下载:从官网下载 Elasticsearch,地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch

  b.启动:解压后,在 bin 目录打开 elasticsearch.bat 启动服务

  c.访问:在浏览器中访问 http://127.0.0.1:9200/ 检查是否安装成功

    注意:端口9300:Java程序访问的端口;端口9200:浏览器访问的端口

       Elasticsearch 的版本需要跟后面的 Spring-Data-Elasticsearch 的版本相符,不然会不兼容(Spring 5.1.1 对应 Elasticsearch 6.8.0)

2.安装Head管理Elasticsearch插件

  a.安装nodejs:在nodejs官网下载msi,地址:https://nodejs.org/en/download/,安装后在CMD命令行输入 node -v 查看是否安装成功

  b.安装grunt:在CMD命令行输入 npm install -g grunt-cli 安装grunt,安装后输入 grunt -version 查看是否安装成功

  c.下载Head:在GitHub下载并解压 elasticsearch-head-master,地址:https://github.com/mobz/elasticsearch-head

  d.修改Head配置:修改Head根目录下Gruntfile.js,添加:hostname:‘*‘,

  

  e.修改Elasticsearch配置:修改 Elasticsearch 的 config 目录下的 elasticsearch.yml 文件,添加如下配置。并重启

network.host: 127.0.0.1
# 解决elasticsearch-head 集群健康值: 未连接问题
http.cors.enabled: true
http.cors.allow-origin: "*"

  f.安装Head:在 elasticsearch-head-master 根目录下执行命令 npm install 进行安装

  g.启动Head:同样在 elasticsearch-head-master 根目录下执行命令 grunt server(或者npm run start)

  h.访问Head:在浏览器中访问 http://localhost:9100/ 即可进入管理界面

3.安装IK中文分词器

  a.下载:在GitHub上下载对应版本的IK分词器zip包,地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

  b.注意:Elasticsearch和IK分词器必须版本统一

  c.解压安装:解压到 elasticsearch 的 plugins 目录下,并改名为 ik。并重启 elasticsearch

  

4.使用 Spring-Data-Elasticsearch 操作 Elasticsearch

  a.导入pom依赖

 <properties>

    ......

    <!-- spring -->
    <spring.version>5.1.1.RELEASE</spring.version>

  </properties>

  <dependencies>
    <!-- elasticsearch -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-elasticsearch</artifactId>
      <version>3.1.1.RELEASE</version>
    </dependency>

    <!-- spring-5.X -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-oxm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- AOP-AspectJ spring-aop依赖 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.6</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.6</version>
    </dependency>

    <!-- jackson-json spring-mvc依赖 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.4</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.4</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.4</version>
    </dependency>

    <!-- fastjson -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.47</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  b.创建BaseEntity基础实体类

public class BaseEntity {

    @Id
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

  c.创建Article和Author实体类

//index相当于数据库,type相当于表
@Document(indexName = "elasticsearch", type = "article")
public class Article extends BaseEntity {

    private String title;
    private String content;
    //内嵌对象
    @Field(type = FieldType.Nested)
    private List<Author> authors;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<Author> getAuthors() {
        return authors;
    }

    public void setAuthors(List<Author> authors) {
        this.authors = authors;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "Article: id[" + this.getId() + "], title[" + title + "], content[" + content + "], authors[" + authors + "]";
    }
}
public class Author {

    private String name;

    private int year;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "Author: name[" + name + "], year[" + year + "]";
    }
}

  d.创建BaseDao基础DAO

public class BaseDao<T extends BaseEntity> {

    @Resource(name="elasticsearchTemplate")
    protected ElasticsearchTemplate esTemplate;
    protected Class<T> clazz;

    @PostConstruct
    private void construct(){
        clazz = (Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * 保存单个对象
     * @param t 对象
     * @return
     */
    public boolean save(T t){
        String id = t.getId();
        if (id == null) {
            id = UUID.randomUUID().toString().replaceAll("-", "");
            t.setId(id);
        }
        IndexQuery indexQuery = new IndexQueryBuilder().withId(id).withObject(t).build();
        esTemplate.index(indexQuery);
        return true;
    }

    /**
     * 保存多个
     * @param list 对象集合
     * @return
     */
    public boolean save(List<T> list){
        List<IndexQuery> queries = new ArrayList<IndexQuery>();
        for (T t : list) {
            String id = t.getId();
            if (id == null) {
                id = UUID.randomUUID().toString().replaceAll("-", "");
                t.setId(id);
            }
            IndexQuery indexQuery = new IndexQueryBuilder().withId(id).withObject(t).build();
            queries.add(indexQuery);
        }
        esTemplate.bulkIndex(queries);
        return true;
    }

    /**
     * 根据ID删除
     * @param id 对象ID
     * @return
     */
    public boolean deleteById(String id){
        esTemplate.delete(clazz, id);
        return true;
    }

    /**
     * 根据多个ID删除
     * @param idList 对象ID集合
     * @return
     */
    public boolean deleteByIds(List<String> idList) {
        DeleteQuery deleteQuery = new DeleteQuery();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        if(idList != null) {
            for (String id : idList) {
                queryBuilder.should(QueryBuilders.matchQuery("id", id));
            }
        }
        deleteQuery.setQuery(queryBuilder);;
        esTemplate.delete(deleteQuery, clazz);
        return true;
    }

    /**
     * 根据过滤条件删除
     * @param filter 过滤条件Map
     * @return
     */
    public boolean delete(Map<String,Object> filter){
        DeleteQuery deleteQuery = new DeleteQuery();
        BoolQueryBuilder queryBuilder = QueryBuilders. boolQuery();
        if(filter != null) {
            for (Map.Entry<String, Object> entry : filter.entrySet()) {
                //"content.keyword" 表示不进行分词(精准查询),解决 "content" 分词后 term 查不到数据的问题
                queryBuilder.must(QueryBuilders.matchQuery(entry.getKey() + ".keyword", entry.getValue()));
            }
        }
        deleteQuery.setQuery(queryBuilder);
        esTemplate.delete(deleteQuery, clazz);
        return true;
    }

    /**
     * 根据条件查询集合
     * @param filter 过滤条件Map
     * @param highFields 高亮字段
     * @param sortField 排序字段
     * @param order 正序倒序
     * @return
     */
    public List<T> queryList(Map<String, Object> filter, final List<String> highFields, String sortField, SortOrder order) {
        NativeSearchQueryBuilder searchBuilder = new NativeSearchQueryBuilder();

        //-----------------------------高亮-----------------------------
        List<Field> fieldList = new ArrayList<>();
        if(highFields != null) {
            for (String highField : highFields) {
                fieldList.add(new HighlightBuilder.Field(highField).preTags("<em>").postTags("</em>").fragmentSize(250));
            }
        }
        searchBuilder.withHighlightFields(fieldList.toArray(new Field[fieldList.size()]));

        //-----------------------------排序-----------------------------
        if (sortField != null && order != null){
            searchBuilder.withSort(new FieldSortBuilder(sortField + ".keyword").order(order));
        }

        //-----------------------------过滤条件-----------------------------
        BoolQueryBuilder queryBuilder= QueryBuilders.boolQuery();
        for (Map.Entry<String, Object> entry : filter.entrySet()) {
            queryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
        }
        searchBuilder.withQuery(queryBuilder);

        //-----------------------------查询建立-----------------------------
        SearchQuery searchQuery = searchBuilder.build();
        Page<T> page = null;
        //如果设置高亮
        if (highFields != null && highFields.size() > 0) {
            page = esTemplate.queryForPage(searchQuery, clazz, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse response,Class<T> clazz, Pageable pageable) {
                    List<T> list = new ArrayList<T>();
                    for (SearchHit searchHit : response.getHits()) {
                        if (response.getHits().getHits().length <= 0) {
                            return null;
                        }
                        Map<String, Object> entityMap = searchHit.getSourceAsMap();
                        for (String highName : highFields) {
                            String highValue = searchHit.getHighlightFields().get(highName).fragments()[0].toString();
                            entityMap.put(highName, highValue);
                        }
                        T t = JSONObject.parseObject(JSONObject.toJSONString(entityMap), clazz);
                        list.add(t);
                    }
                    if (list.size() > 0) {
                        return new AggregatedPageImpl<T>(list);
                    }
                    return null;
                }
            });
            //如果不设置高亮
        } else{
            page = esTemplate.queryForPage(searchQuery, clazz);
        }
        return page.getContent();
    }

    /**
     * 根据条件查询分页列表
     * @param filter 过滤条件Map
     * @param highFields 高亮字段
     * @param sortField 排序字段
     * @param order 正序倒序
     * @param pageIndex 当前页
     * @param pageSize 分页大小
     * @return
     */
    public Map<String, Object> queryPage(Map<String,Object> filter, final List<String> highFields, String sortField, SortOrder order, int pageIndex, int pageSize) {
        NativeSearchQueryBuilder searchBuilder = new NativeSearchQueryBuilder();

        //-----------------------------高亮-----------------------------
        List<Field> fieldList = new ArrayList<>();
        if(highFields != null) {
            for (String highField : highFields) {
                fieldList.add(new HighlightBuilder.Field(highField).preTags("<em>").postTags("</em>").fragmentSize(250));
            }
        }
        searchBuilder.withHighlightFields(fieldList.toArray(new Field[fieldList.size()]));

        //-----------------------------排序-----------------------------
        if (sortField != null && order != null){
            searchBuilder.withSort(new FieldSortBuilder(sortField + ".keyword").order(order));
        }

        //-----------------------------分页-----------------------------
        searchBuilder.withPageable(PageRequest.of(pageIndex, pageSize));

        //-----------------------------过滤条件-----------------------------
        BoolQueryBuilder queryBuilder= QueryBuilders.boolQuery();
        for (Map.Entry<String, Object> entry : filter.entrySet()) {
            queryBuilder.must(QueryBuilders.matchQuery(entry.getKey(), entry.getValue()));
        }
        searchBuilder.withQuery(queryBuilder);

        //-----------------------------查询建立-----------------------------
        SearchQuery searchQuery = searchBuilder.build();
        Page<T> page = null;
        //如果设置高亮
        if (highFields != null && highFields.size() > 0) {
            page = esTemplate.queryForPage(searchQuery, clazz, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse response,Class<T> clazz, Pageable pageable) {
                    List<T> list = new ArrayList<T>();
                    for (SearchHit searchHit : response.getHits()) {
                        if (response.getHits().getHits().length <= 0) {
                            return null;
                        }
                        Map<String, Object> entityMap = searchHit.getSourceAsMap();
                        for (String highName : highFields) {
                            String highValue = searchHit.getHighlightFields().get(highName).fragments()[0].toString();
                            entityMap.put(highName, highValue);
                        }
                        T t = JSONObject.parseObject(JSONObject.toJSONString(entityMap), clazz);
                        list.add(t);
                    }
                    if (list.size() > 0) {
                        return new AggregatedPageImpl<T>(list);
                    }
                    return null;
                }
            });
            //如果不设置高亮
        } else{
            page = esTemplate.queryForPage(searchQuery, clazz);
        }

        //-----------------------------查询数据总条数-----------------------------
        searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        long totalCount = esTemplate.count(searchQuery, clazz);

        //组装结果
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("pageIndex", pageIndex);
        resultMap.put("pageSize", pageSize);
        resultMap.put("data", page.getContent());
        resultMap.put("totalCount", totalCount);
        return resultMap;
    }

}

参考文档:https://blog.csdn.net/chen_2890/article/details/83757022

     https://blog.csdn.net/lihuanlin93/article/details/83448967

原文地址:https://www.cnblogs.com/vettel0329/p/11096687.html

时间: 2024-10-28 15:03:31

分布式全文搜索引擎——Elasticsearch的相关文章

3.高并发教程-基础篇-之分布式全文搜索引擎elasticsearch的搭建

高并发教程-基础篇-之分布式全文搜索引擎elasticsearch的搭建 如果大家看了我的上一篇<2.高并发教程-基础篇-之nginx+mysql实现负载均衡和读写分离>文章,如果能很好的利用,那么其实已经可以轻松日抗千万级别的访问量了,但是如果业务涉及查询比较多,查询条件比较丰富,又或者我就想要查询的响应更快点,那么在mysql上面去做优化,其实比较辛苦,有没有更好的解决方案呢?答案是肯定的!它就是我们今天的主角,分布式全文搜索引擎elasticsearch. 技巧提示:mysql集群层主要

分布式全文搜索引擎ElasticSearch—超详细

1 ElasticSearch 1.1 ES的概念和特点 ES:全文检索的框架,专门做搜索,支持分布式.集群.封装的Lucene. 特点: 原生的Lucene使用的不足,优化了Lucene的调用方式 高可用的分布式集群,处理PB级别的数据 目的是通过简单的restful API来隐藏Lucene的复杂性,从而使全文检索变得简单,达到“开瓶即饮”的效果 Lucene:全文检索,api比较麻烦,操作全文检索的最底层技术. 核心:创建索引,搜索索引 1.2 ES的对手 Solr和ES的区别: (1)

ES(ElasticSearch)分布式全文搜索引擎

1.什么是ES**ES** 全称 **ElasticSearch** 是一种分布式全文搜索引擎,基于Lucene(全文搜索框架)开发而来.Lucene是公认的迄今为止的最好用的搜索引擎库,但是他所提供的API对于我们使用者来说,是非常苦恼的,常要花费大量时间去熟悉学习.ES的出现就很好的解决了这个问题,良好的封装,易用的API,链式书写方式,开瓶即饮.2.ES特点**ES** 虽然是以Lucene核心库开发的,但是却不是以它作为核心,**ES** 的贴点体现在: *分布式实时文件存储,每个字段皆

全文搜索引擎 Elasticsearch 入门

1. 百科 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎.设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便. 2. 安装 依赖Java8,本文在Linux上运行 下载.解压 $ wget https://artifacts.elastic.co/downloads/ela

全文搜索引擎Elasticsearch初探

前言: 在Web应用或后台数据管理中,随着数据量的倍数增长,搜索引擎特别是全文搜索引擎的应用越来越迫切.基于技术和成本考虑,我们不可能去开发一个搜索引擎以满足我们的需求,庆幸的是业界已有许多优秀的开源搜索引擎可供我们使用,Elasticsearch便是其中之一. 简介: Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎.无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进.性能最好的.功能最全的搜索引擎库.但是,Lucene只是一个库.想要使用它,你

全文搜索引擎 ElasticSearch 还是 Solr?

最近项目组安排了一个任务,项目中用到了全文搜索,基于全文搜索 Solr,但是该 Solr 搜索云项目不稳定,经常查询不出来数据,需要手动全量同步,而且是其他团队在维护,依赖性太强,导致 Solr 服务一出问题,我们的项目也基本瘫痪,因为所有的依赖查询都无结果数据了.所以考虑开发一个适配层,如果 Solr 搜索出问题,自动切换到新的搜索--ES. 其实可以通过 Solr 集群或者服务容错等设计来解决该问题.但是先不考虑本身设计的合理性,领导需要开发,所以我开始踏上了搭建 ES 服务的道路,从零开始

全文搜索引擎 Elasticsearch 入门教程

全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选. 它可以快速地储存.搜索和分析海量数据.维基百科.Stack Overflow.Github 都采用它. Elastic 的底层是开源库 Lucene.但是,你没法直接用 Lucene,必须自己写代码去调用它的接口.Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用. 本文从零开始,讲解如何使用 Elastic 搭建自己的全文搜索引擎.每一步都

全文搜索引擎 Elasticsearch 介绍

全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选. 它可以快速地储存.搜索和分析海量数据.维基百科.Stack Overflow.Github 都采用它. Elastic 的底层是开源库 Lucene.但是,你没法直接用 Lucene,必须自己写代码去调用它的接口.Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用. 原文地址:https://www.cnblogs.com/jacksonxi

ElasticSearch(分布式全文搜索引擎)

1.Lucene的优化, 实现了高可用的分布式集群的搜索方案 2.首先,ES的索引库管理支持依然是基于Apache Lucene(TM)的开源搜索引擎.ES也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单. 3.ES的特点 a)  分布式的实时文件存储,每个字段都被索引并可被搜索 分布式的实时分析搜索引擎 可以扩展到上百台服务器,处理PB级结构化或非结构化数据 高度集成化的