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

Posted vettel0329

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式全文搜索引擎——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

 

以上是关于分布式全文搜索引擎——Elasticsearch的主要内容,如果未能解决你的问题,请参考以下文章

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

分布式全文检索引擎之ElasticSearch

干货 | 分布式全文检索引擎之ElasticSearch

分布式全文检索引擎之ElasticSearch

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

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