• 全文检索工具elasticsearch:第一章:理论知识


    搜索

    什么是搜索, 计算机根据用户输入的关键词进行匹配,从已有的数据库中摘录出相关的记录反馈给用户。 

    常见的全网搜索引擎,像百度、谷歌这样的。但是除此以外,搜索技术在垂直领域也有广泛的使用,比如淘宝、京东搜索商品,万芳、知网搜索期刊,csdn中搜索问题贴。也都是基于海量数据的搜索。

       如何处理搜索

        用传统关系性数据库

    弊端:  

    1、 对于传统的关系性数据库对于关键词的查询,只能逐字逐行的匹配,性能非常差。

    2、匹配方式不合理,比如搜索“小密手机” ,如果用like进行匹配, 根本匹配不到。但是考虑使用者的用户体验的话,除了完全匹配的记录,还应该显示一部分近似匹配的记录,至少应该匹配到“手机”。

       专业全文索引是怎么处理的

         全文搜索引擎目前主流的索引技术就是倒排索引的方式。

       传统的保存数据的方式都是

          记录→单词

    而倒排索引的保存数据的方式是

       单词→记录

    例如

      搜索“红海行动”

    但是数据库中保存的数据如图:

    那么搜索引擎是如何能将两者匹配上的呢?

    基于分词技术构建倒排索引

    首先每个记录保存数据时,都不会直接存入数据库。系统先会对数据进行分词,然后以倒排索引结构保存。如下:

    然后等到用户搜索的时候,会把搜索的关键词也进行分词,会把“红海行动”分词分成:红海行动两个词。

    这样的话,先用红海进行匹配,得到id=1和id=2的记录编号,再用行动匹配可以迅速定位id为1,3的记录。

    那么全文索引通常,还会根据匹配程度进行打分,显然1号记录能匹配的次数更多。所以显示的时候以评分进行排序的话,1号记录会排到最前面。而2、3号记录也可以匹配到。

       全文检索工具elasticsearch

      lucene与elasticsearch

    咱们之前讲的处理分词,构建倒排索引,等等,都是这个叫lucene的做的。那么能不能说这个lucene就是搜索引擎呢?

    还不能。lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。

    好比lucene是类似于jdk,而搜索引擎软件就是tomcat 的。

    目前市面上流行的搜索引擎软件,主流的就两款,elasticsearch和solr,这两款都是基于lucene的搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了服务器安装、部署、管理、集群以外,对于数据的操作,修改、添加、保存、查询等等都十分类似。就好像都是支持sql语言的两种数据库软件。只要学会其中一个另一个很容易上手。

    从实际企业使用情况来看,elasticSearch的市场份额逐步在取代solr,国内百度、京东、新浪都是基于elasticSearch实现的搜索功能。国外就更多了 像维基百科、GitHub、Stack Overflow等等也都是基于ES的

     

      elasticSearch的使用场景

    1. 为用户提供按关键字查询的全文搜索功能。
    2. 著名的ELK框架(ElasticSearch,Logstash,Kibana),实现企业海量日志的处理分析的解决方案。大数据领域的重要一份子。

      elasticSearch的安装

    全文检索工具elasticsearch:第二章:安装配置

       elasticsearch的基本概念

    cluster

    整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。

    node

    集群中的一个节点,一般只一个进程就是一个node

    shard

    分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。

    index

    相当于rdbms的database, 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。

    type

    类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合。

    document

    类似于rdbms的 row、面向对象里的object

    field

    相当于字段、属性

     

     

       利用kibana学习 elasticsearch restful api (DSL)

    执行bin目录下的kibana程序:

    cd /opt/kibana-5.6.4-linux-x86_64/bin

     ./kibana

       es中保存的数据结构

    public class  Movie {

     String id;

         String name;

         Double doubanScore;

         List<Actor> actorList;

    }

    public class Actor{

    String id;

    String name;

    }

    这两个对象如果放在关系型数据库保存,会被拆成2张表,但是elasticsearch是用一个json来表示一个document。

    所以他保存到es中应该是:

    {

      “id”:”1”,

      “name”:”operation red sea”,

      “doubanScore”:”8.5”,

      “actorList”:[  

    {“id”:”1”,”name”:”zhangyi”},

    {“id”:”2”,”name”:”haiqing”},

    {“id”:”3”,”name”:”zhanghanyu”}

    ]

    }

     

     对数据的操作增删改查

    查看es中有哪些索引

    GET /_cat/indices?v

    es 中会默认存在一个名为.kibana的索引

    health status index   uuid                   pri rep docs.count docs.deleted store.size pri.store.size
    yellow open   .kibana sBDZ-v6YQMWx9GaQOmSQQg   1   1          1            0      3.2kb          3.2kb

    表头的含义

    health

    green(集群完整) yellow(单点正常、集群不完整) red(单点不正常)

    status

    是否能使用

    index

    索引名

    uuid

    索引统一编号         

    pri

    主节点几个

    rep

    从节点几个

    docs.count

    文档数

    docs.deleted

    文档被删了多少

    store.size

    整体占空间大小

    pri.store.size

    主节点占

     

    增加一个索引

    PUT /movie_index

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

    删除一个索引

          ES 是不删除也不修改任何数据

    DELETE /movie_index

    {
      "acknowledged": true
    }

     新增文档 

    格式 :PUT /index/type/id

    PUT /movie_index/movie/1

    { "id":1,

      "name":"operation red sea",

      "doubanScore":8.5,

      "actorList":[  

    {"id":1,"name":"zhang yi"},

    {"id":2,"name":"hai qing"},

    {"id":3,"name":"zhang han yu"}

    ]

    }

    PUT /movie_index/movie/2

    {

      "id":2,

      "name":"operation meigong river",

      "doubanScore":8.0,

      "actorList":[  

    {"id":3,"name":"zhang han yu"}

    ]

    }

    PUT /movie_index/movie/3

    {

      "id":3,

      "name":"incident red sea",

      "doubanScore":5.0,

      "actorList":[  

    {"id":4,"name":"zhang chen"}

    ]

    }

    如果之前没建过index或者type,es 会自动创建。

     直接用id查找

    GET movie_index/movie/1

     

     修改整体替换

    和新增没有区别

    PUT /movie_index/movie/3

    {

      "id":"3",

      "name":"incident red sea",

      "doubanScore":"5.0",

      "actorList":[  

    {"id":"1","name":"zhang chen"}

    ]

    }

    修改—某个字段

    POST movie_index/movie/3/_update

    {

      "doc": {

        "doubanScore":"7.0"

      }

    }

    修改—某个字段和 修改整体替换二者选一,否则:

     删除一个document

    DELETE movie_index/movie/3

    {
      "found": true,
      "_index": "movie_index",
      "_type": "movie",
      "_id": "3",
      "_version": 18,
      "result": "deleted",
      "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
      }
    }

    搜索type全部数据

    GET movie_index/movie/_search

    结果

    {

      "took": 2,    //耗费时间 毫秒

      "timed_out": false, //是否超时

      "_shards": {

        "total": 5,   //发送给全部5个分片

        "successful": 5,

        "skipped": 0,

        "failed": 0

      },

      "hits": {

        "total": 3,  //命中3条数据

        "max_score": 1,   //最大评分

        "hits": [  // 结果

          {

            "_index": "movie_index",

            "_type": "movie",

            "_id": 2,

            "_score": 1,

            "_source": {

              "id": "2",

              "name": "operation meigong river",

              "doubanScore": 8.0,

              "actorList": [

                {

                  "id": "1",

                  "name": "zhang han yu"

                }

              ]

            }

              。。。。。。。。

              。。。。。。。。

          }

     

     按条件查询(全部)

    GET movie_index/movie/_search

    {

      "query":{

        "match_all": {}

      }

    }

    按分词查询 

    GET movie_index/movie/_search

    {

      "query":{

        "match": {"name":"red"}

      }

    }

    注意结果的评分

    按分词子属性查询 

    GET movie_index/movie/_search

    {

      "query":{

        "match": {"actorList.name":"zhang"}

      }

    }

     结果:

    {
      "took": 2,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
      },
      "hits": {
        "total": 2,
        "max_score": 1,
        "hits": [
          {
            "_index": "movie_index",
            "_type": "movie",
            "_id": "2",
            "_score": 1,
            "_source": {
              "id": 2,
              "name": "operation meigong river",
              "doubanScore": 8,
              "actorList": [
                {
                  "id": 3,
                  "name": "zhang han yu"
                }
              ]
            }
          },
          {
            "_index": "movie_index",
            "_type": "movie",
            "_id": "1",
            "_score": 1,
            "_source": {
              "id": 1,
              "name": "operation red sea",
              "doubanScore": 8.5,
              "actorList": [
                {
                  "id": 1,
                  "name": "zhang yi"
                },
                {
                  "id": 2,
                  "name": "hai qing"
                },
                {
                  "id": 3,
                  "name": "zhang han yu"
                }
              ]
            }
          }
        ]
      }
    }

    match phrase

    GET movie_index/movie/_search

    {

        "query":{

          "match_phrase": {"name":"operation red"}

        }

    }

    按短语查询,不再利用分词技术,直接用短语在原始数据中匹配

    我就不发结果了,太长的博客也不好看。

      fuzzy查询

    GET movie_index/movie/_search

    {

        "query":{

          "fuzzy": {"name":"rad"}

        }

    }

    校正匹配分词,当一个单词都无法准确匹配,es通过一种算法对非常接近的单词也给与一定的评分,能够查询出来,但是消耗更多的性能。

     

    过滤--查询后过滤

    GET movie_index/movie/_search

    {

        "query":{

          "match": {"name":"red"}

        },

        "post_filter":{

          "term": {

            "actorList.id": 3

          }

        }

    }

    先查询后过滤效率慢,好比,我先从全国所有人中先过滤其他省份的留下广东的,再查询比先查询全国所有人再过滤广东的

    过滤--查询前过滤(推荐)

    GET movie_index/movie/_search

    {

        "query":{

            "bool":{

              "filter":[ {"term": {  "actorList.id": "1"  }},

                         {"term": {  "actorList.id": "3"  }}

               ],

               "must":{"match":{"name":"red"}}

             }

        }

    }

     

     过滤--按范围过滤

    GET movie_index/movie/_search

    {

       "query": {

         "bool": {

           "filter": {

             "range": {

                "doubanScore": {"gte": 8}

             }

           }

         }

       }

    }

    关于范围操作符:

    gt

    大于

    lt

    小于

    gte

    大于等于

    lte

    小于等于

      排序

    GET movie_index/movie/_search

    {

      "query":{

        "match": {"name":"red sea"}

      }

      , "sort": [

        {

          "doubanScore": {

            "order": "desc"

          }

        }

      ]

    }

    这个先按名称后按red sea排序

     分页查询

    GET movie_index/movie/_search

    {

      "query": { "match_all": {} },

      "from": 1,

      "size": 1

    }

     指定查询的字段

    GET movie_index/movie/_search

    {

      "query": { "match_all": {} },

      "_source": ["name", "doubanScore"]

    }

    太多了,但我坚持,希望尽最大努力把我知道的全部写出来。

    高亮

    GET movie_index/movie/_search

    {

        "query":{

          "match": {"name":"red sea"}

        },

        "highlight": {

          "fields": {"name":{} }

        }  

    }

    聚合

    取出每个演员共参演了多少部电影

    GET movie_index/movie/_search

    {

      "aggs": {

        "groupby_actor": {

          "terms": {

            "field": "actorList.name.keyword"  

          }

        }

      }

    }

    每个演员参演电影的平均分是多少,并按评分排序

    GET movie_index/movie/_search

    {

      "aggs": {

        "groupby_actor_id": {

          "terms": {

            "field": "actorList.name.keyword" ,

            "order": {

              "avg_score": "desc"

              }

          },

          "aggs": {

            "avg_score":{

              "avg": {

                "field": "doubanScore"

              }

            }

           }

        }

      }

    }

     关于mapping的类型

    之前说type可以理解为table,那每个字段的数据类型是如何定义的呢

    查看看mapping

    GET movie_index/_mapping/movie

    {
      "movie_index": {
        "mappings": {
          "movie": {
            "properties": {
              "actorList": {
                "properties": {
                  "id": {
                    "type": "long"
                  },
                  "name": {
                    "type": "text",
                    "fields": {
                      "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                      }
                    }
                  }
                }
              },
              "doubanScore": {
                "type": "float"
              },
              "id": {
                "type": "long"
              },
              "name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              }
            }
          }
        }
      }
    }

    实际上每个type中的字段是什么数据类型,由mapping定义。

    但是如果没有设定mapping系统会自动,根据一条数据的格式来推断出应该的数据格式。

    1. true/false → boolean
    2. 1020  →  long
    3. 20.1 → double
    4. “2017-02-01” → date
    5. “hello world” → text +keyword

    默认只有text会进行分词,keyword是不会分词的字符串。

    mapping除了自动定义,还可以手动定义,但是只能对新加的、没有数据的字段进行定义。一旦有了数据就无法再做修改了。

    注意:虽然每个Field的数据放在不同的type下,但是同一个名字的Field在一个index下只能有一种mapping定义。

     中文分词

    elasticsearch本身自带的中文分词,就是单纯把中文一个字一个字的分开,根本没有词汇的概念。但是实际应用中,用户都是以词汇为条件,进行查询匹配的,如果能够把文章以词汇为单位切分开,那么与用户的查询条件能够更贴切的匹配上,查询速度也更加快速。

    分词器下载网址:https://github.com/medcl/elasticsearch-analysis-ik

    安装

    下载好的zip包,请解压后放到 /usr/share/elasticsearch/plugins/

     cp /opt/elasticsearch-analysis-ik-5.6.4.zip  elastic

    unzip elastic

    rm elastic

    然后重启es

    service elasticsearch stop

    service elasticsearch start

    测试使用

    使用默认

    GET movie_index/_analyze

    {  

      "text": "我是中国人"

    }

        请观察结果

     使用分词器

    GET movie_index/_analyze

    {  "analyzer": "ik_smart",

      "text": "我是中国人"

    }

    请观察结果

       另外一个分词器

        ik_max_word

    GET movie_index/_analyze

    {  "analyzer": "ik_max_word",

      "text": "我是中国人"

    }

    请观察结果

    能够看出不同的分词器,分词有明显的区别,所以以后定义一个type不能再使用默认的mapping了,要手工建立mapping, 因为要选择分词器。

    基于中文分词搭建索引

    1、建立mapping

    PUT movie_chn

    {

      "mappings": {

        "movie":{

          "properties": {

            "id":{

              "type": "long"

            },

            "name":{

              "type": "text"

              , "analyzer": "ik_smart"

            },

            "doubanScore":{

              "type": "double"

            },

            "actorList":{

              "properties": {

                "id":{

                  "type":"long"

                },

                "name":{

                  "type":"keyword"

                }

              }

            }

          }

        }

      }

    }

     

    插入数据

    PUT /movie_chn/movie/1

    { "id":1,

      "name":"红海行动",

      "doubanScore":8.5,

      "actorList":[  

      {"id":1,"name":"张译"},

      {"id":2,"name":"海清"},

      {"id":3,"name":"张涵予"}

     ]

    }

    PUT /movie_chn/movie/2

    {

      "id":2,

      "name":"湄公河行动",

      "doubanScore":8.0,

      "actorList":[  

    {"id":3,"name":"张涵予"}

    ]

    }

    PUT /movie_chn/movie/3

    {

      "id":3,

      "name":"红海事件",

      "doubanScore":5.0,

      "actorList":[  

    {"id":4,"name":"张晨"}

    ]

    }

    查询测试

    GET /movie_chn/movie/_search

    {

      "query": {

        "match": {

          "name": "红海战役"

        }

      }

    }

    GET /movie_chn/movie/_search

    {

      "query": {

        "term": {

          "actorList.name": "张译"

        }

      }

    }

    自定义词库

    修改/usr/share/elasticsearch/plugins/ik/config/中的IKAnalyzer.cfg.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

    <properties>

            <comment>IK Analyzer 扩展配置</comment>

            <!--用户可以在这里配置自己的扩展字典 -->

            <entry key="ext_dict"></entry>

             <!--用户可以在这里配置自己的扩展停止词字典-->

            <entry key="ext_stopwords"></entry>

            <!--用户可以在这里配置远程扩展字典 -->

             <entry key="remote_ext_dict">http://192.168.67.163/fenci/myword.txt</entry>

            <!--用户可以在这里配置远程扩展停止词字典-->

            <!-- <entry key="remote_ext_stopwords">words_location</entry> -->

    </properties>

    按照标红的路径利用nginx发布静态资源

    在nginx.conf中配置

      server {

            listen  80;

            server_name  192.168.67.163;

            location /fenci/ {

               root es;

        }

       }

     

    并且在/usr/local/nginx/下建/es/fenci/目录,目录下加myword.txt

    myword.txt中编写关键词,每一行代表一个词。

     

    然后重启es服务器,重启nginx。

    在kibana中测试分词效果

    更新完成后,es只会对新增的数据用新词分词。历史数据是不会重新分词的。如果想要历史数据重新分词。需要执行:

    POST movies_index_chn/_update_by_query?conflicts=proceed

    有点长了,分章节吧,第三章继续写。

  • 相关阅读:
    平衡树之splay BZOJ3224 普通平衡树
    线段树 洛谷P1531 I Hate It
    倍增LCA code[vs]1036商务旅行
    线段树 hdu1698 Just a Hook
    猥琐的暴搜 NOIP2011 Mayan游戏
    [BZOJ2301][HAOI2011]Problem b
    [BZOJ1101][POI2007]Zap
    [BZOJ1100][POI2007]对称轴osi
    [BZOJ3167][Heoi2013]Sao
    [BZOJ3039]玉蟾宫
  • 原文地址:https://www.cnblogs.com/javawxid/p/12812029.html
Copyright © 2020-2023  润新知