ES

作者: 忒无聊了叭 | 来源:发表于2020-11-15 22:29 被阅读0次

    前奏

    RestFul

    全文检索

    关于全文检索,我们需要知道哪些以下这些:

    • 只处理文本
    • 不处理语义(不是人工智能,检索:你是谁,只会出现关于你是谁三个关键字的相关内容,而不会出现回答)
    • 搜索时英文不区分大小写,可以尝试一下百度,大小写搜出来的东西是一样的
    • 结果列表有相关度排序

    什么是ElasticSearch

    简称ES,Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。

    ES应用场景:支持站内搜索,主要以轻量级的json作为数据存储格式,这点和mongdb类似,但是在性能上要优于mongdb。同样也支持地理查询,还方便地理位置和文本混合查询。在统计、日志类的数据存储和分析,可视化这方面是引领者。

    • 国外:GitHub、维基百科在使用ES
    • 国内:百度、新浪、阿里巴巴也在使用

    ES安装

    1、安装jdk8

    rpm -ivh jdk-8u181 -linux-x64.rpm
    

    2、由于ES不能以Root用户进行安装,所以我们应该先创建普通用户和组

    # 添加es组
    groupadd es
    # 添加用户 将用户szw指向es组
    useradd szw -g es
    # 设置用户的密码
    passwd szw
    

    3、我们这里为了学习方便,使用docker进行安装es和可视化工具。参考以下的博客进行docker方式安装:

    https://www.cnblogs.com/gyyyblog/p/11596707.html

    4、我们启动完es后,需要在服务器内部查看es是否启动成功。因为需要开启远程服务权限(不适用docker方式的安装需要配置,修改配置文件中的network.host)

    [root@iZ2zed97t0sgast08g54toZ ~]# curl http://localhost:9200
    {
      "name" : "6f95bf5faf11",
      "cluster_name" : "docker-cluster",# es比较特殊,单个即集群
      "cluster_uuid" : "TZto9fKBRQesKb7309Hqvw",
      "version" : {
        "number" : "7.3.0",
        "build_flavor" : "default",
        "build_type" : "docker",
        "build_hash" : "de777fa",
        "build_date" : "2019-07-24T18:30:11.767338Z",
        "build_snapshot" : false,
        "lucene_version" : "8.1.0",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    
    

    9200是es的web端口。9300是内部tcp访问端口。

    值得注意的是,es默认的分配的运行内存是1g,我们这里可以在配置文件中进行修改256m,这样才可以使es与kibana同时运行!

    就绪

    接近实时:es是一个接近实时的搜索平台,这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟。
    步骤:索引数据创建索引,直到可以被搜索,需要1s中左右。
    

    索引

    什么是索引?

    便于理解,我们可以把一个索引理解成关系型数据库(如mysql)的一个库,我们在es中可以创建任意多个索引。索引必须全部都是小写字母,并且使唯一标识。

    类型

    在一个索引中,你可以定义一种或多个类型。一个类型就类似于mysql中的表。假设:你要开发一款网上购物系统,用户的信息保存为一个类型,商品的信息保存为一个类型。

    映射

    Mapping是ES中一个很重要的概念,相当于传统数据库中的约束(暂且这样比喻,实际是不正确的!),用于定义一个索引中的类型的数据的结构。我们可以手动的创建,ES也可以根据自动插入的数据自动创建type及mapping。mapping主要包括字段名、字段数据类型和字段索引类型。

    文档

    相当于关系型数据库中的一条记录,在es中是以json格式进行存储。

    ES5.x的模型:一个索引可以定义多个类型,6.x之后的版本也可以兼容,但是不推荐,7、8版本彻底移除了一个索引可以创建多个类型。

    类比.png

    由于版本更新后,我们不可以再比喻成关系型数据库中的表,因为一个索引中只能创建一种类型。

    安装Kibana

    注意:由于学生机的配置比较低,无法同时运行es和kinana,至少需要3gb左右的运行内存才可以同时运行。

    这里推荐使用在本地创建虚拟机再去安装Kibana。

    正文

    索引

    创建索引
    Put /szw # 注意我们的索引名必须是小写,可以写数字但是不推荐
    

    返回结果:

    {
      "acknowledged" : true,
      "shards_acknowledged" : true,
      "index" : "szw"
    }
    

    我们在创建索引的时候也可以去创建一些配置:

    # 创建索引
    PUT /szw
    {
      "settings": {
        "number_of_replicas": 1,
        "number_of_shards": 1
      }
    }
    
    查看索引
    # 查看索引
    GET /_cat/indices
    

    返回结果:

    green open .kibana_task_manager         Phv_k8FHRuu4Rw-pu9zn1g 1 1     2 0  54.1kb   31.4kb
    green open szw                          EDGWPYufRNWX6e9rG0jzeQ 1 1     0 0    566b     283b
    green open .kibana_1                    EndCRzm-TKOSvWmUt1nVFA 1 1   116 1   1.9mb 1017.7kb
    

    我们可以发现,在每个索引的开头含有green,其实还有另外两个red和yello,返回的就相当于一个表格,我们可以通过以下命令将详细的表格表头显示出来。

    # 查看索引
    GET /_cat/indices?v
    

    返回结果:

    health status index                        uuid                   pri rep docs.count docs.deleted store.size pri.store.size
    green  open   test                         od_x1RJmSIGJjb_i6jQqYA   1   1          1            0      8.5kb          4.1kb
    green  open   test2                        VhhcPIYfQDqppivy7uvJZA   1   1          1            0        7kb          3.5kb
    green  open   .kibana_task_manager         Phv_k8FHRuu4Rw-pu9zn1g   1   1          2            0     54.1kb         31.4kb
    green  open   szw                          EDGWPYufRNWX6e9rG0jzeQ   1   1          0            0       566b           283b
    

    我们可以看到health,就代表该条索引的健康状态,index代表索引的名字,uuid代表唯一标识,pri代表分片数量。docs代表文档的相关属性。

    green代表绿色:健壮

    yello代表黄色:不健壮,但是可用

    red代表索引不完整的,不可用的

    删除索引

    Delete /szw
    

    返回结果:

    {
      "acknowledged" : true
    }
    

    默认带的两个kibana的索引不要删除,删除后kibana不能使用,需要重启。

    类型操作

    创建szw索引中的person类型:

    # 这是6版本的书写方式
    PUT /szw
    {
      "mappings": {
        "person":{
          "properties":{
            "id":{"type":"String"},
            "name":{"type":"String"},
            "age":{"type":"integer"},
            "bir":{"type":"date"}
          }
        }
      }
    }
    # 7版本的由于只能创建一个类型,并且没有了string类型
    PUT /szw
    {
      "mappings": {
          "properties":{
            "id":{"type":"keyword"},
            "name":{"type":"keyword"},
            "age":{"type":"integer"},
            "bir":{"type":"date"}
          }
        }
    }
    

    查看创建的索引以及类型中的映射:

    Get /szw/_mapping
    

    文档操作

    # 文档操作:插入一条文档,put /索引/类型/1
    PUT /szw/person/1
    {
      "id":1,
      "name":"szw",
      "age":21,
      "bir":"1999-01-01"
    }
    
    
    # 我们也可以让rest中的1忽略不写,不过就要使用post请求,这样就意味先让es帮我们创建一个id,再去修改上面的值
    
    POST /szw/person
    {
      "id":2222,
      "name":"szw",
      "age":21,
      "bir":"1999-01-01"
    }
    
    返回的result:
    
    
    {
      "_index" : "szw",
      "_type" : "person",
      "_id" : "3kjB1nUBPN-1jv0gnoHm", # 我们可以看到这里的id是自动生成的
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 1,
      "_primary_term" : 1
    }
    
    
    # 查询文档中的一条记录
    
    GET /szw/person/1
    
    
    # 删除一条文档
    DELETE /szw/person/1
    
    # 更新一条文档
    
    ---1、这种情况会丢失原有的数据,先删除,再添加,我门再去查这个数据,未修改的数据就不存在了
    
    POST /szw/person/1
    {
      "name":"xiaomengzi"
    }
    
    --2、会保留原有的数据,并且如果你在更新的时候添加了新字段,不报错,es也会自动帮你生成,_update和doc都需要加上
    
    POST /szw/person/4/_update
    {
      "doc":{
        "name":"qqqq",
        "sex":"mele"
      }
    }
    
    --3、脚本更新
    
    # 基于脚本的更新
    
    POST /szw/person/4/_update
    {
      "script":"ctx._source.age+=3"
    }
    
    

    批量操作

    # 文档的批量操作 _bulk批量操作,index 添加、delete删除、update修改
    PUT /szw/person/_bulk
    {"index":{"_id":"10"}}
      {"name":"新增的文档","age":"18"}
    {"delete":{"_id":"1"}}
    {"update":{"_id":"4"}}
      {"doc":{"name":"hhhh","age":"222"}}
    

    我们可以看一下返回的数据,操作的每条文档都会显示

    #! Deprecation: [types removal] Specifying types in bulk requests is deprecated.
    {
      "took" : 66,
      "errors" : false,
      "items" : [
        {
          "index" : {
            "_index" : "szw",
            "_type" : "person",
            "_id" : "10",
            "_version" : 1,
            "result" : "created",
            "_shards" : {
              "total" : 2,
              "successful" : 1,
              "failed" : 0
            },
            "_seq_no" : 11,
            "_primary_term" : 1,
            "status" : 201
          }
        },
        {
          "delete" : {
            "_index" : "szw",
            "_type" : "person",
            "_id" : "1",
            "_version" : 4,
            "result" : "deleted",
            "_shards" : {
              "total" : 2,
              "successful" : 1,
              "failed" : 0
            },
            "_seq_no" : 12,
            "_primary_term" : 1,
            "status" : 200
          }
        },
        {
          "update" : {
            "_index" : "szw",
            "_type" : "person",
            "_id" : "4",
            "_version" : 4,
            "result" : "updated",
            "_shards" : {
              "total" : 2,
              "successful" : 1,
              "failed" : 0
            },
            "_seq_no" : 13,
            "_primary_term" : 1,
            "status" : 200
          }
        }
      ]
    }
    
    

    我们可以看到,批量操作返回的并不是一条结果,而是多条结果。所以这个批量操作不是一个原子性的操作,删除、修改等各部分间不受影响,后续执行与之前没关系。

    为什么不是原子操作?

    因为es我们通常是用来做检索的,所以通常会弱化事务或者是根本没有事务。

    高级搜索Query

    要知道:全文检索:索 对文档创建索引的过程。检索:查询条件

    检索的两种方式_search

    es查询方式
    • 第一种适用于简单的查询,将参数直接挂在url上,但是无法书写复杂的查询
    • 第二种是将查询的条件以json的格式书写,这种格式会十分的公正

    使用语法

    # URL查询:get/索引/类型/_search/参数
    
    # DSL查询: get/索引/类型/_search
    {
        “query”
    }
    
    

    首先批量插入数据

    PUT /szw/_bulk
    {"index":{"_id":"11"}}
      {"name":"邵哈哈哈","age":"18","id":"2"}
    {"index":{"_id":"10"}}
      {"name":"qqq","age":"18","id":"4"}
    {"index":{"_id":"8"}}
      {"name":"szw","age":"19","id":"5"}
    {"index":{"_id":"5"}}
      {"name":"ssss","age":"20","id":"8"}
    
    # 也可以不指定索引文档id
    PUT /szw/_bulk
    {"index":{}}
      {"name":"随机的","age":"18","id":"2"}
    {"index":{}}
      {"name":"随机","age":"18","id":"4"}
    {"index":{}}
      {"name":"szw","age":"19","id":"5"}
    {"index":{}}
      {"name":"ssss","age":"20","id":"8"}
    

    URL检索

    # 基于age进行排序,默认升序asc,des是降序
    GET /szw/_search?q=*
    
    GET /szw/_search?q=*&sort=age:des
    
    # 我们可以将上面批量添加数据的id去除掉,这样可以批量添加很多条数据
    # 再经过查询,我们可以发现每次es会给我们返回10条数据,这就很像分页了,那么我们如何做到es中的分页呢?
    
    GET /szw/_search?q=*&sort=age:asc&size=2&from=3
    
    # 上面这个就代表,按每页2条记录分页,查询第几条数据,(当前页-1)*size
    
    GET /szw/_search?q=*&sort=age:asc&size=2&_source=name
    # 上面这种查询的source代表 只返回name字段的数据
    

    DSL格式查询

    # dsl查询 match_all默认每页返回十条,不进行分页
    
    GET /szw/_search
    {
      "query": {
        "match_all": {}
      }
    }
    
    # del 查询带排序,排序不可以排text类型的字段,因为该类型的需要进行分词!
    GET /szw/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": [
        {
          "name": {
            "order": "desc"
          }
        },
         {
          "age": {
            "order": "desc"
          }
        }
      ]
    }
    # del查询 分页 size form,form作用是第几条开始查询
    GET /szw/_search
    {
      "query": {
        "match_all": {}
      },
      "size": 3,
      "from": 0
    }
    
    # 指定返回的字段返回
    GET /szw/_search
    {
      "query": {
        "match_all": {}
      },
      "_source": "name"
    }
    
    
    # 指定返回多个字段,_source中就要放入一个数组
    GET /szw/_search
    {
      "query": {
        "match_all": {}
      },
      "_source": ["name","age"]
    }
    
    
    

    返回值介绍

    {
      "took" : 3,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 9,
          "relation" : "eq"
        },
        "max_score" : 1.0,
        "hits" : [
          {
            "_index" : "szw",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.0,
            "_source" : {
              "name" : "szw",
              "age" : 21
            }
          },
          }
        ]
      }
    }
    

    took:查询结果的毫秒ms数”
    time_out:是否超时,成功查询到就是false
    shards:分片
    max_score:查询的最大分数,由于这里做的查询是查询所有,所以每个文档的分数都是1

    重要!
    基于关键词查询:

    # 使用term查询  基于关键词
    
    # 结果:可以查到
    GET szw/_search
    {
      "query": {"term": {
        "name": {
          "value": "邵哈哈哈"
        }
      }}
    }
    
    
    # 查不到结果
    GET szw/_search
    {
      "query": {"term": {
        "name": {
          "value": "哈哈"
        }
      }}
    }
    
    
    
    • es中关键字查询中,用的是标准分词,中文是单字分词,英文是单词分词,但是要注意的是只有text类型才分词, 其他类型都是不分词的!上面查不到结果的原因就是这个!

    查看默认的分词效果

    GET _analyze
    {
      "text":"ssssss 是 啦啦啦啦啦"
    }
    

    返回结果

    我们可以看到返回结果ssss作为单词是没有分的,而中文都是按字分词的。

    {
      "tokens" : [
        {
          "token" : "ssssss",
          "start_offset" : 0,
          "end_offset" : 6,
          "type" : "<ALPHANUM>",
          "position" : 0
        },
        {
          "token" : "是",
          "start_offset" : 7,
          "end_offset" : 8,
          "type" : "<IDEOGRAPHIC>",
          "position" : 1
        },
        {
          "token" : "啦",
          "start_offset" : 9,
          "end_offset" : 10,
          "type" : "<IDEOGRAPHIC>",
          "position" : 2
        },
        {
          "token" : "啦",
          "start_offset" : 10,
          "end_offset" : 11,
          "type" : "<IDEOGRAPHIC>",
          "position" : 3
        },
        {
          "token" : "啦",
          "start_offset" : 11,
          "end_offset" : 12,
          "type" : "<IDEOGRAPHIC>",
          "position" : 4
        },
        {
          "token" : "啦",
          "start_offset" : 12,
          "end_offset" : 13,
          "type" : "<IDEOGRAPHIC>",
          "position" : 5
        },
        {
          "token" : "啦",
          "start_offset" : 13,
          "end_offset" : 14,
          "type" : "<IDEOGRAPHIC>",
          "position" : 6
        }
      ]
    }
    

    范围查询

    rang :范围查询 age,大于等于10小于等于20,e代表等于,gt大于。lt小于

    GET /szw/_search
    {
      "query": {
        "range": {
          "age": {
            "gte": 10,
            "lte": 20
          }
        }
      },
      "_source": "age"
    }
    

    前缀查询

    这种查询不管你是否分词,只要前缀匹配正确,就会返回结果

    GET /szw/_search
    {
      "query": {
        "prefix": {
          "name": {
            "value": "sz"
          }
        }
      }
    }
    

    通配符查询

    wildcard:通配符查询,?代表匹配一个关键字,*代表匹配多个关键字。
    注意:这种查询通配符不可以放在前面

    GET szw/_search
    {
      "query": {
        "wildcard": {
          "name": {
            "value": "s?"      # 这样的形式查不到name为szw的结果,而s*可以。
          }
        }
      }
    }
    

    多个ids查询

    关键字:ids

    GET szw/_search
    {
      "query": {
        "ids": {
          "values": ["1","8","5"]
        }
      }
    }
    
    

    模糊查询

    关键字:fuzzy
    当输入的搜索词,长度小于等于2,不允许错误出现
    长度为3-5,允许一个出错
    长度为6或以上,允许两个出错

    GET /szw/_search
    {
      "query": {
        "fuzzy": {
          "name": "lsss"
        }
      }
    }
    # 该例子为搜索es中的name为ssss的文档时,允许出错一个数据,即lsss也可以搜索到name为ssss的数据。
    

    布尔查询

    关键字:bool、must_not、should、must

    GET /szw/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "range": {
                "age": {
                  "gte": 10,
                  "lte": 20
                }
              }
            },
            {
              "prefix": {
                "name": {
                  "value": "邵"
                }
              }
            }
          ]
        }
      }
    }
    # must是必须包含所有条件
    must not是查询除了条件之外的所有结果
    should是查询条件至少包含一个的结果
    
    

    高亮查询

    高亮查询不会影响原有的数据,而是附带上高亮数据返回出来

    GET /szw/_search
    {
      "query": {
        "fuzzy": {
          "name": {
            "value": "小红爱吃水"
          }
        },
      "highlight": {
        "fields": {
          "name":{}
        }
      }
    }
    
    # 我们还可以使用pre_tags和post_tags进行对高亮数据的渲染样式。默认只有斜体。
    

    我们可以来看下查询数据:可以发现高亮的数据是单独分离出来进行显示的


    数据

    多字段分词查询

    # 多字段分词查询
    
    GET  szw/_search
    {
      "query": {
        "multi_match": {
          "query": "小",
          "fields": ["name","date"]
        }
      }
    }
    
    
    # 和上面的无差别使用,但是这个可以选择分词器
    GET szw/_search
    {
      "query": {
        "query_string": {
          "default_field": "name",
          "query": "小红爱吃水果",
          "analyzer": ""
        }
      }
    }
    
    

    ES索引的底层原理

    当我们在创建一条文档的时候,文档首先去建立索引,建立索引的时候就会进行分词(text类型),默认的分词器,英文按单词分词,中文按每个字分词。除了text类型,其他不分词,将文档中的数据处理完之后,保存到索引区,而完整的数据存放在元数据区,当我们进行搜索时,会先进行找索引区的索引,然后再根据索引命中元数据区中的文档,返回结果。另外图中的显示的索引区只是一个简化,索引区还会存放着文档相关的长度、等等数据。


    es索引库原理

    ik分词器

    默认的分词器对于我们中文很不友好,我们可以使用ik分词器进行分词,首先我们应该先去安装ik分词器,然后才可以进行使用。

    # 首先进入docker的es容器中
    
    docker exec -it  容器号 bash
    
    
    # 安装ik分词器,注意这里应该与自己es版本号对应
    elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.3.0/elasticsearch-analysis-ik-7.3.0.zip 
    
    # 安装结束后,进行重启容器
    

    安装完ik分词器插件后,我们可以在es的根目录下的plugins看到ik。
    进行测试:

    
    # 测试ik分词器
    
    GET /_analyze
    {
      "text": "中科软科技股份有限公司",
      "analyzer": "ik_max_word"
    }
    

    那么对于网络热词,我们又该怎么处理呢?
    我们可以参考下github上的教程:https://github.com/medcl/elasticsearch-analysis-ik
    在config目录下的ik中进行配置。

    -rw-rw---- 1 elasticsearch root     625 Nov 28 03:10 IKAnalyzer.cfg.xml
    -rw-rw---- 1 elasticsearch root 5225922 Nov 28 03:10 extra_main.dic
    -rw-rw---- 1 elasticsearch root   63188 Nov 28 03:10 extra_single_word.dic
    -rw-rw---- 1 elasticsearch root   63188 Nov 28 03:10 extra_single_word_full.dic
    -rw-rw---- 1 elasticsearch root   10855 Nov 28 03:10 extra_single_word_low_freq.dic
    -rw-rw---- 1 elasticsearch root     156 Nov 28 03:10 extra_stopword.dic
    -rw-rw---- 1 elasticsearch root 3058510 Nov 28 03:10 main.dic
    -rw-rw---- 1 elasticsearch root     123 Nov 28 03:10 preposition.dic
    -rw-rw---- 1 elasticsearch root    1824 Nov 28 03:10 quantifier.dic
    -rw-rw---- 1 elasticsearch root     164 Nov 28 03:10 stopword.dic
    -rw-rw---- 1 elasticsearch root     192 Nov 28 03:10 suffix.dic
    -rw-rw---- 1 elasticsearch root     752 Nov 28 03:10 surname.dic
    
    
    配置扩展词典和停用词典

    相关文章

      网友评论

          本文标题:ES

          本文链接:https://www.haomeiwen.com/subject/mtvobktx.html