分布式全文搜索引擎——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.创建配置文件

    spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 自动扫描的包名 -->
    <context:component-scan base-package="com.wode" />

    <!-- 开启AOP代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true" />

    <!--开启注解处理器 -->
    <context:annotation-config>
    </context:annotation-config>

    <context:property-placeholder location="classpath:elasticsearch.properties"/>
    <!-- Spring中引入其他配置文件 -->
    <import resource="classpath*:/spring-elasticsearch.xml" />

</beans>

    spring-elasticsearch.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">

    <!-- 搜索DAO 扫描 -->
    <elasticsearch:repositories base-package="com.wode.dao" />

    <!-- 配置Client -->
    <elasticsearch:transport-client id="client" cluster-nodes="${elasticsearch.host}:${elasticsearch.port}"/>

    <!-- 配置搜索模板  -->
    <bean id="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
        <constructor-arg name="client" ref="client" />
    </bean>

</beans>

    elasticsearch.properties

elasticsearch.host=localhost
elasticsearch.port=9300

 

 

  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 Author author;

    public String getTitle() {
        return title;
    }

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

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    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 + "], author[" + author + "]";
    }
}

 

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();
        Map<String, Object> filter = new HashMap<>();
        filter.put("ids", idList);
        BoolQueryBuilder queryBuilder = this.getQueryBuilder(filter, QueryBuilders.boolQuery());
        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 = this.getQueryBuilder(filter, QueryBuilders.boolQuery());
        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 = this.getQueryBuilder(filter, QueryBuilders.boolQuery());
        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);
        }
        List<T> resultList = new ArrayList<>();
        if(page != null){
            resultList = page.getContent();
        }
        return resultList;
    }

    /**
     * 根据条件查询分页列表
     * @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 = this.getQueryBuilder(filter, QueryBuilders.boolQuery());
        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);
        }

        List<T> resultList = new ArrayList<>();
        if(page != null){
            resultList = page.getContent();
        }

        //-----------------------------查询数据总条数-----------------------------
        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", resultList);
        resultMap.put("totalCount", totalCount);
        return resultMap;
    }

    /**
     * 根据过滤条件获取BoolQueryBuilder
     * @param filter 过滤条件
     * @param query BoolQueryBuilder
     * @return
     */
    protected BoolQueryBuilder getQueryBuilder(Map<String, Object> filter, BoolQueryBuilder query){
        if(query == null){
            query = QueryBuilders.boolQuery();
        }
        if(CollectionUtils.isEmpty(filter)){
            return query;
        }
        for(Map.Entry<String, Object> entry : filter.entrySet()){
            String key = entry.getKey();
            Object value = entry.getValue();
            switch (key){
                case "id":
                    query = query.must(QueryBuilders.matchQuery("id.keyword", value));
                    break;
                case "ids":
                    List<String> idList = (List<String>) value;
                    for (String id : idList) {
                        query.should(QueryBuilders.matchQuery("id.keyword", id));
                    }
                    break;
                case "idNot":
                    query = query.mustNot(QueryBuilders.matchQuery("id.keyword", value));
                default:
                    break;
            }
        }
        return query;
    }

}

 

 

  e.创建 ArticleDao 继承 BaseDao

@Repository
public class ArticleDao extends BaseDao<Article> {

    @Override
    protected BoolQueryBuilder getQueryBuilder(Map<String, Object> filter, BoolQueryBuilder query) {
        query = super.getQueryBuilder(filter, query);
        for(Map.Entry<String, Object> entry : filter.entrySet()){
            switch (entry.getKey()){
                case "contentMatch":
                    query = query.must(QueryBuilders.matchQuery("content", entry.getValue()));
                    break;
                case "title":
                    // ".keyword" 表示不进行分词(精准查询),解决 "title" 分词后 term 查不到数据的问题
                    query = query.must(QueryBuilders.matchQuery("title.keyword", entry.getValue()));
                    break;
                case "authorYearBt":
                    query = query.must(QueryBuilders.rangeQuery("author.year").gt(entry.getValue()));
                default:
                    break;
            }
        }
        return super.getQueryBuilder(filter, query);
    }
}

 

 

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

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

 

posted @ 2019-06-29 10:02  晨M风  阅读(525)  评论(0编辑  收藏  举报