V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
CoolSpring
V2EX  ›  Elasticsearch

Elasticsearch 全文搜索引擎的个人学习笔记

  •  3
     
  •   CoolSpring · 2020-03-14 19:48:39 +08:00 · 4609 次点击
    这是一个创建于 1744 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这篇文章是我对自己在自学探索 Elasticsearch 全文搜索功能时学到的概念的一个总结(如果是大佬,请手下留情),大概算是 A Quick Glimpse Into Elasticsearch (Full-Text Search Part),并不一定能帮助读者建构起对于 Elasticsearch 本身客观成体系的认识。(实际上如果只是为了使用 Elasticsearch,也并不需要完全了解其工作机制。)如有需要可参考文末的官方文档和参考资料。

    为什么用 Elasticsearch 来做全文检索,而不是 MySQL 等数据库自带的检索功能?

    • 速度更快——在 full-text search 领域性能更佳,数据量越大时会越明显。

    • 效果更好——搜索条件可以非常灵活,分布式设计易于扩展。

    (当然 Elasticsearch 还有分析、聚合等其他功能,而 MySQL 等关系型数据库也有其更广泛丰富的需求,两者的定位并不相同。一般推荐将常规数据库作为业务数据的主要存储处,向 Elasticsearch 导入其中的部分数据进行索引以方便搜索。)

    怎么安装?

    https://www.elastic.co/cn/downloads/elasticsearch 有详细的逐步介绍。

    没有什么特殊的依赖、配置等,只需要下载解压运行,或者 yum / apt / homebrew,抑或 docker。

    安装完成后,Elasticsearch Server 默认运行于本机 9200 端口,提供 REST API——这意味着现在可以通过普通的浏览器来尝试访问。http://localhost:9200/ (另有 9300 端口供 TCP 协议客户端使用和节点间通信)

    可以通过 bulk 来批量导入 JSON 形式的数据,或者使用 Logstash 等从已有数据库中导入。

    基础概念简介

    • Document 文档:序列化为 JSON 格式存储、可被索引的一个数据单元,被指定了唯一 ID。一篇文档可包含多个字段。

    • Field 字段:键-值对,字段可被定义为某一类型,如 text (适用于全文搜索),keyword (适用于仅会用于精确搜索的用途,如"status": "published"),integer,date,boolean,nested 等。

    • Index 索引:所谓的“全文搜索”不是真的在每次搜索时读取一遍全部文本,而是先建立索引,需要搜索时再查询索引。这里指的是 inverted index,而 Elasticsearch index 作为名词是另一个概念,见下文。

    • Mapping 映射:文档结构,类似于 MySQL 的 Schema,在创建 index 时指定(之后可修改),定义了文档包含字段的名称及类型。Elasticsearch 也可以在不定义 mapping 的情况下使用( schema-less ),此时它会通过第一个存入的数据推断字段类型。GET /<index>/_mapping 可以查看当前<index>的 mapping。</index></index>

    • Analyzer 分词器:索引时首先要分词才能建立索引。搜索时也是如此,若搜索的不止一个词,而是包含多个词的短语或句子,则先用分词器分离,再进行搜索。英文的分词显然较为容易,中文分词推荐 ik_max_word (最细粒度拆分出尽可能多的词语组合,官方说“适合 Term Query”)和 ik_smart (最粗粒度的拆分,“适合 Phrase 查询”)。搜索和索引的分词器可以不同,若在搜索时未指定分词器,则默认为索引的默认分词器。

    • Cluster 集群,Node 节点,Index (Indices) 索引, Shard 切片, Replica 副本切片:Elasticsearch 是分布式设计,一个对外提供服务的 Elasticsearch Server 即是一个 Cluster 集群,可以添加 Node 节点以提升性能、容量与冗余,存入的数据与收到的查询请求将被自动分配。(在同一台机器上运行多个 Node 也是可行的。)一个 Elasticsearch index 是文档的集合,这些文档一般具有相似特征。在增删改查这批文档时需要指定 index 的名字。Indices 是 index 的复数形式。一个 Elasticsearch Index 包含一个或多个 Shard 切片,一个切片实际上是一个 Lucene Index 实例( Apache Lucene 是 Elasticsearch 的底层基础),可以说 Shard 是数据的容器,但与使用 ES 服务的应用程序直接交互的是 Index。Shard 分为 primary shard 主切片和 replica shard 副本切片,文档存储于主切片中,而副本切片是主切片的一份拷贝,提供冗余并处理请求。

    • Near Real-Time 准实时:在数据存入后,它就在 1 秒内被索引而可查询。

    • Query DSL 查询专用语言:一套描述搜索条件的语言,格式为 JSON。

    • Score:搜索结果中的某篇文档与搜索要求的契合程度。一般情况下是相关度,目前 Elasticsearch 默认采用 BM25 算法,过去曾使用过 TF/IDF (词频 /逆向文档频率)。也可以在搜索请求中指定,当满足某条件时增加或减少评分,或直接给出固定分值。

    Query DSL 中和全文搜索相关的一些查询关键词

    建议使用 curl 命令来测试下面的查询。官方给出的示例:

    curl -XGET 'http://localhost:9200/twitter/_search?pretty=true' -H 'Content-Type: application/json' -d '
    {
        "query" : {
            "match" : { "user": "kimchy" }
        }
    }'
    

    如果要用浏览器 URI 进行简单搜索则需要用到 Lucene 查询语法(参考: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.htmlhttps://lucene.apache.org/core/2_9_4/queryparsersyntax.html ),给个例子(注意 AND ): http://localhost:9200/post_index/_search?q=user:CoolSpring%20AND%20content:Surface&size=30&pretty。

    • match: 最常用的查询方式。若输入的是短语或句子,则 Elasticsearch 会先用分词器分开,再进行搜索。只关心这些词是否都出现,而不考虑它们之间的距离。

      {
        "query": {
          "match": {
            "content": "quick fox"
          }
        }
      }
      
    • match_phrase: 短语匹配。将查询内容分词成为词的列表后,搜索时考虑它们在文本中的相对位置。可以指定一个 slop 值,即允许的最大距离。slop 默认为 0,此时可以看作“完全匹配”(均出现,且相邻)。

      {
        "query": {
          "match_phrase": {
            "content": "包含一字不差的内容才显示为结果"
          }
        }
      }
      
    • term: 精确匹配。比较适合用在 keyword 类型的字段,或者 board_id, topic_id, user_id 这种精确值字段( exact value string field )。

      {
          "query": {
              "term": {
                  "user": {
                      "value": "Kimchy",
                      "boost": 1.0
                  }
              }
          }
      }
      
    • bool: 用以连接多个布尔查询( must, must_not, should, filter )

      • must: 如其名,必须出现。

      • must_not: 不能出现。

      • should: “应该”出现。

      • filter: 必须出现。与 must 不同的是查询的评分将被忽略。

    包含多个词的 match 查询经过分词后可以改写,前述第一个 match 查询示例在内部等效于

    {
    "query": {
      "bool": {
        "should": [
          {"term": { "text": "quick" }},
          {"term": { "text": "fox"   }}
        ]
      }
    }
    }
    
    • minimum_should_match: 至少有几个 /百分之几的 should 语句匹配才可出现在结果中。

    • _source: 在索引时传递的原始 JSON 文档数据。索引与原内容并不相同,_source 本身不被索引、不可搜索,只是保存。

    • from, size: 易于理解,用于分页搜索中。

    • rescore: 对于 query 返回的前 n 个搜索结果(如 100-500 个)按照已有分数和新的条件进行重新评分排列。“match 返回的搜索结果不考虑词的距离所以效果不好”的问题可能很常见,一种方法时可以用简单的 bool{must: match, should: match_phrase}“使用邻近度提高相关度”的方式来解决:

      {
        "query": {
          "bool": {
            "must": {
              "match": { 
                "message": {
                  "query":"the quick brown",
                  "minimum_should_match": "30%"
                }
              }
            },
            "should": {
              "match_phrase": { 
                "message": {
                  "query": "the quick brown",
                  "slop":  2
                }
              }
            }
          }
        }
      }
      

      但是由于 match_phrase 比较消耗性能(相对于普通的 match 而言),并且用户一般只关心结果前几页,所以可以选择用 rescore 仅对前 50 项进行处理。

      {
         "query" : {
            "match" : {
               "message" : {
                  "operator" : "or",
                  "query" : "the quick brown"
               }
            }
         },
         "rescore" : {
            "window_size" : 50,
            "query" : {
               "rescore_query" : {
                  "match_phrase" : {
                     "message" : {
                        "query" : "the quick brown",
                        "slop" : 2
                     }
                  }
               },
               "query_weight" : 0.7,
               "rescore_query_weight" : 1.2
            }
         }
      }
      

    结语

    Elasticsearch 的使用实在很难穷尽,笔者在自学过程中还只能管窥一二,许多功能还没来得及探索。在写这篇文章的时候,关于导入文档的具体操作等重要内容也没有提及。不过有句话说得好,文档和代码是最好的学习资源。Elasticsearch 基于 Apache Lucene,分为 Apache 2.0 授权下的开源版、Elastic License 下的免费版,和商业版。(不过就普通使用的小规模搜索用途而言不大需要研究它的代码。)官方英文文档见: https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html。

    参考资料:

    1. 引用了来自官方文档的一些例子;

    2. What is ElasticSearch? Why ElasticSearch? Advantages of ElasticSearch!: https://medium.com/@AIMDekTech/what-is-elasticsearch-why-elasticsearch-advantages-of-elasticsearch-47b81b549f4d (写了一半查资料时看到这篇文章,深受启发。一部分内容参考了它,在此感谢并推荐。)

    9 条回复    2020-03-15 10:14:43 +08:00
    hugee
        1
    hugee  
       2020-03-14 20:28:21 +08:00
    感觉好重啊
    Heartbleed
        2
    Heartbleed  
       2020-03-14 20:32:36 +08:00 via Android
    这个是不是没有 Python 版本的..
    hantsy
        3
    hantsy  
       2020-03-14 20:59:58 +08:00
    @Heartbleed 服务器端跟你没关系的。一般使用都是客户端,各种语言的 Client 应该都有。
    CoolSpring
        4
    CoolSpring  
    OP
       2020-03-14 22:43:41 +08:00
    @Heartbleed
    关于客户端官方提供了两个 Python 库:更灵活自由的 low-level 的[elasticsearch-py]( https://github.com/elastic/elasticsearch-py)和更 Pythonic 的 high-level 的[elasticsearch-dsl-py]( https://github.com/elastic/elasticsearch-dsl-py)
    L00kback
        5
    L00kback  
       2020-03-14 23:03:16 +08:00 via Android
    顶一顶,在数据分析领域是一个很实用的工具。配合 kibana 做可视化太舒服了,非常快速,功能强大。
    est
        6
    est  
       2020-03-15 01:22:24 +08:00
    把 ES 调教好不容易。比如默认 shard 几个好?
    SpencerCJH
        7
    SpencerCJH  
       2020-03-15 02:43:31 +08:00
    我最近在集成阿里云的 open search,目的就是降低这一块的学习门槛...
    yepinf
        8
    yepinf  
       2020-03-15 10:01:58 +08:00
    ES 好像也是吃内存大户

    团队最后用了 solr
    ddup
        9
    ddup  
       2020-03-15 10:14:43 +08:00
    ES 用起来还是挺简单的,吃透还是要下一番功夫的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   847 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 21:32 · PVG 05:32 · LAX 13:32 · JFK 16:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.