zoukankan      html  css  js  c++  java
  • Elasticsearch 基本概念与用法

    1. 特点

    2. 查询语法
    3. 分词器

    4. spring boot + Elasticsearch 实现搜索框提示
    5. @Document 与 @Field

    一、特点

    官网文档:https://www.elastic.co/guide/en/elasticsearch/reference/5.5/index.html

    常用于描述 es 是: 全文搜索引擎、倒排索引、实时分析、分布式

    基本概念

    • Index :一系列文档的集合,类似于 mysql 中数据库的概念
    • type: 在 Index 里面可以定义不同的 type ,type 的概念类似mysql中表的概念,是一系列具有相同特征数据的结合
    • document : 文档的概念类似于mysql 中一条存储记录,为 json 格式
    • shards: 索引分片
    • replica : 副本

    分片

    Elasticsearch 集群允许系统存储的数据总量超过单机容量,为了实现这个功能,ES 将数据分散到多个物理的 Lucene 索引上,这些 Lunene 索引被称为分片(shard),散步这些分片的过程叫做分片处理(sharding),

    ES 会自动完成分片工作,对用户来说更像一个是大是索引。

    当然,除了 ES 自动进行进行分片处理外,用户为具体的应用进行参数调优也至关重要,因为分片的数量在索引创建时就被配置好了,之后无法改变,除非创建一个新索引并重新索引全部数据

    副本

    分片处理允许用户推送超过单机容量的数据到 ES ,副本(replica)则解决了访问压力过大时单机无法处理所有请求的问题。

    实现方法是为每个分片创建冗余的副本,处理查询时可以把这些副本当作最初的主分片(primary shard)使用。

    如果主分片所在的主机宕机了,ES 会自动从该分片中选出一个当作新的主分片,不会中断索引和搜索服务,并且可以在任意时间节点添加或删除副本,可以随时调整副本的数量

    二、查询语法

    1._cat :查询当前集群中 index 数量、运行状态、当前集群所在的 ip 

    • health  检查集群的运行状况
    • count  查询集群或者 index 中文档的数量
    • indices  查询集群中index的数据,包括index 的分片书、document的数量、存储所用空间

    示例:

    http://81.68.206.246:9200/_cat/health?v (?v 的意思是显示列出项的 title)

    2.查询API 

    • query
    • term
    • match
    • bool
    • range
    • from
    • size
    • sort
    • _source
    • script_fields
    • ags

    3. query DSL

    query DSL 是 es 提供的一套完整的基于 json 格式的结构化查询方法,包含两类不同的查询语义

    • Leaf query clauses : 叶子查询语法是在指定的字段中搜索指定的值,有 match 、term 、range
    • Compound query clauses : 复合查询语法包含叶子句法 或者复合句法,作用是为了多重查询,有 bool 、dis_max 

    4.分词器选择

    ik_smart

    ik_max_word

    三、ik 分词器

    ES 内置很多分词器,但是对中文不友好,使用第三方插件--中文分词器(ik 分词器)

    使用默认的分词器查询测试:81.68.206.246:9200/_analyze?pretty=true

    ES 分词器使用的都是将中文分成单个的词,因此需要导入第三方分词器

    ES IK分词器下载:https://github.com/medcl/elasticsearch-analysis-ik/releases

    ES 常用分词器如下:

    StandardAnalyzer 单字分词
    CJKAnalyzer 二分法
    IKAnalyzer 词库分词

    其中 IK  分词器是第三方插件,下载需要找与 ES 对应的版本

    将下载的 zip 分词包上传到服务器, 并且在 ES 目录的 plugins 目录下创建空文件夹,将zip 解压到内容复制到该文件夹下,重启ES

    分词器参考文档:

    https://www.cnblogs.com/xiaobaozi-95/p/9328948.html

    https://blog.csdn.net/weixin_44723434/article/details/89888489

     四、spring boot 与 ES 实现搜索框提示

    1.maven pom.xml 依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.10.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>top.supoman</groupId>
        <artifactId>springboot-elasticsearch</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springboot-elasticsearch</name>
        <description>elasticsearch for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!--集合工具包-->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>19.0</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    View Code

    2.项目结构

     3.定义实体 

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Document(indexName = "school",type = "_doc")
    public class Notice {
    
    
        @JsonProperty("id")
        private String id;
    
        @JsonProperty("title")
        private String title;
    
        @JsonProperty("originCreateTime")
        private String originCreateTime;
    
        @JsonProperty("msg")
        private String msg;
    
        @JsonProperty("readCount")
        private Integer readCount;
    
    }
    View Code
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class ResultVO<T>{
    
        private String msg ;
        private  T t;
    
    
        public static<T>  ResultVO  success(T t){
            ResultVO resultVO = new ResultVO();
            resultVO.setT(t);
            resultVO.setMsg("success");
           return resultVO;
        }
    }
    View Code

    4.Repository

    @Component
    public interface NoticeRepository extends ElasticsearchRepository<Notice,String> {
    }

    5.控制层接口

    @Controller
    public class HomeController {
    
        @GetMapping({"/","/index","/home"})
        public String index(){
            return "index";
        }
    }
    @RestController
    @RequestMapping("/api")
    public class NoticeController {
    
        @Autowired
        private NoticeRepository noticeRepository;
    
        @GetMapping("/save")
        public ResultVO save(String id, String title){
            Notice notice = new Notice();
            notice.setId(id);
            notice.setReadCount(10);
            notice.setTitle(title);
            noticeRepository.save(notice);
            return ResultVO.success(notice);
        }
    
        @GetMapping("/search")
        public List<Notice> search(String keyword, @PageableDefault(page = 0,value = 10)Pageable pageable){
            //按标题进行搜索
            QueryBuilder queryBuilder = QueryBuilders.matchQuery("title",keyword);
    
            //如果实体和数据的名称对应,就自动封装
            Iterable<Notice> listIt =  noticeRepository.search(queryBuilder,pageable);
    
            List<Notice> list = Lists.newArrayList(listIt);
    
            return list;
        }
    
    }
    View Code

    6.html 展示案例

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
    #header_search_suggest{
        position: absolute;
        width: calc(100% - 10px);
        left: 4px;
        border: solid 0px #ccc;
        background-color: white;
        text-align: left;
        z-index: 101;
        display: block;
    }
    #header_search_suggest li{
        font-size: 14px;
        border-bottom: 0px solid #eeeeee;
        padding-left:460px;
        list-style-type:none;
    }
    #header_search_suggest li a{
        padding:0.5em 1em;
        color:#333333;
        display: block;
        text-decoration: none;
    }
    #header_search_suggest li a:hover{
        background-color: #EDF0F2;
        color:#2F7EC4;
    }
    #header_search_suggest li a em{
        font-style: italic;
        color:#999;
        font-size:0.8em;
    }
    .btn{width: 7em;}
    
        </style>
    
    
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
    
    </head>
    <body>
       
    <div  class="container" style="margin-top: 3em;">
    
        <form method="GET" action="/api/search" id="header_search" class="form-inline">
            <div class="form-group">
               <label for="exampleInputName2">输入搜索关键词试试</label>
               <input type="text" class="form-control" id="keyword" name="keyword" value="" autocomplete="off" />
            </div>
            <div class="form-group">
                <input type="submit" value="搜索"  class="btn btn-success"/>
            </div>
          
        </form>
        <ul id="header_search_suggest"></ul>
    
    </div>
    
    <!--  js部分,这部分控制,输入框输入时,进行及时提示的功能  -->
    <script>
    var xhr = null;
    $('#keyword').bind('input propertychange', function () {
        if (xhr) {
            xhr.abort();//如果存在ajax的请求,就放弃请求
        }
    
        var inputText = $.trim(this.value);
        if (inputText != "") { //检测键盘输入的内容是否为空,为空就不发出请求
            xhr = $.ajax({
                type: 'GET',
                url: '/api/search',//注意这里输入框输入进行及时提示的方法与action方法不同
                cache: false,//不从浏览器缓存中加载请求信息
                data: "keyword=" + inputText,
                //data: {keyword: inputText},
                dataType: 'json',
                success: function (json) {
                    if (json.length != 0) {
                        //检测返回的结果是否为空
                        var lists = "";   
                        for(var index=0;index<json.length;index++){
                            //标题
                            var searchContent = json[index].title;
                            var suggestItem = '';
                            if(searchContent.toLowerCase().indexOf(inputText.toLowerCase()) > -1 ){
                                 var searchRegExp = new RegExp('(' + inputText + ')', "gi");
                                 suggestItem = searchContent.replace(searchRegExp, ("<strong>$1</strong>"));
                            }
                            suggestItem = suggestItem + "<em> - " + json[index].title + ' * ' + json[index].id + "</em>";
                            lists += "<li class='listName' ><a href='/api/search?id=" + json[index].id + "&key=" + json[index].msg+"'>" +suggestItem+ "</a></li>";
                        }
                        $("#header_search_suggest").html(lists).show();//将搜索到的结果展示出来
                    } else {
                        $("#header_search_suggest").hide();
                    }
                    //记录搜索历史记录
                    // $.post('/index.php/index/index/savesearchlog',{keyword: inputText,count: json.count});
                }
            });
        } else {
            $("#header_search_suggest").hide();//没有查询结果就隐藏搜索框
        }
    }).blur(function () {
        setTimeout('$("#header_search_suggest").hide()',500);//输入框失去焦点的时候就隐藏搜索框,为了防止隐藏过快无法点击,设置延迟0.5秒隐藏
    });
    </script>
    
    
    
    </body>
    </html>
    View Code

    7.配置文件

    server:
      port: 9000
    
    spring:
      data:
        elasticsearch:
          repositories:
            enabled: true
          cluster-nodes: 81.68.206.246:9300

    8.最终效果

     Github地址:https://github.com/baizhuang/springboot-elasticsearch

    可能会报错 :超时 127.0.0.1:9200

    因为spring boot 中 es会有健康检,使用了默认的 ip,修改配置文件

    spring:
      data:
        elasticsearch:
          repositories:
            enabled: true
          cluster-nodes: 81.68.206.246:9300
      elasticsearch:
        rest:
          uris: ["http://81.68.206.246:9200"]

    参考:http://blog.joylau.cn/2019/01/16/SpringBoot-Elasticsearch-HealthCheck/

     五、Elasticsearch 注解

    在 spring boot 使用ES中定义了一些注解,如 @Document 、@Field

    @Data
    @Document(indexName = "tradingAccount")
    public class TradingAccount {
    
        @Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
        private Long userId;
    
        @Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
        private Long accountId;
    
        private String userAvatar;
    
        private String username;
    }

    这里主要有 @Document 和 @Field 两个注解

    @Document

    @Persistent
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface Document {
        String indexName();
    
        String type() default "";
    
        boolean useServerConfiguration() default false;
    
        short shards() default 5;
    
        short replicas() default 1;
    
        String refreshInterval() default "1s";
    
        String indexStoreType() default "fs";
    
        boolean createIndex() default true;
    
        VersionType versionType() default VersionType.EXTERNAL;
    }

    indexName 对应的是 elasticsearch 中的 index . createIndex 默认值是True ,所以当 elasticsearch 中没有对应的索引时会创建索引

    @Field

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    @Documented
    @Inherited
    public @interface Field {
        @AliasFor("name")
        String value() default "";
    
        @AliasFor("value")
        String name() default "";
    
        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 {};
    }

    type : 返回值是 FieldType 类型,指定数据类型

    public enum FieldType {
        text, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip, Attachment, keyword
    }

    analyzer : 指定分词器

    searchAnalyzer :查询时候使用的分词器

    参考:https://my.oschina.net/kipeng/blog/1799827

  • 相关阅读:
    java微信开发
    LeetCode题目解答
    LeetCode 题目总结/分类
    http://baldtheme.com/theme/cleanizr/html/ 类似于这样的
    发现UC/OS-III源码有一处不明白!会不会是BUG.高手过来看看!
    hibernate hbm2ddl auto 不能创建表的问题
    Can jxta be used to develop online card game (p2p style)?
    父子对等组之间的关系
    WScript中调用js方法
    7-wonders-in-java
  • 原文地址:https://www.cnblogs.com/baizhuang/p/13808659.html
Copyright © 2011-2022 走看看