文档

在Elasticsearch中,文档以JSON格式进行存储,可以是复杂的结构。
一个文档不只有数据。它还包含了元数据——关于文档的信息,三个必须的元数据节点是:

节点说明
_index文档存储的地方
_type文档代表的对象的类
_id文档的唯一标识

查询响应

  • 可以在查询url后面添加pretty参数,使得返回的json更容易查看
  • 在响应的数据中,如果不需要全部的字段,可以指定某些需要的字段进行返回,例: GET /blog/user/666?_source=id,name
  • 如不需要返回元数据,仅仅返回原始数据,可以这样: GET /blog/user/666/_source
  • 如果不需要全部的字段且不需要返回元数据,还可以这样:GET /blog/user/666/_source?_source=id,name

判断文档是否存在

如果只需要判断文档是否存在,而不是查询对象的内容,那么可以这样:HEAD /blog/user/666,存在时返回的状态Status是200OK,不存在时则返回的是404 NOT FOUND。

批量操作

  • 有些情况下可以通过批量操作以减少网络请求。如:批量查询、批量插入数据。
  • 如果某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据,存在found:true,不存在found:false。
  • 在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。

一次请求多少性能最高?

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

分页

  • 和SQL使用LIMIT关键字返回只有一页的结果一样,Elasticsearch接受from和size参数。
  • 应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住一个搜索请求常常涉及多个分片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。
  • 在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网略搜索引擎中任何语句不能返回多于1000个结果的原因。

映射

  • 一般创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段类型的,否则,自动判断的类型和实际需求是不相符的。
  • 字符串有string、text、keyword这三种类型,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。
  • text类型:适用于当一个字段是要被全文搜索的,不用于排序,很少用于聚合。
  • keyword类型:适用于索引结构化的字段,如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合。keyword类型的字段只能通过精确值搜索到。

结构化查询

1、term查询

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

POST /blog/user/_search
{
  "query" : {
    "term" : {
      "age" : 29
    }
  }
}

2、terms查询

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

POST /blog/user/_search
{
  "query" : {
    "term" : {
      "age" : [29,30]
    }
  }
}

3、range查询

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

POST /blog/user/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 29,
        "lte": "36"
      }
    }
  }
}

4、exists查询

exists查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的IS_NULL条件。这个查询只是针对已经查出一批数据来,但是想区分某个字段是否存在的时候使用。

POST /blog/user/_search
{
  "query": {
    "exists": { #必须包含
      "field": "age"
    }
  }
}

5、match查询

  • match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
  • 如果你使用match查询一个全文本字段,它会在真正查询之前用分析器match一下查询字符。
  • 如果用match下指定了一个确切值,在遇到数字、日期、布尔值或者not_analyzed的字符串时,它将为你搜索你给定的值。
{ "match": { "age" 26 }}
{ "match": { "data": "2021-03-19" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}

6、bool查询

bool查询可以用来合并多个条件查询结果的布尔逻辑,它包含以下操作符:

  • must:多个查询条件的完全匹配,相当于 and。
  • must_not:多个查询条件的相反匹配,相当于 not。
  • should:至少有一个查询条件匹配,相当于 or。
这些参数可以分别继承一个查询条件或者一个查询条件的数组:
{
  "bool": {
    "must": {
      "term": {
        "name": "gsm"
      }
    },
    "must_not": {
      "term": {
        "tag": "studing"
      }
    },
    "should": [
      {
        "term": {
          "starred": true
        },
        "term": {
          "unread": true
        }
      }
    ]
  }
}

过滤查询

Elasticsearch也支持过滤查询,如term、range、match等。示例:查询年龄为30岁的用户

POST /blog/user/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "age": 30
        }
      }
    }
  }
}

查询和过滤的对比

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

建议:做精确匹配搜索时,最好用过滤语句,因为过滤语句可以缓存数据。

全文搜索

全文搜索两个最重要的方面是:

  • 相关性(Relevance)它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是TF/IDF方法、地理位置邻近、模糊相似,或其他的某些算法。
  • 分词(Analysis)它是将文本块转换为有区别的、规范化的token的一个过程,目的是为了创建倒排索引以及查询倒排索引。

1、单词搜索

POST /blog/user/_search
{
  "query": {
    "match": {
      "hobby": "音乐"
    }
  },
  "highlight": {
    "fields":{
      "hobby": {}
    }
  }
}
  • 检查字段类型:爱好hobby字段是一个text类型(指定了IK分词器),这意味着查询字符串本身也应该被分词。
  • 分析查询字符串:将查询的字符串“音乐”传入IK分词器中,输出的结果是单个项 音乐。因为只有一个单词项,所以match查询执行的是单个底层term查询。
  • 查找匹配文档:用term查询在倒排索引中音乐“然后获取一组包含该项的文档”。
  • 为每个文档评分:用term查询计算每个文档相关度评分_score,这是种将词频(term frequency,即词“音乐”在相关文档的hobby字段中出现的频率)和反向文档频率(inverse document frequency,即词“音乐”在所有文档的hobby字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式。

2、多词搜索

POST /blog/user/_search
{
  "query": {
    "match": {
      "hobby": "音乐 篮球"
    }
  },
  "highlight": {
    "fields":{
      "hobby": {}
    }
  }
}

默认是“或”的关系,在Elasticsearch中,可以指定词之间的逻辑关系,如下:

POST /blog/user/_search
{
  "query": {
    "match": {
      "hobby": {
        "query":"音乐 篮球",
        "operator": "and"
      }
    }
  },
  "highlight": {
    "fields": {
      "hobby": {}
    }
  }
}

“OR”和”AND”搜索,这是两个极端,其实在实际场景中,并不会选取这2个极端,更有可能是选取这种,或者说,只需要符合一定的相似度就可以查询到数据,在Elasticsearch中也支持这样的查询,通过minimum_should_match来指定匹配度,如80%,示例:

POST /blog/user/_search
{
  "query": {
    "match": {
      "hobby": {
        "query":"音乐 篮球",
        "minimun_should_match": "80%"
      }
    }
  },
  "highlight": {
    "fields": {
      "hobby": {}
    }
  }
}

相似度应该多少合适,需要在实际的需求中进行反复测试,才可得到合理的值。

3、组合搜索

在搜索中,也可以使用过滤器中讲过的bool组合查询,示例:


#搜索结果中必须包含篮球,不能包含音乐,如果包含了游泳,那么它的相似度更高。
POST /blog/user/_search
{
  "query":{ 
    "bool":{ 
      "must":{ 
        "match":{ 
          "hobby":"篮球" 
          } 
        },
      "must_not":{
        "match":{ 
          "hobby":"音乐" 
        } 
      },
      "should":[ 
        { "match": { "hobby":"游泳" } }
      ] 
    }
}

默认情况下,should中的内容不是必须匹配的,如果查询语句中没有must,那么就会至少匹配其中一个。当然了,也可以通过minimum_should_match参数进行控制,该值可以是数字也可以是百分比,示例:

POST /blog/user/_search
{
  "query": {
    "bool": {
      "should":[
        {
          "match": {
            "hobby":"游泳" 
          }
        },
        {
          "match": {
            "hobby":"篮球"
          }
        },
        { 
          "match": {
            "hobby":"音乐" 
          } 
        }
      ],
      minmum_should_match: 2 #should中的三个词,至少要满足2个
    }
  }
}

4、权重

有时候可能需要某些词增加权重来影响该条数据的得分。如下:搜索关键字为“游泳篮球”,如果结果中包含了“音乐”权重为10,包含了“跑步”权重为2。

POST /blog/user/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "hobby": {
            "query": "游泳篮球",
            "operator": "and"
          }
        }
      },
      "should": [
        {
          "match": {
            "hobby": {
              "query": "音乐",
              "boost": 10
            }
          }
        },
        {
          "match": {
            "hobby": {
              "query": "跑步",
              "boost": 2
            }
          }
        }
      ]
    }
  },
  "highlight": {
    "field": {
      "hobby": {}
    }
  }
}
最后修改日期:2021年3月29日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。