3

一、基本概念

1、Cluster(集群)

es集群对外提供索引和搜索的服务,其包含一个或者多个节点,每个节点都有统一的集群名称。参考本地集群搭建

image.png

2、Node(节点)

单独一个Elasticsearch服务器实例称为一个node,node是集群的一部分,每个node有独立的名称,默认是启动时获取一个UUID作为名称,也可以自行配置。

3、Shard(分片)

Shard分片也称为primary shard,是单个Lucene索引,由于单台机器的存储容量是有限的,而Elasticsearch索引的数据可能特别大,单台机器无法存储全部数据,就需要将索引中的数据切分为多个shard,分布在多台服务器上存储。利用shard可以很好地进行横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升集群整体的吞吐量和性能。创建索引时就应该预估好需要的分片数量,一旦创建则分片数量无法更改。

4、Replica(复制分片)

replica全称叫replica shard,是索引的副本,完全拷贝shard的内容,shard与replica的关系可以是一对多,同一个shard可以有一个或多个replica。当集群中有节点宕机了,丢失的shard会由replica补位,保证数据高可用;replica shard也能分担查询请求,提升集群的吞吐量。

5、Index(索引)

es索引,其保存具有相同结构的文档集合,类似于关系型数据库的数据库实例(6.0.0版本type废弃后,索引的概念下降到等同于数据库表的级别)。一个集群里可以定义多个索引

6、Type(类型)

类型,原本是在索引(Index)内进行的逻辑细分,但后来发现企业研发为了增强可阅读性和可维护性,制订的规范约束,同一个索引下很少还会再使用type进行逻辑拆分(如同一个索引下既有订单数据,又有评论数据),因而在6.0.0版本之后,此定义废弃。

7、Document(文档)

Elasticsearch最小的数据存储单元,JSON数据格式,类似于关系型数据库的表记录(一行数据),结构定义多样化,同一个索引下的document,结构尽可能相同。

8、Field type(字段类型)

  • text:被分析索引的字符串类型
  • keyword:不能被分析,只能被精确匹配的字符串类型
  • date:日期类型,可以配合format一起使用
  • long:长整型,数字类型
  • integer:整型,数字类型
  • short:短整型,数字类型
  • double:双精度浮点型,数字类型
  • Boolean:布尔型,true、false
  • array:数组类型
  • object:对象类型,json嵌套
  • ip:ip类型
  • geo_point:地理位置类型

9、分词器

ElasticSearch借助各种类型的分词器来对文档内容进行分词处理,以便于创建倒排索引,这是搜索的核心。同时也对搜索内容进行分词处理,用以在倒排索引中索引文档。

1、内置分词器

标准分词器(standard analyzer)(默认)
  • 分析过程:字符过滤器->字符处理->分词过滤(分词转换)

说明:首先是字符过滤器过滤掉特殊符号以及量词(the、an、a等),进一步将分词小写处理,

英文分词器(english analyzer)
  • 分析过程:字符过滤器->字符处理->分词过滤(分词转换,词干转化)

说明:首先是字符过滤器过滤掉特殊符号以及量词(the、an、a等),进一步将分词小写处理,再次进行分词转换,例如:eating -> eat,其实它们两是一回事。

简单分词器(simple analyzer)

先按照空格分词,英文大写转小写。

空格分词器(whitespace analyzer)

先按照空格分词,英文不做大小写处理。

2、外部分词器

中文分词器(ik_maxword)

会将文本做最细粒度的拆分;尽可能多的拆分出词语,例如:如南京市长江大桥 --> 南京市/南京/市长/长江大桥/长江/大桥

中文分词器(ik_smart)

会做最粗粒度的拆分;已被分出的词语将不会再次被其它词语占有,如南京市长江大桥 --> 南京市/长江大桥

安装中文分词器
  • 安装方式一
#去GitHub下载与elasticsearch对应版本的分词插件
https://github.com/medcl/elasticsearch-analysis-ik/releases

#进入es的plugins目录创建文件夹
cd /usr/local/elasticsearch-7.8.0/plugins
mkdir analysis-ik

#解压ik分词插件到ik文件夹
unzip elasticsearch-analysis-ik-7.8.0.zip
  • 安装方式二
cd /usr/local/elasticsearch-7.8.0/bin

#切换到root用户
su root

#执行es自带插件安装命令
./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.0/elasticsearch-analysis-ik-7.8.0.zip

#查看安装的插件
./elasticsearch-plugin list
查看es已安装插件列表

image.png

image.png

es插件目录

image.png

ik插件词库及配置文件目录

image.png

说明:通过以上两种方式之一安装均可,安装好插件重启es服务即可。

10、评分规则

  • TF(token frequency):词频,文档中出现分词的数量,出现次数越多,相关性越强。
  • IDF(inverse document frequency):逆向文档频率,包含分词的文档数量相关,包含分词的文档数量越多则表明相关性越弱。
  • TFNORM(token frequency normalize):词频规格化,根据field长度做归一化,文档内出现频率越高,field越短相关性越强。

11、倒排索引与正排索引

ElasticSearch除了强大的搜索功能外,还可以支持排序、聚合之类的操作,搜索需要用到倒排索引,可以高效的通过分词索引到对应的文档,但是对于排序、聚合等操作,倒排索引就显得吃力低效了,此时正排索引就派上用场了,正排索引使得ElasticSearch高效的进行排序、聚合操作。

1、倒排索引

倒排索引包含两个部分,单词词典和倒排列表。

单词词典

单词词典(Term Dictionary)记录所有文档的单词,记录单词到倒排列表的关联关系。单词词典比较大,可以通过 B + 树 或者 哈希拉链法实现,以满足高性能的插入与查询

倒排列表

倒排列表(Postion List)记录了单词对应的文档结合,由倒排索引项组成(文档ID、词频TF、偏移)

image.png

2、正排索引

基于文档维度的,建立文档与字段值的映射关系。

document age birthday
doc1 18 1994-10-14
doc2 20 1985-06-04
doc_values与fielddata

在ElasticSearch中,doc_values和fielddata就是用来给文档建立正排索引的。他俩一个很显著的区别是,前者的工作地盘主要在磁盘,而后者的工作地盘在内存。

维度 doc_value fielddata
创建时间 创建索引时创建 使用时动态创建
创建位置 磁盘 内存(jvm heap)
优点 节约内存空间 节约磁盘空间
缺点 索引速度稍低 文档越多,耗费内存越大,有oom的风险

说明:ElasticSearch对于非分词字段,默认doc_value是开启的,如果非常确定某个字段将来不会用来排序或者聚合操作,可以显示地指定关闭doc_value;对于分词字段,如果未将fielddata显示开启,则对于分词字段的排序、聚合操作将会报错。开启fielddata需要慎重,因为开销是昂贵的,还有oom风险。

二、分词器定制化扩展词库

有些场景我们更注重查准率而牺牲一定的查全率,通过扩展词库来解决这一业务场景,是个不错的选择。IK中文分词器中的词库是有限的,而实际生活中很多专业名词、个性化词等需要补充到分词器的词库中,让分词器认识这些扩展词。以下将对IK分词器进行词库扩展。

IK分词器词库.png

词库冷扩展

  • 新建词库文件new_word.dic
#进入ik分词器插件目录
cd /usr/local/elasticsearch-7.8.0/config/analysis-ik

#在ik分词器词库目录下,创建新的扩展词库文件
touch new_word.dic

#修改扩展词库文件往词库中添加词语
vim new_word.dic

image.png

  • 修改IK分词器配置文件
#进入ik分词器插件目录
cd /usr/local/elasticsearch-7.8.0/config/analysis-ik

#修改IK分词器配置文件,添加新的扩展词库文件
vim IKAnalyzer.cfg.xml

IK分词器配置文件.png

  • 重启elasticsearch服务,让分词器加载扩展词库
  • 查询更新索引
#通过查询更新索引
POST /shop/_update_by_query
{
  "query": {
    "bool": {
      "must": [
        {"term":{"name":"凯"}},
        {"term":{"name":"悦"}}
      ]
    }
  }
}

说明:我们新添加了“凯悦”这个词语,现在的分词器将不会对凯悦进一步分词了,那么问题来了,原来未扩展词库时,ik_max_word分词器会将“凯悦”分成“”和“”,将导致扩展词库后反倒差不出包含凯悦的记录了。因此扩展完词库后,需要对所有扩展词影响到的文档索引都更新一次。

词库热扩展

1、配置远程扩展字典

  • 修改ik分词器配置文件IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict">new_word.dic</entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <entry key="remote_ext_dict">http://serverIp:8080/getCustomDic</entry>
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

说明:需要有个远程的http服务来支持,该http接口能返回扩展词库文件,并且响应头中应该包含last_modified、etag两个响应头,通过这两个头来标识是否词库有更新,ik分词插件默认一分钟检测一次。

2、修改ik分词器源码,从mysql加载

三、同义词扩展

ElasticSearch无法智能地理解中文中的同义词、近义词等,例如搜索“苹果”在某种意义上等同于“iPhone”。要解决这个问题,我们需要定义同义词库,建立同义词之间的映射关系。

1、建立自定义同义词的索引结构

#定义支持同义词的门店索引结构
#自定义分词器,分词器指定自定义的同义词过滤器,该过滤器的作用是分词后的过滤步骤中将同义词加入到分词中
#synonyms_path:同义词文件的路径,相对于es的config目录
PUT /shop
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1,
    "analysis":{
      "filter":{
        "my_synonym_filter":{
          "type":"synonym",
          "synonyms_path":"analysis-ik/synonyms.txt"
        }
      },
      "analyzer":{
        "ik_syno":{
          "type":"custom",
          "tokenizer":"ik_smart",
          "filter":["my_synonym_filter"]
        },
        "ik_syno_max":{
          "type":"custom",
          "tokenizer":"ik_max_word",
          "filter":["my_synonym_filter"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id":{"type": "integer"},
      "name":{"type": "text","analyzer": "ik_syno_max","search_analyzer": "ik_syno"},
      "tags":{"type": "text","analyzer": "whitespace","fielddata": true},
      "location":{"type": "geo_point"},
      "remark_score":{"type": "double"},
      "price_per_man":{"type": "integer"},
      "category_id":{"type": "integer"},
      "category_name":{"type": "keyword"},
      "seller_id":{"type": "integer"},
      "seller_remark_score":{"type": "double"},
      "seller_disabled_flag":{"type": "integer"}
    }
  }
}

2、建立同义词映射关系的文件

image.png

#进入ik分词插件目录
cd /usr/local/elasticsearch-7.8.0/config/analysis-ik

#创建同义词映射文件
touch synonyms.txt

#添加同义词映射内容
vim synonyms.txt

image.png

3、查询分析

GET /shop/_analyze
{
  "field": "name",
  "text": "凯悦"
}

结果如下:

{
  "tokens" : [
    {
      "token" : "凯悦",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "锡箔",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "SYNONYM",
      "position" : 0
    },
    {
      "token" : "红桃",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "SYNONYM",
      "position" : 0
    }
  ]
}

说明:注意分词结果,type:SYNONYM表示的是同义词,自定义分词器过滤器生效了。

四、相关性重塑

试想一下,加入用户搜索“休息住宿”,搜索列表一无所获返回空白,那么这个体验也是很差的了,理论上应该识别“休息”和“住宿”是品类此,例如对应酒店类目,召回酒店类目下的文档。以下步骤将优化相关性,提升用户体验。

1、采取词性影响召回策略模型

GET /shop/_search
{
  "_source": "*",
  "script_fields": {
    "distance": {
      "script": {
        "source": "haversin(lat,lon,doc['location'].lat,doc['location'].lon)",
        "lang": "expression",
        "params": {"lat":31.23916171,"lon":121.48789949}
      }
    }
  },
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  {"match":{"name": {"query": "住宿","boost": 0.1}}},
                  {"term":{"category_id": {"value": 2,"boost": 0}}}
                ]
              }
            },
            {"term":{"seller_disabled_flag": 0}}
          ]
        }
      },
      "functions": [
        {
          "gauss": {
            "location": {
              "origin": "31.23916171,121.48789949",
              "scale": "100km",
              "offset": "0km",
              "decay": 0.5
            }
          },
          "weight": 9
        },
        {
          "field_value_factor": {
            "field": "remark_score"
          },
          "weight": 0.2
        },
        {
          "field_value_factor": {
            "field": "seller_remark_score"
          },
          "weight": 0.1
        },
        {
          "filter": {"term":{"category_id": 2}},
          "weight": 0.2
        }
      ],
      "score_mode": "sum",
      "boost_mode": "sum"
    }
  },
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "aggs": {
    "group_by_tags": {
      "terms": {
        "field": "tags",
        "size": 10
      }
    }
  }
}

说明:以上通过布尔should查询来增加了类目召回规则,es客户端灵活组织条件。

2、构建类目与相关性词语的映射关系

这一步很重要,对于相关性重塑影响很大,相关性词语与类目的映射关系质量直接影响所搜结果。构建类似于下表的映射关系。

相关性词语 类目名称 类目编号
休息,住宿 酒店 1
下午茶,吃饭 美食 2

说明:将用户搜索内容调用分词接口分词后与相关性词语列表就行匹配,如果命中相关性词语则找到了用户意向的类目。

影响文档的召回

一种比较优化的实践是,首先不要影响文档召回,仅仅当用户搜索内容无结果时,进行降级二次搜索,此时进行关性分析,不至于导致搜索引擎无响应的糟糕体验。

影响文档的排序

任何时候如果用户搜索的内容命中了相关性词语,此时应该利用function_score的过滤规则去影响评分,提高相关性文档的得分,使得排名靠前,这个能提高搜索内容的质量,这点是很重要的。

五、基本语法

1、准备数据

抱着学习的目的,首先需要ElasticSearch的环境,有了环境以后,我们还需要初始化一批数据,咱们可以借助TMDB(the movie database)数据源来方便我们练习基本语法。

2、创建索引

  • 非结构化方式创建索引
PUT /employee
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}
  • 结构化方式创建索引
#定义门店索引结构
#name字段分别指定了索引和查询的两个不同的中文分词器,目的是在索引时尽可能多的分词,在查询时尽可能只能地分词
#tags字段指定了空格分词器,仅仅是以空格分词,不会处理大小写等,对于标签这个分词字段还需要排序、聚合等操作,需要将fielddata开启,允许在内存中构建正排索引
#category_name字段仅仅作为一个关键字,进行分词处理反而产生歧义
PUT /shop
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "id":{"type": "integer"},
      "name":{"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},
      "tags":{"type": "text","analyzer": "whitespace","fielddata": true},
      "location":{"type": "geo_point"},
      "remark_score":{"type": "double"},
      "price_per_man":{"type": "integer"},
      "category_id":{"type": "integer"},
      "category_name":{"type": "keyword"},
      "seller_id":{"type": "integer"},
      "seller_remark_score":{"type": "double"},
      "seller_disabled_flag":{"type": "integer"}
    }
  }
}
PUT /movie
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "title":{"type": "text","analyzer": "english"},
      "tagline":{"type": "text","analyzer": "english"},
      "release_date":{"type": "date","format": "8yyyy/MM/dd||yyyy/M/dd||yyyy/MM/d||yyyy/M/d"},
      "popularity":{"type": "double"},
      "overview":{"type": "text","analyzer": "english"},
      "cast":{
        "type": "object",
        "properties": {
          "character":{"type":"text","analyzer":"standard"},
          "name":{"type":"text","analyzer":"standard"}
        }
      }
    }
  }
}

说明:以上指定了个别字段使用英文分词器,日期类型指定了format,可以支持解析多种类型的日期字符串格式。

3、删除索引

  • 删除索引
DELETE /employee

4、新增文档

  • 强制指定创建,若已存在,则失败
POST /employee/_create/1
{
  "name":"关二爷",
  "age":31
}
  • (不存在)新增或者(存在)更新文档
PUT /employee/_doc/1
{
  "name":"jayway1",
  "age":30
}

说明:如果文档不存在,则会插入文档,如果文档已存在,则会覆盖原文档。

5、修改文档

  • 指定更新字段更新文档
POST /employee/_update/1
{
  "doc": {
    "name":"jayway2"
  }
}

说明:指定字段更新就只会更新指定的字段,不会去覆盖原文档。

5、删除文档

  • 删除指定文档
DELETE /employee/_doc/1

6、简单查询文档

  • 查询指定文档
GET /employee/_doc/2
  • 查询多条文档
GET /employee/_search
  • 不带条件查询所有记录
GET /employee/_search
{
  "query": {
    "match_all": {}
  }
}
  • 分页查询
GET /employee/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 20
}
  • 带关键字的条件查询
GET /employee/_search
{
  "query": {
    "match": {
      "name": "关羽"
    }
  }
}
  • 带关键字的条件查询,并排序
GET /employee/_search
{
  "query": {
    "match": {
      "name": "关"
    }
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

说明:自主排序后,分数就没有意义了,分数值为null

  • 指定操作符查询
#分词后的and和or的逻辑,match默认使用的是or
GET /movie/_search
{
  "query": {
    "match": {
      "title": "basketball with cartoom aliens"
    }
  }
}

GET /movie/_search
{
  "query": {
    "match": {
      "title": {
        "query": "basketball with cartoom aliens",
        "operator": "and"
      }
    }
  }
}
  • 指定最小词匹配项查询
#最小词匹配项(最小匹配两个词)
GET /movie/_search
{
  "query": {
    "match": {
      "title": {
        "query": "basketball love aliens",
        "operator": "or",
        "minimum_should_match": 2
      }
    }
  }
}
  • 短语查询
#短语查询,将查询内容视作一个短语,不做分词
GET /movie/_search
{
  "query": {
    "match_phrase": {
      "title": "steve zissou"
    }
  }
}
  • 多字段查询
GET /movie/_search
{
  "query": {
    "multi_match": {
      "query": "basketball with cartoom aliens",
      "fields": ["title","overview"]
    }
  }
}

说明:待查询内容分词分别匹配各个字段,每个字段都有一个评分,取最大的评分。

  • 优化多字段查询
#我们认为title字段比较重要,人为地去干涉评分的权重,
#如下的含义是:评分放大系数(boost,默认2.2)放大十倍
#tie_breaker:将除最大评分字段以外的其它字段得分乘以此系数累加到最大评分中获得最终得分;综合考虑多个字段得分,而不是一刀切
GET /movie/_search
{
  "explain": true,
  "query": {
    "multi_match": {
      "query": "basketball with cartoom aliens",
      "fields": ["title^10","overview"],
      "tie_breaker": 0.3
    }
  }
}
  • 指定查询类型的查询
#best_fields(最匹配模式):默认的得分方式,取得最高的分数作为对应文档的对应分数
GET /movie/_search
{
  "query": {
    "multi_match": {
      "query": "basketball with cartoom aliens",
      "fields": ["title^10","overview"]
    }
  }
}

#most_fields:考虑绝大多数(所有的),文档的字段得分相加,获得我们想要的结果
GET /movie/_search
{
  "query": {
    "multi_match": {
      "query": "basketball with cartoom aliens",
      "fields": ["title^10","overview^0.1"],
      "type": "most_fields"
    }
  }
}

#cross_fields:以分词为单位计算栏位的总分,适用于词维度的匹配,分词在各个字段中的得分取最大值,然后累加这些最大得分
GET /movie/_search
{
  "query": {
    "multi_match": {
      "query": "steve job",
      "fields": ["title","overview"],
      "type": "cross_fields"
    }
  }
}
  • query string方式查询
#方便的利用 and or not
GET /movie/_search
{
  "query": {
    "query_string": {
      "fields": ["title"],
      "query": "steve or jobs"
    }
  }
}

7、布尔查询

  • 单条件过滤
GET /movie/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {"title": "steve"}
      }
    }
  }
}

说明:term关键字查询不会分词,match才会分词

  • 多条件过滤
GET /movie/_search
{
  "query": {
    "bool": {
      "filter": [
        {"term":{"title": "steve"}},
        {"term":{"cast.name": "gaspard"}},
        {"range":{"release_date": {"lte":"2014/01/01"}}},
        {"range":{"popularity": {"gte":"25"}}}
      ]
    }
  }
}
  • 布尔查询
#bool查询
#must:必须都为true
#must not:必须都是false
#should:其中只有一个为true即可
#为true越多则分数越高
GET /movie/_search
{
  "query": {
    "bool": {
      "should": [
        {"match": {"title": "basketball with cartoom aliens"}},
        {"match": {"overview": "basketball with cartoom aliens"}}
      ]
    }
  }
}
  • 带match打分的filter
GET /movie/_search
{
  "query": {
    "bool": {
      "should": [
        {"match":{"title": "life"}}
      ], 
      "filter": [
        {"term":{"title": "steve"}},
        {"term":{"cast.name": "gaspard"}},
        {"range":{"release_date": {"lte":"2014/01/01"}}},
        {"range":{"popularity": {"gte":"25"}}}
      ]
    }
  }
}

说明:match查询负责打分匹配,filter负责过滤,最终返回的文档是有分数的。

  • 多条件过滤加排序
GET /movie/_search
{
  "query": {
    "bool": {
      "filter": [
        {"term":{"title": "steve"}},
        {"term":{"cast.name": "gaspard"}},
        {"range":{"release_date": {"lte":"2014/01/01"}}},
        {"range":{"popularity": {"gte":"25"}}}
      ]
    }
  },
  "sort": [
    {
      "popularity": {
        "order": "desc"
      }
    }
  ]
}

8、自定义得分查询

  • 自定义得分查询
#functionscore,对原始查询得到的分数,进一步进行处理
GET /movie/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query": "steve job",
          "fields": ["title","overview"],
          "operator": "or",
          "type": "most_fields"
        }
      },
      "functions": [
        {
          "field_value_factor": {
            "field": "popularity",
            "modifier": "log2p",
            "factor": 10
          }
        },
        {
          "field_value_factor": {
            "field": "popularity",
            "modifier": "log2p",
            "factor": 5
          }
        }
      ],
      "score_mode": "sum",
      "boost_mode": "sum"
    }
  }
}
  • 优化function_score自定义评分解决排序模型
#haversin:内置求距离函数
#name字段:降权为0.1,该字段只负责将文档匹配召回,相比距离而言,name的评分影响可以降低
#location字段:基于地理距离的高斯排序模型
#remark_score字段:店铺评分最高5分,0.2的权重,累加分值控制在[0,1]的范围
#seller_remark_score字段:店铺评分最高5分,0.2的权重,累加分值控制在[0,1]的范围
#score_mode:设置为加法模式,function_score内部评分相加
#boost_mode:设置为加法模式,function_score与query部分评分相加,如果boost_mode:replace则会丢弃query部分的评分
GET /shop/_search
{
  "_source": "*",
  "script_fields": {
    "distance": {
      "script": {
        "source": "haversin(lat,lon,doc['location'].lat,doc['location'].lon)",
        "lang": "expression",
        "params": {"lat":31.23916171,"lon":121.48789949}
      }
    }
  },
  "query": {
    "function_score": {
      "query": {
        "bool": {
          "must": [
            {"match":{"name": {"query": "凯悦","boost": 0.1}}},
            {"term":{"seller_disabled_flag": 0}},
            {"term":{"tags": "落地大窗"}}
          ]
        }
      },
      "functions": [
        {
          "gauss": {
            "location": {
              "origin": "31.23916171,121.48789949",
              "scale": "100km",
              "offset": "0km",
              "decay": 0.5
            }
          },
          "weight": 9
        },
        {
          "field_value_factor": {
            "field": "remark_score"
          },
          "weight": 0.2
        },
        {
          "field_value_factor": {
            "field": "seller_remark_score"
          },
          "weight": 0.1
        }
      ],
      "score_mode": "sum",
      "boost_mode": "sum"
    }
  },
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "aggs": {
    "group_by_tags": {
      "terms": {
        "field": "tags",
        "size": 10
      }
    }
  }
}

9、带脚本字段查询

  • 带脚本字段查询,使用地理位置字段计算距离排序
#haversin:内置求距离函数
#这种纯距离排序太不灵活,很难满足业务需求,距离近的不一定相关性高,失去了评分优势
GET /shop/_search
{
  "query": {
    "match": {
      "name": "凯悦"
    }
  },
  "_source": "*",
  "script_fields": {
    "distance": {
      "script": {
        "source": "haversin(lat,lon,doc['location'].lat,doc['location'].lon)",
        "lang": "expression",
        "params": {"lat":31.37,"lon":127.12}
      }
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.37,
          "lon": 127.12
        }, 
        "order": "asc",
        "unit": "km",
        "distance_type": "arc"
      }
    }
  ]
}

10、聚合查询

GET /employee/_search
{
  "query": {
    "match": {
      "name": "关"
    }
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ],
  "aggs": {
    "group_by_age": {
      "terms": {
        "field": "age",
        "size": 10
      }
    }
  }
}

11、查询分析

  • 使用analyze api查看分词状态
GET /movie/_analyze
{
  "field": "name",
  "text": "Eating an apple a day & keeps the doctor away"
}
  • 使用explain查看打分的分析结果
GET /movie/_search
{
  "explain": true,
  "query": {
    "match": {
      "title": "steve"
    }
  }
}
  • explanation
GET /movie/_validate/query?explain
{
  "query": {
    "multi_match": {
      "query": "basketball with cartoom aliens",
      "fields": ["title^10","overview"],
      "type": "best_fields"
    }
  }
}

neojayway
52 声望10 粉丝

学无止境,每天进步一点点