Elasticsearch中核心详解

Posted roadlandscape

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch中核心详解相关的知识,希望对你有一定的参考价值。

1.文档

  在Elasticsearch中,文档以JSON格式进行存储,可以是复杂的结构,如:

{
    "_index": "haoke",
    "_type": "user",
    "_id": "1005",
    "_version": 1,
    "_score": 1,
    "_source": {
        "id": 1005,
        "name": "孙七",
        "age": 37,
        "sex": "女",
        "card": {
            "card_number": "123456789"
        }
    }
}

    其中,card是一个复杂对象,嵌套的Card对象。

    元数据(metadata)

      一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:

       _index:

        索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。

        注意:我们的索引被存储在分片(shards)中,索引只是把一个或多个分片组合在一起的逻辑空间。

      _type:

        在Elasticsearch中,使用相同类型(type)的文档表示相同的“事物”,因为他们的数据结构也是相同的。
        注意:_type 的名字可以是大写或小写,不能包含下划线或逗号。

      _id:

        id仅仅是一个字符串,它与 _index 和 _type 组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成(32位长度)。
2.查询响应

  访问:http://192.168.43.182:9200/haoke/user/69AyEXEBVAiLr6jRzMgR

{
    "_index": "haoke",
    "_type": "user",
    "_id": "69AyEXEBVAiLr6jRzMgR",
    "_version": 1,
    "found": true,
    "_source": {
        "id": 1004,
        "name": "赵六",
        "age": 19,
        "sex": "女"
    }
}

  指定响应字段访问:

    GET /haoke/user/69AyEXEBVAiLr6jRzMgR?_source=id,name

{
    "_index": "haoke",
    "_type": "user",
    "_id": "69AyEXEBVAiLr6jRzMgR",
    "_version": 1,
    "found": true,
    "_source": {
        "name": "赵六",
        "id": 1004
    }
}

    如不需要返回元数据,仅仅返回原始数据,可以这样:

      GET /haoke/user/1005/_source

{
    "id": 1004,
    "name": "赵六",
    "age": 19,
    "sex": "女"
}

    也可以这样:GET /haoke/user/1005/_source?_source=id,name

{
    "name": "赵六",
    "id": 1004
}

3.判断文档是否存在

  如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:

    HEAD /haoke/user/69AyEXEBVAiLr6jRzMgR

    技术图片

     如果不存在则返回404

4.批量操作

  批量查询:

    POST /haoke/user/_mget

{
    "ids" : [ "1001", "1003" ]
}

      如果,某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据。

  _bulk操作:

    在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。

    批量插入数据:POST /haoke/user/_bulk

{"create":{"_index":"haoke","_type":"user","_id":2001}}
{"id":2001,"name":"name1","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2002}}
{"id":2002,"name":"name2","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2003}}
{"id":2003,"name":"name3","age": 20,"sex": "男"}

      注意:最后必须有一个回车

    批量删除:POST /haoke/user/_bulk

{"delete":{"_index":"haoke","_type":"user","_id":2001}}
{"delete":{"_index":"haoke","_type":"user","_id":2002}}
{"delete":{"_index":"haoke","_type":"user","_id":2003}}

      注意:最后必须有一个回车

    其他操作类似

  一次请求多少性能最高?
    整个批量请求需要被加载到接收我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低。
    最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载。
    幸运的是,这个最佳点(sweetspot)还是容易找到的:试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以使用较小的批次。
    通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次最好保持在5-15MB大小间。
5.分页

  和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数:

    size: 结果数,默认10
    from: 跳过开始的结果数,默认0

  如果你想每页显示5个结果,页码从1到3,那请求如下:

    GET /_search?size=5
    GET /_search?size=5&from=5
    GET /_search?size=5&from=10

    应该当心分页太深或者一次请求太多的结果,结果在返回前会被排序。记住一个搜索请求常常涉及多个分片,每个分片生成自己排好序的结果,接着需要集中起来排序以确保整体排序正确。
    http://192.168.43.182:9200/haoke/user/_search?size=1&from=2

    技术图片

  在集群系统中深度分页
    假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回给它们请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。
    现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个!

    可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因。
6.映射

  前面我们创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段类型的。
  自动判断的规则如下:

    技术图片

   Elasticsearch中支持的类型如下:

    技术图片

    string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。
    text 类型,当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
    keyword类型适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合建议使用。keyword类型的字段只能通过精确值搜索到。
    创建明确类型的索引:
      PUT /fan

{
    "settings": {
        "index": {
            "number_of_shards": "2",
            "number_of_replicas": "0"
        }
    },
    "mappings": {
        "person": {
            "properties": {
                "name": {
                    "type": "text"
                },
                "age": {
                    "type": "integer"
                },
                "mail": {
                    "type": "keyword"
                },
                "bobby": {
                    "type": "text"
                }
            }
        }
    }
}

    查看映射:

      GET /fan/_mapping 

        技术图片

     插入数据:

      POST /fan/_bulk

{"index":{"_index":"fan","_type":"person"}}
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}
{"index":{"_index":"fan","_type":"person"}}
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}
{"index":{"_index":"fan","_type":"person"}}
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}
{"index":{"_index":"fan","_type":"person"}}
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳"}
{"index":{"_index":"fan","_type":"person"}}
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影"}

        注意:最后必须有一个回车

      技术图片

7.结构化查询

  term查询:

    term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):

      { "term": { "age": 26}}
      { "term": { "date": "2014-09-01" }}
      { "term": { "public": true }}
      { "term": { "tag":  "full_text" }}

    POST /fan/person/_search

{
    "query": {
        "term": {
            "age": 20
        }
    }
}

  terms查询:

    terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:

{
    "query": {
        "terms": {
            "age": [20, 21, 22]
        }
    }
}

  range查询:
    range 过滤,允许我们按照指定范围查找一批数据:

    范围操作符包含:
      gt :: 大于
      gte :: 大于等于
      lt :: 小于
      lte :: 小于等于

{
    "query": {
        "range": {
            "age": {
                "gte": 20,
                "lt": 30
            }
        }
    }
}

  exists 查询:
    exists 查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL 条件

    如果存在则返回相应的数据,否则返回空数据

{
    "query": {
        "exists": {
            "field": "card"
        }
    }
}

  match查询:
    match 查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
      如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析再进行查询字符:

{
    "query": {
        "match": {
            "hobby": "羽毛球 兵乓球"
        }
    }
}

      如果用 match 指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed 的字符串时,它将为你搜索你给定的值:

        { "match": { "age":   26      }}
        { "match": { "date":  "2014-09-01" }}
        { "match": { "public": true     }}
        { "match": { "tag":   "full_text" }}

  bool查询:

    bool 查询可以用来合并前面的多个查询,它包含一下操作符:
      must :: 多个查询条件的完全匹配,相当于 and 。
      must_not :: 多个查询条件的相反匹配,相当于 not 。
      should :: 至少有一个查询条件匹配, 相当于 or 。

{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "hobby": "足球"
                }
            },
            "must_not": {
                "hobby": "音乐"
            }
        }
    }
}

8.过滤查询

  查询年龄为20岁的用户:

{  
    "query": {    
        "bool": {      
            "filter": {        
                "term": {          
                    "age": 20       
                }     
            }   
        } 
    }
}

  查询和过滤的对比:
    一条过滤语句会询问每个文档的字段值是否包含着特定值。
    查询语句会询问每个文档的字段值与特定值的匹配程度如何。
      一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且按照相关性对匹配到的文档进行排序。
       这种评分方式非常适用于一个没有完全配置结果的全文本搜索。
    一个简单的文档列表,快速匹配运算并存入内存是十分方便的,每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。
    查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比过滤语句更耗时,并且查询结果也不可缓存。
    建议:做精确匹配搜索时,最好用过滤语句,因为过滤语句可以缓存数据。

以上是关于Elasticsearch中核心详解的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch基本概念及核心配置文件详解

详解Android WebView加载html片段

elasticsearch代码片段,及工具类SearchEsUtil.java

ElasticSearch查询流程详解

ElasticSearch学习问题记录——Invalid shift value in prefixCoded bytes (is encoded value really an INT?)(代码片段

elasticsearch-jdbc实现MySQL同步到ElasticSearch深入详解