zoukankan      html  css  js  c++  java
  • ElasticSearch

    最近在学习es;
    ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    交互方式

    基于HTTP协议,以JSON为数据格式的RESTful API

    Get 'http://localhost:3000/web/people'
    {
        "query": {
        }
    }
    

    以上语句查询了web库people表的所有数据

    存储的结构和基本概念.
    • _index:相当于关系型数据库中的数据库的概念,上一个查询的index就是web
    • _type:相当于关系型数据库中表的概念,上一个查询的type就是people
    • _id: 数据的唯一id
    • _source:该参数声明了你要查询哪些字段,不传默认显示全部字段。

    _mapping 映射

    {
       "articles": {
          "mappings": {
             "magazine": {
                "properties": {
                   "date": {
                      "type": "date",
                      "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
                   },
                   "title": {
                      "type": "text"
                   },
                   "article_id": {
                      "type": "long"
                   },
                   "type": {
                      "type": "text",
                            "fields": {
                                "keyword": {
                                    "type": "keyword",
                                    "ignore_above": 256
                                }
                            }
                    }
                }
             }
          }
       }
    }
    

    这是查询magazine这个表的mapping,如果想要查询整个数据库的mapping,去掉表名即可

    说一下常用的几个类型

    • text 和 keyword 都是string类型,区别在于keyword在存储的时候不会进行分词,用于精确精确检索。
    • long 和 integer 都是number类型
    • date 是时间类型

    query查询和filter查询区别

    • query查询会进行计算_score,来进行相关度的评分
    • filter查询,用于过滤,不会参与到评分的计算中,并且会进行缓存,所以filter查询更快

    如何选择查询和过滤

    • 使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。

    简单查询实例

    从people表中查询type等于12的前10条数据,并且更根据view进行desc排序

    Get 'http://localhost:3000/web/people'
    {
        "query":{
            "bool":{
                "filter":[
                    {
                        "term":{
                            "type":12
                        }
                    }
                ]
            }
        },
        "sort":[
            {
                "view":{
                    "order":"desc"
                }
            }
        ],
        "from":0,
        "size":10,
        "_source":[
            "people_id",
            "title"
        ]
    }
    

    等价sql:

    SELECT
    	people_id, title
    FROM
    	people 
    WHERE
    	type = 12 
    ORDER BY
    	view  DESC
    	LIMIT 10 OFFSET 0
    

    当我们想有更多的过滤条件时,就可以在filter中添加更多的查询条件(子查询)

    • term 是一个值的精确查询,这些精确值可能是数字、时间、布尔或者那些 not_analyzed(例如keyword类型) 的字符串
    • terms 是多个值的精确查询
    • range
    • 是范围查询
      • gt: > 大于(greater than)
      • lt: < 小于(less than)
      • gte: >= 大于或等于(greater than or equal to)
      • lte: <= 小于或等于(less than or equal to)
    Get 'http://localhost:3000/web/people'
    {
      "query": {
        "bool": {
          "filter": [
            {
              "term": {
                "type": 11
              }
            },
            {
              "range": {
                "times": {
                  "gte": 5,
                  "lte": 10
                }
              }
            },
            {
              "terms": {
                "car_type": [
                  1,
                  2,
                  4
                ]
              }
            }
          ]
        }
      }
    }
    

    我们往往会需要更复杂的查询,就需要用到bool组合查询支持下面四个分类数:

    must

    文档 必须 匹配这些条件才能被包含进来。

    must_not

    文档 必须不 匹配这些条件才能被包含进来。

    should

    如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。

    filter

    必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。

    {
       "query":{
            "bool": {
                "must":     { "match": { "title": "学习Elasticsearch" }},
                "must_not": { "match": { "comment":   "写的不好" }},
                "should": [
                    { "match": { "comment": "写的很棒" }}
                ],
                "filter": {
                  "bool": { 
                      "must": [
                          { "range": { "date": { "gte": "2018-01-01" }}}
                      ],
                      "must_not": [
                          { "term": { "category": "education" }}
                      ]
                  }
                }
            }
       }
    }
    

    这是一个很好的简单易懂的例子:

    它查询了时间大于等于2018-01-01、分类不是education的、并且title中含有"学习Elasticsearch"、comment中一定不含有"写的不好"的文章

    ,看起来其中的should好像并没有什么用呢,其实不是,should条件参加相关度的评分,让更符合目标的结果(commen中含有"写的很棒")排在前面。

    这其中有个小点要注意下:

    如果没有 must 语句,那么至少需要能够匹配其中的一条 should 语句。但,如果存在至少一条 must 语句,则对 should 语句的匹配没有要求。

    刚才对这个查询的描述其实并不是很准确,我们需要了解一下这个match查询

    如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串,这里涉及到一个新的概念分析器

    分析器

    一个分析器(analyzer)包含了一个或多个字符过滤器(char_filter)、一个分词器(tokenizer)、一个或多个词单元过滤器(filter)

    重点说一下分词器es默认的分析器是standard,以后关于分析器还需要详细讲一下,这里标准的分析器会把字符串分割成单独的字词,如果检索的是英文的话,倒是没有什么问题,如果是中文的话,会把检索字符串的每一个汉字分成一个词,如下:

    Post http://localhost:3000/web/_analyze?analyzer=standard
    {
        "tokens": [
            {
                "token": "学",
                "start_offset": 0,
                "end_offset": 1,
                "type": "",
                "position": 0
            },
            {
                "token": "习",
                "start_offset": 1,
                "end_offset": 2,
                "type": "",
                "position": 1
            },
            {
                "token": "elasticsearch",
                "start_offset": 2,
                "end_offset": 15,
                "type": "",
                "position": 2
            }
        ]
    }
    
    { "match": { "title": "学习Elasticsearch" }
    

    所以这个代表是在title中任意包含“学”、“习”、‘elasticsearch’中任意一个的都被检索了出来,所以需要match_phrase才能满足我们的需求,代表的是整个短语的匹配,要求 “学”、“习”、‘elasticsearch’三个词都必须出现,“习”的position比“学”大1,‘elasticsearch’比“习”的position大2,必须同时满足这3个条件。所以把上面的match换成match_phrase才是符合刚才的释义

    相关度评分

    • 词频:词在文档中出现的频度是多少? 频度越高,权重 越高 。 5 次提到同一词的字段比只提到 1 次的更相关.如果不在意词在某个字段中出现的频次,而只在意是否出现过,则可以在字段映射中禁用词频统计:"index_options": "docs"
    • 逆向文档频率:词在集合所有文档里出现的频率是多少?频次越高,权重 越低 。 常用词如 and 或 the 对相关度贡献很少,因为它们在多数文档中都会出现
    • 字段长度归一值:字段越短,字段的权重 越高 。如果词出现在类似标题 title 这样的字段,要比它出现在内容 body 这样的字段中的相关度更高。可以通过修改字段映射禁用归一值"norms": { "enabled": false }
    • 结合使用:以上三个因素——词频(term frequency)、逆向文档频率(inverse document frequency)和字段长度归一值(field-length norm)——是在索引时计算并存储的。 最后将它们结合在一起计算单个词在特定文档中的 权重

    中文检索【中文分析器】,为什么要用中文分词器

    假设有一个文档【这几天天气变热,风里也透着热浪】,采用默认分词器,它会把每一个汉字分成一个词,当你用match_phrase进行检索「热风」的时候,该文档也会被检索出来,这明显不是我们想要。不仅仅是这个原因,才有中文分析器可以分析出更符合汉语的词。

    • 这个GitHub上比较火的ik分析器
    • ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
    • ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。

    自定义分词器

    一个分析器(analyzer)包含了0个或多个字符过滤器(char_filter)、一个分词器(tokenizer)、一个或多个词单元过滤器(filter)

    分词器详细介绍

    {
        "settings":{
            "analysis":{
                "analyzer":{
                    "keywordAnalyzer":{
                        "type":"pattern",
                        "pattern":",",
                        "filter":[
                            "standard",
                            "stop",
                            "trim"
                        ]
                    }
                }
            }
        }
    }
    

    该分析器是以","作为分隔符进行分词,【"standard","stop","trim"】这个三个作为过滤器

    关键词推荐

    现在关键词字段内容格式如下,以英文逗号分开的,多个词语

    帆船,美女,红酒,大海,海螺,腰带
    期望检索出来和关键最相近的数据,设置如下的mapping,禁用了词频统计和长度归一值的计算

    {
        "keywords":{
            "type":"text",
            "norms":false,
            "index_options":"docs",
            "analyzer":"keywordAnalyzer"
        }
    }
    

    采用如下检索

    {
        "query":{
            "match":{
                "keywords":{
                    "query":"帆船,美女,红酒,大海,海螺,腰带"
                }
            }
        }
    }
    

    中文多词检索

    我想要两个词都出现的是排在前面

    {
        "query": {
            "dis_max": {
                "queries": [
                    { "match": { "title": "Quick pets" }},
                    { "match": { "body":  "Quick pets" }}
                ],
                "tie_breaker": 0.3
            }
        }
    }
    

    我们回想一下,如果是英文这样是不是就OK了?那么中文呢?

    和上面不相干,说另外一个东西,看下面

    {
        "query": {
            "dis_max": {
                "queries": [
                    { "match": { "title": "Quick pets" }},
                    { "match": { "body":  "Quick pets" }}
                ]
            }
        }
    }
    ------------------------
    {
        "multi_match": {
            "query":                "Quick pets",
            "type":                 "best_fields", 
            "fields":               [ "title", "body" ]
        }
    }
    

    上面这个查询两个相等

    但是上面的和下面的却完全不一样

    {
        "query":{
            "bool":{
                "should":[
                     { "match": { "title": "Quick pets" }},
                     { "match": { "body":  "Quick pets" }}
                ]
            }
        }
    }
    

    回到刚才的问题 如果是英文刚才那个查询dis_max(multi_match)一定程度是ok的,但是中文却是不行的,为啥?因为match,虽然可以是100%匹配,但是他并没有像match_phrase字和字之间紧紧相连。所以只能拿出最终的神奇了query_string

    {
        "query":{
            "bool":{
                "should":[
                    {
                        "query_string":{
                            "fields":[
                                "article_content"
                            ],
                            "analyzer":"ik_smart",
                            "query":"迪奥 香水",
                            "auto_generate_phrase_queries":true,
                            "default_operator":"OR",
                             "use_dis_max":true,
                            "boost":1
                        }
                    }
                ]
            }
        }
    }
    

    这个查询代表了用ik_smart分析器对迪奥 香水进行分词(mapping是ik_max_word),然后对article_content字段进行检索,要求迪奥和香水这个两个词必须出现一个或者出现两个,采用"use_dis_max":true使同时出现两个词的结果排在前面。

    查看检索逻辑

    添加 "profile": true这个参数,可以相对清楚看出es是按照什么逻辑来检索的

    {
      "query": {
        "match": {
          "relevant_keywords": {
            "query": "米歇尔,美国,奥巴马,人物,女儿,总统,家庭"
          }
        }
      },
      "size": 7,
      "from": 0,
      "_source": [
        "article_id",
        "title",
        "relevant_keywords",
        "belong_program"
      ],
      "profile": true
    }
    
  • 相关阅读:
    纯awk传参和awk脚本传参
    【转】MySQL 备份和恢复(MyISAM 和 Innodb)
    【z】多线程Java Socket编程示例
    Java,Tomcat,Mysql乱码总结
    mysql备份与同步shell脚本
    【转】Velocity用户手册中文版(学习修改版)
    wml新学的标签
    【原】小心log4j的多进程写入
    【z】Webservice 的设计和模式
    【z】linux面试题参考答案
  • 原文地址:https://www.cnblogs.com/mapleChain/p/11527895.html
Copyright © 2011-2022 走看看