对ElastaticSearch的介绍,基础入门篇

ElasticSearch入门篇

第一节 ElasticSearch概述

1.1 ElasticSearch是什么

一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTfulweb接口。ElasticSearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。构建在全文检索开源软件Lucene之上的Elasticsearch,不仅能对海量规模的数据完成分布式索引与检索,还能提供数据聚合分析。据国际权威的数据库产品评测机构DBEngines的统计,在2016年1月,Elasticsearch已超过Solr等,成为排名第一的搜索引擎类应用
概括:基于Restful标准的高扩展高可用的实时数据分析的全文搜索工具

1.2 ElasticSearch的基本概念

Index
  类似于mysql数据库中的database
Type
  类似于mysql数据库中的table表,es中可以在Index中建立type(table),通过mapping进行映射。
Document
  由于es存储的数据是文档型的,一条数据对应一篇文档即相当于mysql数据库中的一行数据row,一个文档中可以有多个字段也就是mysql数据库一行可以有多列。
Field
  es中一个文档中对应的多个列与mysql数据库中每一列对应
Mapping
  可以理解为mysql或者solr中对应的schema,只不过有些时候es中的mapping增加了动态识别功能,感觉很强大的样子,其实实际生产环境上不建议使用,最好还是开始制定好了对应的schema为主。
indexed
  就是名义上的建立索引。mysql中一般会对经常使用的列增加相应的索引用于提高查询速度,而在es中默认都是会加上索引的,除非你特殊制定不建立索引只是进行存储用于展示,这个需要看你具体的需求和业务进行设定了。
Query DSL
  类似于mysql的sql语句,只不过在es中是使用的json格式的查询语句,专业术语就叫:QueryDSL
GET/PUT/POST/DELETE
  分别类似与mysql中的select/update/delete……

1.3 Elasticsearch的架构

image

Gateway层
es用来存储索引文件的一个文件系统且它支持很多类型,例如:本地磁盘、共享存储(做snapshot的时候需要用到)、hadoop的hdfs分布式存储、亚马逊的S3。它的主要职责是用来对数据进行长持久化以及整个集群重启之后可以通过gateway重新恢复数据。

Distributed Lucene Directory

Gateway上层就是一个lucene的分布式框架,lucene是做检索的,但是它是一个单机的搜索引擎,像这种es分布式搜索引擎系统,虽然底层用lucene,但是需要在每个节点上都运行lucene进行相应的索引、查询以及更新,所以需要做成一个分布式的运行框架来满足业务的需要。

四大模块组件
districted lucene directory之上就是一些es的模块,Index Module是索引模块,就是对数据建立索引也就是通常所说的建立一些倒排索引等;Search Module是搜索模块,就是对数据进行查询搜索;Mapping模块是数据映射与解析模块,就是你的数据的每个字段可以根据你建立的表结构通过mapping进行映射解析,如果你没有建立表结构,es就会根据你的数据类型推测你的数据结构之后自己生成一个mapping,然后都是根据这个mapping进行解析你的数据;River模块在es2.0之后应该是被取消了,它的意思表示是第三方插件,例如可以通过一些自定义的脚本将传统的数据库(mysql)等数据源通过格式化转换后直接同步到es集群里,这个river大部分是自己写的,写出来的东西质量参差不齐,将这些东西集成到es中会引发很多内部bug,严重影响了es的正常应用,所以在es2.0之后考虑将其去掉。

Discovery、Script
es4大模块组件之上有 Discovery模块:es是一个集群包含很多节点,很多节点需要互相发现对方,然后组成一个集群包括选主的,这些es都是用的discovery模块,默认使用的是 Zen,也可是使用EC2;es查询还可以支撑多种script即脚本语言,包括mvel、js、python等等。

Transport协议层
再上一层就是es的通讯接口Transport,支持的也比较多:Thrift、Memcached以及Http,默认的是http,JMX就是java的一个远程监控管理框架,因为es是通过java实现的。

RESTful接口层
最上层就是es暴露给我们的访问接口,官方推荐的方案就是这种Restful接口,直接发送http请求,方便后续使用nginx做代理、分发包括可能后续会做权限的管理,通过http很容易做这方面的管理。如果使用java客户端它是直接调用api,在做负载均衡以及权限管理还是不太好做。

1.4 RESTfull API

一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。在目前主流的三种Web服务交互方案中,REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-RPC更加简单明了

(Representational State Transfer 意思是:表述性状态传递)

它使用典型的HTTP方法,诸如GET,POST.DELETE,PUT来实现资源的获取,添加,修改,删除等操作。即通过HTTP动词来实现资源的状态扭转
复制代码

GET 用来获取资源

POST 用来新建资源(也可以用于更新资源)

PUT 用来更新资源

DELETE 用来删除资源

1.5 CRUL命令

以命令的方式执行HTTP协议的请求
GET/POST/PUT/DELETE

示例:

1
2
3
4
5
6
7
8
9
# 访问一个网页
curl www.baidu.com
curl -o tt.html www.baidu.com
# 显示响应的头信息
curl -i www.baidu.com
# 显示一次HTTP请求的通信过程
curl -v www.baidu.com
# 执行GET/POST/PUT/DELETE操作
curl -X GET/POST/PUT/DELETE url

1.6 ElastaticSearch 请参看大数据存储框架之ElastaticSearch(1) 概述

ElastaticSearch 基本操作

版本控制

ElasticSearch采用了乐观锁来保证数据的一致性,也就是说,当用户对document进行操作时,并不需要对该document作加锁和解锁的操作,只需要指定要操作的版本即可。当版本号一致时,ElasticSearch会允许该操作顺利执行,而当版本号存在冲突时,ElasticSearch会提示冲突并抛出异常(VersionConflictEngineException异常)。
ElasticSearch的版本号的取值范围为1到2^63-1。
内部版本控制:使用的是version
外部版本控制:elasticsearch在处理外部版本号时会与对内部版本号的处理有些不同。它不再是检查_version是否与请求中指定的数值_相同
,而是检查当前的_version是否比指定的数值小。如果请求成功,那么外部的版本号就会被存储到文档中的_version中。
为了保持_version与外部版本控制的数据一致
使用version_type=external

什么是Mapping

1
2
3
4
5
6
7
8
PUT /myindex/article/1 

{
"post_date": "2018-05-10",
"title": "Java",
"content": "java is the best language",
"author_id": 119
}
1
2
3
4
GET /myindex/article/_search?q=2018-05
GET /myindex/article/_search?q=2018-05-10
GET /myindex/article/_search?q=html
GET /myindex/article/_search?q=java

查看es自动创建的mapping
es自动创建了index,type,以及type对应的mapping(dynamic mapping)

1
GET /myindex/article/_mapping

什么是映射:mapping定义了type中的每个字段的数据类型以及这些字段如何分词等相关属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"myindex": {
"mappings": {
"article": {
"properties": {
"author_id": {
"type": "long"
},
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"post_date": {
"type": "date"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}

创建索引的时候,可以预先定义字段的类型以及相关属性,这样就能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理字符串值等

支持的数据类型:

(1)核心数据类型(Core datatypes)
字符型:string,string类型包括
text 和 keyword
text类型被用来索引长文本,在建立索引前会将这些文本进行分词,转化为词的组合,建立索引。允许es来检索这些词语。text类型不能用来排序和聚合。
Keyword类型不需要进行分词,可以被用来检索过滤、排序和聚合。keyword 类型字段只能用本身来进行检索
数字型:long, integer, short, byte, double, float
日期型:date
布尔型:boolean
二进制型:binary
(2)复杂数据类型(Complex datatypes)
数组类型(Array datatype):数组类型不需要专门指定数组元素的type,例如:
字符型数组: [ “one”, “two” ]
整型数组:[ 1, 2 ]
数组型数组:[ 1, [ 2, 3 ]] 等价于[ 1, 2, 3 ]
对象数组:[ { “name”: “Mary”, “age”: 12 }, { “name”: “John”, “age”: 10 }]
对象类型(Object datatype):_ object _ 用于单个JSON对象;
嵌套类型(Nested datatype):_ nested _ 用于JSON数组;
(3)地理位置类型(Geo datatypes)
地理坐标类型(Geo-point datatype):_ geo_point _ 用于经纬度坐标;
地理形状类型(Geo-Shape datatype):_ geo_shape _ 用于类似于多边形的复杂形状;
(4)特定类型(Specialised datatypes)
IPv4 类型(IPv4 datatype):_ ip _ 用于IPv4 地址;
Completion 类型(Completion datatype):_ completion 提供自动补全建议;
Token count 类型(Token count datatype):
token_count _ 用于统计做了标记的字段的index数目,该值会一直增加,不会因为过滤条件而减少。
mapper-murmur3
类型:通过插件,可以通过 murmur3 来计算 index 的 hash 值;
附加类型(Attachment datatype):采用 mapper-attachments
插件,可支持_ attachments _ 索引,例如 Microsoft Office 格式,Open Document 格式,ePub, HTML 等。

支持的属性:

“store”:false//是否单独设置此字段的是否存储而从_source字段中分离,默认是false,只能搜索,不能获取值

“index”: true//分词,不分词是:false
,设置成false,字段将不会被索引

“analyzer”:”ik”//指定分词器,默认分词器为standard analyzer

“boost”:1.23//字段级别的分数加权,默认值是1.0

“doc_values”:false//对not_analyzed字段,默认都是开启,分词字段不能使用,对排序和聚合能提升较大性能,节约内存

“fielddata”:{“format”:”disabled”}//针对分词字段,参与排序或聚合时能提高性能,不分词字段统一建议使用doc_value

“fields”:{“raw”:{“type”:”string”,”index”:”not_analyzed”}} //可以对一个字段提供多种索引模式,同一个字段的值,一个分词,一个不分词

“ignore_above”:100 //超过100个字符的文本,将会被忽略,不被索引

“include_in_all”:ture//设置是否此字段包含在_all字段中,默认是true,除非index设置成no选项

“index_options”:”docs”//4个可选参数docs(索引文档号) ,freqs(文档号+词频),positions(文档号+词频+位置,通常用来距离查询),offsets(文档号+词频+位置+偏移量,通常被使用在高亮字段)分词字段默认是position,其他的默认是docs

“norms”:{“enable”:true,”loading”:”lazy”}//分词字段默认配置,不分词字段:默认{“enable”:false},存储长度因子和索引时boost,建议对需要参与评分字段使用 ,会额外增加内存消耗量

“null_value”:”NULL”//设置一些缺失字段的初始化值,只有string可以使用,分词字段的null值也会被分词

“position_increament_gap”:0//影响距离查询或近似查询,可以设置在多值字段的数据上火分词字段上,查询时可指定slop间隔,默认值是100

“search_analyzer”:”ik”//设置搜索时的分词器,默认跟ananlyzer是一致的,比如index时用standard+ngram,搜索时用standard用来完成自动提示功能

“similarity”:”BM25”//默认是TF/IDF算法,指定一个字段评分策略,仅仅对字符串型和分词类型有效

“term_vector”:”no”//默认不存储向量信息,支持参数yes(term存储),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量) 对快速高亮fast vector highlighter能提升性能,但开启又会加大索引体积,不适合大数据量用

映射的分类:
(1)动态映射:
当ES在文档中碰到一个以前没见过的字段时,它会利用动态映射来决定该字段的类型,并自动地对该字段添加映射。
可以通过dynamic设置来控制这一行为,它能够接受以下的选项:
true:默认值。动态添加字段
false:忽略新字段
strict:如果碰到陌生字段,抛出异常
dynamic设置可以适用在根对象上或者object类型的任意字段上。

1
POST /lib2

给索引lib2创建映射类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{

"settings":{
"number_of_shards" : 3,
"number_of_replicas" : 0
},
"mappings":{
"books":{
"properties":{
"title":{"type":"text"},
"name":{"type":"text","index":false},
"publish_date":{"type":"date","index":false},
"price":{"type":"double"},
"number":{"type":"integer"}
}
}
}
}

基本查询(Query查询)

数据准备
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PUT /lib3

{
"settings":{
"number_of_shards" : 3,
"number_of_replicas" : 0
},
"mappings":{
"user":{
"properties":{
"name": {"type":"text"},
"address": {"type":"text"},
"age": {"type":"integer"},
"interests": {"type":"text"},
"birthday": {"type":"date"}
}
}
}
}
1
2
GET /lib3/user/_search?q=name:lisi
GET /lib3/user/_search?q=name:zhaoliu&sort=age:desc
term查询和terms查询

term query会去倒排索引中寻找确切的term,它并不知道分词器的存在。这种查询适合keyword 、numeric、date。

term:查询某个字段里含有某个关键词的文档

1
2
3
4
5
6
7
GET /lib3/user/_search/

{
"query": {
"term": {"interests": "changge"}
}
}

terms:查询某个字段里含有多个关键词的文档

1
2
3
4
5
6
7
8
9
GET /lib3/user/_search

{
"query":{
"terms":{
"interests": ["hejiu","changge"]
}
}
}
控制查询返回的数量

from:从哪一个文档开始
size:需要的个数

1
2
3
4
5
6
7
8
9
10
11
GET /lib3/user/_search

{
"from":0,
"size":2,
"query":{
"terms":{
"interests": ["hejiu","changge"]
}
}
}
返回版本号
1
GET /lib3/user/_search
1
2
3
4
5
6
7
8
{
"version":true,
"query":{
"terms":{
"interests": ["hejiu","changge"]
}
}
}
match查询

match query知道分词器的存在,会对filed进行分词操作,然后再查询

1
2
3
4
5
6
7
8
9
GET /lib3/user/_search

{
"query":{
"match":{
"name": "zhaoliu"
}
}
}

match_all:查询所有文档

1
2
3
4
5
6
7
GET /lib3/user/_search

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

multi_match:可以指定多个字段

1
2
3
4
5
6
7
8
9
10
11

GET /lib3/user/_search

{
"query":{
"multi_match": {
"query": "lvyou",
"fields": ["interests","name"]
}
}
}

match_phrase:短语匹配查询

ElasticSearch引擎首先分析(analyze)查询字符串,从分析后的文本中构建短语查询,这意味着必须匹配短语中的所有分词,并且保证各个分词的相对位置不变:

1
2
3
4
5
6
7
8
9
GET lib3/user/_search

{
"query":{
"match_phrase":{
"interests": "duanlian,shuoxiangsheng"
}
}
}
指定返回的字段
1
2
3
4
5
6
7
8
9
10
GET /lib3/user/_search

{
"_source": ["address","name"],
"query": {
"match": {
"interests": "changge"
}
}
}
控制加载的字段
1
2
3
4
5
6
7
8
9
10
11
GET /lib3/user/_search

{
"query": {
"match_all": {}
},
"_source": {
"includes": ["name","address"],
"excludes": ["age","birthday"]
}
}

使用通配符*

1
2
3
4
5
6
7
8
9
10
11
GET /lib3/user/_search

{
"_source": {
"includes": "addr*",
"excludes": ["name","bir*"]
},
"query": {
"match_all": {}
}
}
排序

使用sort实现排序:
desc:降序,asc升序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /lib3/user/_search

{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order":"asc"
}
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /lib3/user/_search

{
"query": {
"match_all": {}
},
"sort": [
{
"age": {
"order":"desc"
}
}
]
}
前缀匹配查询
1
2
3
4
5
6
7
8
9
10
11
GET /lib3/user/_search

{
"query": {
"match_phrase_prefix": {
"name": {
"query": "zhao"
}
}
}
}
范围查询

range:实现范围查询

参数:from,to,include_lower,include_upper,boost

include_lower:是否包含范围的左边界,默认是true

include_upper:是否包含范围的右边界,默认是true

1
2
3
4
5
6
7
8
9
10
11
12
GET /lib3/user/_search

{
"query": {
"range": {
"birthday": {
"from": "1990-10-10",
"to": "2018-05-01"
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
GET /lib3/user/_search
{
"query": {
"range": {
"age": {
"from": 20,
"to": 25,
"include_lower": true,
"include_upper": false
}
}
}
}
wildcard查询

允许使用通配符* 和 ?来进行查询

*代表0个或多个字符

?代表任意一个字符

1
2
3
4
5
6
7
8
GET /lib3/user/_search
{
"query": {
"wildcard": {
"name": "zhao*"
}
}
}
1
2
3
4
5
6
7
8
GET /lib3/user/_search
{
"query": {
"wildcard": {
"name": "li?i"
}
}
}
fuzzy实现模糊查询

value:查询的关键字

boost:查询的权值,默认值是1.0

min_similarity:设置匹配的最小相似度,默认值为0.5,对于字符串,取值为0-1(包括0和1);对于数值,取值可能大于1;对于日期型取值为1d,1m等,1d就代表1天

prefix_length:指明区分词项的共同前缀长度,默认是0

max_expansions:查询中的词项可以扩展的数目,默认可以无限大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET /lib3/user/_search
{
"query": {
"fuzzy": {
"interests": "chagge"
}
}
}

GET /lib3/user/_search
{
"query": {
"fuzzy": {
"interests": {
"value": "chagge"
}
}
}
}
高亮搜索结果
1
2
3
4
5
6
7
8
9
10
11
12
13
GET /lib3/user/_search
{
"query":{
"match":{
"interests": "changge"
}
},
"highlight": {
"fields": {
"interests": {}
}
}
}

Filter查询

filter是不计算相关性的,同时可以cache。因此,filter速度要快于query。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /lib4/items/_bulk
{"index": {"_id": 1}}

{"price": 40,"itemID": "ID100123"}

{"index": {"_id": 2}}

{"price": 50,"itemID": "ID100124"}

{"index": {"_id": 3}}

{"price": 25,"itemID": "ID100124"}

{"index": {"_id": 4}}

{"price": 30,"itemID": "ID100125"}

{"index": {"_id": 5}}

{"price": null,"itemID": "ID100127"}
简单的过滤查询
1
2
3
4
5
6
7
8
GET /lib4/items/_search
{
"post_filter": {
"term": {
"price": 40
}
}
}
1
2
3
4
5
6
7
8
GET /lib4/items/_search
{
"post_filter": {
"terms": {
"price": [25,40]
}
}
}
1
2
3
4
5
6
7
8
GET /lib4/items/_search
{
"post_filter": {
"term": {
"itemID": "ID100123"
}
}
}

查看分词器分析的结果:

1
GET /lib4/_mapping

不希望商品id字段被分词,则重新创建映射

1
DELETE lib4
1
2
3
4
5
6
7
8
9
10
11
12
13
PUT /lib4
{
"mappings": {
"items": {
"properties": {
"itemID": {
"type": "text",
"index": false
}
}
}
}
}
bool过滤查询

可以实现组合过滤查询

格式:

1
2
3
4
5
6
7
{
"bool": {
"must": [],
"should": [],
"must_not": []
}
}

must:必须满足的条件—and

should:可以满足也可以不满足的条件–or

must_not:不需要满足的条件–not

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /lib4/items/_search
{
"post_filter": {
"bool": {
"should": [
{"term": {"price":25}},
{"term": {"itemID": "id100123"}}
],
"must_not": {
"term":{"price": 30}
}
}
}
}

嵌套使用bool:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET /lib4/items/_search
{
"post_filter": {
"bool": {
"should": [
{"term": {"itemID": "id100123"}},
{
"bool": {
"must": [
{"term": {"itemID": "id100124"}},
{"term": {"price": 40}}
]
}
}
]
}
}
}
范围过滤

gt: >

lt: <

gte: >=

lte: <=

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"post_filter": {
"range": {
"price": {
"gt": 25,
"lt": 50
}
}
}
}
过滤非空
1
2
3
4
5
6
7
8
9
10
11
12
GET /lib4/items/_search
{
"query": {
"bool": {
"filter": {
"exists":{
"field":"price"
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
GET /lib4/items/_search
{
"query" : {
"constant_score" : {
"filter": {
"exists" : { "field" : "price" }
}
}
}
}
过滤器缓存

ElasticSearch提供了一种特殊的缓存,即过滤器缓存(filter cache),用来存储过滤器的结果,被缓存的过滤器并不需要消耗过多的内存(因为它们只存储了哪些文档能与过滤器相匹配的相关信息),而且可供后续所有与之相关的查询重复使用,从而极大地提高了查询性能。

注意:ElasticSearch并不是默认缓存所有过滤器,
以下过滤器默认不缓存:

  • numeric_range

  • script

  • geo_bbox

  • geo_distance

  • geo_distance_range

  • geo_polygon

  • geo_shape

  • and

  • or

  • not

exists,missing,range,term,terms默认是开启缓存的

开启方式:在filter查询语句后边加上
“_catch”:true

聚合查询

(1)sum

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"size":0,
"aggs": {
"price_of_sum": {
"sum": {
"field": "price"
}
}
}
}

(2)min

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"size": 0,
"aggs": {
"price_of_min": {
"min": {
"field": "price"
}
}
}
}

(3)max

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"size": 0,
"aggs": {
"price_of_max": {
"max": {
"field": "price"
}
}
}
}

(4)avg

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"size":0,
"aggs": {
"price_of_avg": {
"avg": {
"field": "price"
}
}
}
}

(5)cardinality:求基数

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"size":0,
"aggs": {
"price_of_cardi": {
"cardinality": {
"field": "price"
}
}
}
}

(6)terms:分组

1
2
3
4
5
6
7
8
9
10
11
GET /lib4/items/_search
{
"size":0,
"aggs": {
"price_group_by": {
"terms": {
"field": "price"
}
}
}
}

对那些有唱歌兴趣的用户按年龄分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
GET /lib3/user/_search
{
"query": {
"match": {
"interests": "changge"
}
},
"size": 0,
"aggs":{
"age_group_by":{
"terms": {
"field": "age",
"order": {
"avg_of_age": "desc"
}
},
"aggs": {
"avg_of_age": {
"avg": {
"field": "age"
}
}
}
}
}
}

复合查询

将多个基本查询组合成单一查询的查询

使用bool查询

接收以下参数:

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

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

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

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

相关性得分是如何组合的。每一个子查询都独自地计算文档的相关性得分。一旦他们的得分被计算出来, bool 查询就将这些得分进行合并并且返回一个代表整个布尔操作的得分。

下面的查询用于查找 title 字段匹配 how to make millions 并且不被标识为 spam 的文档。那些被标识为 starred 或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 两者 都满足,那么它排名将更高:

1
2
3
4
5
6
7
8
9
10
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}

如果没有 must 语句,那么至少需要能够匹配其中的一条 should 语句。但,如果存在至少一条 must 语句,则对 should 语句的匹配没有要求。
如果我们不想因为文档的时间而影响得分,可以用 filter 语句来重写前面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
}
}

通过将 range 查询移到 filter 语句中,我们将它转成不评分的查询,将不再影响文档的相关性排名。由于它现在是一个不评分的查询,可以使用各种对 filter 查询有效的优化手段来提升性能。

bool 查询本身也可以被用做不评分的查询。简单地将它放置到 filter 语句中并在内部构建布尔逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"bool": {
"must": [
{ "range": { "date": { "gte": "2014-01-01" }}},
{ "range": { "price": { "lte": 29.99 }}}
],
"must_not": [
{ "term": { "category": "ebooks" }}
]
}
}
}
}
constant_score查询

它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下。

1
2
3
4
5
6
7
{
"constant_score": {
"filter": {
"term": { "category": "ebooks" }
}
}
}

term 查询被放置在 constant_score 中,转成不评分的filter。这种方式可以用来取代只有 filter 语句的 bool 查询。