数据的存储不是目的,我们希望从海量的酒店数据中检索出需要的信息,这就是ES的搜索功能
官方文档: https://elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html#query-dsl。DSL是用来查询文档的
Elasticsearch提供了基于JSON的DSL来定义查询,简单说就是用json来描述查询条件,然后发送给es服务,最后es服务基于查询条件,把结果返回给我们
常见的查询类型包括如下:
1、查询所有: 查询出所有数据,一般在测试的时候使用
match_all
2、全文检索查询: 利用分词器对用户输入内容进行分词,然后去倒排索引库中匹配
match_query
multi_match_query
3、精确查询: 根据精确的词条值去查找数据,一般是查找keyword、数值、日期、boolean等类型的字段。这些字段是不需要分词的,但是依旧会建立倒排索引,把字段的整体内容作为一个词条,并存入倒排索引。在查找的时候,也就不需要分词,直接把搜索的内容去跟倒排索引匹配即可
ids,表示根据id,进行精确匹配
range,表示根据数值范围,进行精确匹配
term,表示根据数据的值,进行精确匹配
4、地理查询: 根据经纬度查询
geo_distance
geo_bounding_box
5、复合查询: 复合查询可将上述各种查询条件组合一起,合并查询条件
bool,利用逻辑运算把其它查询条件组合起来
function_score,用于控制相关度算分,算分会影响性能
一、DSL查询语法
GET /hotel/_search
{
"query": {
"match_all": {}
}
}
存在一个问题,我们明明查询的是所有文档,查询结果也显示查询出所有的文档了,为什么上图右侧,鼠标往下拉,最多才只有10条文档数据呢
原因: 受默认的分页条件限制,后面学习的时候,会进行解决
二、全文检索查询
首先保证你已经做好了 '实用篇-ES-环境搭建' ,创建了名为hotel的索引库,导入了批量文档。然后开始下面的操作
全文检索查询,分为下面两种,会对用户输入内容进行分词之后,再进行匹配。也就是利用分词器对用户输入内容进行分词,然后去倒排索引库中匹配
match_query
multi_match_query
【第一种全文检索查询】
match查询(也就是match_query查询): 全文检索查询的一种,会对用户输入的内容进行分词,然后去倒排索引库检索
GET /索引库名/_search
{
"query": {
"match": {
"字段名": "TEXT"
}
}
}
具体操作如下,为了让大家知道hotel索引库有哪些字段,我把当初建立gghotel索引库的类先放出来
package cn.itcast.hotel.constants;
public class HotelConstants {
public static final String MAPPING_TEMPLATE = "{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"address\":{\n" +
" \"type\": \"keyword\",\n" +
" \"index\":false\n" +
" },\n" +
" \"price\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"score\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"brand\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"city\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"starName\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"business\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"location\":{\n" +
" \"type\": \"geo_point\"\n" +
" },\n" +
" \"pic\":{\n" +
" \"type\":\"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"all\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
}
注意: 我要解释一下,上面有个字段叫all,那个字段是当时自定义的,不清楚的话可回去看 '实用篇-ES-RestClient操作' 的 'hotel数据结构分析'。
all的作用如下图,相当于一个大的字段,里面存放了几个小字段,优点是我们可以在这个大的字段里面搜索到多个小字段的信息
然后,我们就正式开始全文检索查询,输入如下。注意all换成其它字段也没事,例如换成name字段。正常来说,我们检索name字段,就只在name字段检索匹配的分词文档,但是在all字段里面检索时,也会检索到name、brand、business字段,原因如上面那个图的copy_to属性
GET /hotel/_search
{
"query": {
"match": {
"all": "外滩"
}
}
}
//或者如下
# 第一种全文检索查询 match。查询name字段中包含'酒店'的文档
GET /hotel/_search
{
"query": {
"match": {
"name": "酒店"
}
}
}
【第二种全文检索查询】
multi_match(也就是multi_match_query查询): 与match查询类似,只不过允许同时查询多个字段
GET /索引库名/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"字段名": ["FIELD1", " FIELD12"]
}
}
}
# 第二种全文检索查询 multi_match。查询business、brand、name字段中包含'如家'的文档,满足一个字段即可
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "如家",
"fields":["business","brand","name"]
}
}
}
三、精确查询
term: 根据词条的精确值查询,强调精确匹配
range: 根据值的范围查询,例如金额、时间
【第一种精确查询 term】
具体操作如下
查询city为杭州
GET /hotel/_search
{
"query":{
"term":{
"city":{
"value":"杭州"
}
}
}
}
【第二种精确查询 range】
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 10,
"lte": 20
}
}
}
}
四、地理查询
根据经纬度查询。常见的使用场景包括: 查询附近酒店、附近出租车、搜索附近的人。使用方式有很多种,介绍如下,这种最常用
geo_distance: 查询到指定中心点,以该点为圆心,distance为半径的圆,符合要求的所有文档
GET /索引库名/_search
{
"query": {
"geo_distance": {
"distance": "15km",
"字段名": "31.21,121.5"
}
}
}
具体操作如下
输入如下DSL语句
GET /hotel/_search
{
"query": {
"geo_distance": {
"distance": "15km",
"location": "31.21,121.5"
}
}
}
五、相关性算分
上面学的全文检索查询、精确查询、地理查询,这三种查询在es当中都称为简单查询,下面我们将学习复合查询。复合查询可以其它简单查询组合起来,实现更复杂的搜索逻辑,其中就有 '算分函数查询' 如下
首先保证你已经做好了 '实用篇-ES-环境搭建' ,创建了名为hotel的索引库,导入了批量文档。然后开始下面的操作
算分函数查询(function score): 可以控制文档相关性算分、控制文档排名。例如搜索'外滩' 和 '如家' 词条时,某个文档要是都能匹配这两个词条,那么在所有被搜索出来的文档当中,这个文档的位置就最靠前,简单说就是越匹配就排名越靠前
六、函数算分查询
上面只是简单演了相关性打分中的函数算分查询,文档与搜索关键字的相关度越高,打分就越高,排名就越靠前。不过,有的时候,我们希望人为地去控制控制文档的排名,例如某些文档我们就希望排名靠前一点,算分高一点,此时就需要使用函数算分查询,下面就来学习 '函数算分查询'
首先保证你已经做好了 '实用篇-ES-环境搭建' ,创建了名为gghotel的索引库,导入了批量文档。然后开始下面的操作
使用 ’函数算分查询(function score query)’,可以在原始的相关性算分的基础上加以修改,得到一个想要的算分,从而去影响文档的排名,语法如下
GET /索引库名/_search
{
"query": {
"function_score": {
"query": { "match": {"字段": "词条"} },
"functions": [
{
"filter": {"term": {"指定字段": "值"}},
"算分函数": 函数结果
}
],
"boost_mode": "加权模式"
}
}
}
function score需要考虑的三要素
1. 哪些文档需要算分加权
2. 算分函数是什么
3. 加权模式是什么
下面我们实现一个案例:给如家这个品牌的酒店排名靠前一些
考虑三要素
1. 哪些文档需要算分加权 brand为如家的酒店
2. 算分函数是什么 weigh
3. 加权模式是什么 相加/相乘都可
输入如下DSL语句,表示在 '如家' 这个品牌中,字段为'北京'的酒店排名靠前一些
GET /hotel/_search
{
"query": {
"function_score": {
"query": {"match": {
"brand": "如家"
}},
"functions": [
{
"filter": {
"term": {
"city": "北京"
}
},
"weight": 2
}
],
"boost_mode": "sum"
}
}
}
七、布尔查询
这是第二种复合查询
布尔查询不会去修改算分,而是把多个查询语句组合成一起,形成新查询,这些被组合的查询语句,被称为子查询。子查询的组合方式有如下四种
1、must:必须匹配每个子查询,类似"与"
2、should:选择性匹配子查询,类似"或"
3、must_not:必须不匹配,不参与算分,类似"非"
4、filter:必须匹配,不参与算分
首先保证你已经做好了 '实用篇-ES-环境搭建' ,创建了名为gghotel的索引库,导入了批量文档。然后开始下面的操作
输入如下DSL语句,表示搜索名字包含'如家',价格不高于400,在坐标31.21,121.5周围10km范围内的文档
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "如家"
}
}
],
"must_not": [
{
"range": {
"price": {
"gt": 400
}
}
}
],
"filter": [
{
"geo_distance": {
"distance": "10km",
"xxlocation": {
"lat": 31.21,
"lon": 121.5
}
}
}
]
}
}
}
八、搜索结果处理
lasticsearch(称为es)支持对搜索的结果,进行排序,默认是根据 '相关度' 算分,也就是score值,根据score值进行排序。
可以排序的字段类型有: keyword类型、数值类型、地理坐标类型、日期类型
首先保证你已经做好了 '实用篇-ES-环境搭建' ,创建了名为gghotel的索引库,导入了批量文档。然后开始下面的操作
1. 排序
输入如下DSL语句,表示对所有的文档,根据评分(score)进行降序排序,如果评分相同就根据价格(price)升序排序
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"score": {
"order": "desc"
},
"price": {
"order": "asc"
}
}
]
}
2. 分页
elasticsearch(称为es)默认情况下只返回前10 条数据。而如果要查询更多数据就需要修改分页参数,分页参数包括from和size,语法如下
GET /索引库名/_search
{
"query": {
"要查询的字段": {}
},
"from": 要查第几页, // 分页开始的位置,默认为0
"size": 每页显示多少条文档, // 期望获取的文档总数
"sort": [ //表示排序
{"price": "排序方式"}
]
}
输入如下DSL语句,表示对所有的文档,根据价格(price)进行升序排序,每次分页显示20条数据,看的是第六页
size默认是10,表示一页显示多少条文档。from默认是0,表示你要看的是第一页
GET /hotel/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": {
"order": "asc"
}
}
],
"from": 5,
"size": 20
}
3. 搜索结果处理-高亮
高亮: 就是在搜索结果中把搜索关键字突出显示。高亮显示的原理如下
1、将搜索结果中的关键字用标签标记出来
2、在页面中给标签添加css样式
首先保证你已经做好了 '实用篇-ES-环境搭建' ,创建了名为gghotel的索引库,导入了批量文档。然后开始下面的操作
语法
GET /索引库名/_search
{
"query": {
"match": { //match表示带关键字的查询
"字段": "TEXT"
}
},
"highlight": {
"fields": {
"字段名": {
"require_field_match": "false",//默认是true,表示 '字段' 要和 '字段名' 要一致。如果我们写的是不一致的话,就需要修改为false
"pre_tags": "<em>", // 用来标记高亮字段的前置标签,es会帮我们把标签加在关键字上。默认是<em>
"post_tags": "</em>" // 用来标记高亮字段的后置标签,es会帮我们把标签加在关键字上。默认是</em>
}
}
}
}
总结
GET /索引库名/_search
{
"query": {
"match": {
"字段名": "如家"
}
},
"from": 0, // 分页开始的位置
"size": 20, // 期望获取的文档总数
"sort": [
{ "price": "asc" }, // 普通排序
{
"_geo_distance" : { // 距离排序
"location" : "31.040699,121.618075",
"order" : "asc",
"unit" : "km"
}
}
],
"highlight": {
"fields": { // 高亮字段
"字段名": {
"pre_tags": "<em>", // 用来标记高亮字段的前置标签
"post_tags": "</em>" // 用来标记高亮字段的后置标签
}
}
}
}