Elasticsearch[学习笔记]

1url

1 .1elasticsearch-head访问地址

http://192.168.124.129:9100/

1.2kibana 访问地址

http://192.168.124.129:5601/app/home#/

进入容器,开一个伪端口
docker exec -u 0 -it 1057344e383e /bin/bash

执行

 apt-get update
  apt-get install vim -y

#使用vi 修改文件内容
vi /usr/share/kibana/config/kibana.yml

server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://192.168.124.129:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
i18n.locale: zh-CN

2

3酒店搜索

3.1创建酒店索引

PUT /hotel
{
	"mappings": {
		"properties": {
			"title": {
				"type": "text"
			},
			"city": {
				"type": "keyword"
			},
			"price": {
				"type": "double"
			}
		}
	}
}

3.2写入文档

POST /hotel/_doc/001
{
 "title":"好再来酒店",
 "city":"青岛",
 "price":"578.23"
}

 

 3.3根据id搜索文档

 GET /hotel/_doc/001

 3.4根据一般字段搜索文档

使用term进行精确查询

GET /hotel/_search
{
	"query": {
		"term": {
			"price": {
				"value": "578.23"
			}
		}
	}
}

 3.5根据文本字段搜索文档

使用match搜索对某个字段进行模糊匹配

GET /hotel/_search
{
	"query": {
		"match": {
		 "title": "再来"
		}
	}
}

4SpringBoot整合Spring Data Elasticsearch

4.1pom引入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>myes</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>myes</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <elasticsearch>7.17.1</elasticsearch>

    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

4.2修改 application.yml

spring:
  elasticsearch:
    rest:
      uris: http://192.168.124.129:9200

4.3代码

Hotel类

package com.example.myes.dto;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

import java.io.Serializable;

/**
 * @author: liuy
 * @date: 2022/11/9 20:40
 * @description:
 * @version: 1.0
 */
@Document(indexName = "hotel")
@Data
public class Hotel implements Serializable {
    private static final long serialVersionUID = 5649160852330501329L;
    /**
     * es的_id
     */
    @Id
    private String id;
    /**
     * 对应索引的title
     */
    private String title;
    /**
     * 对应索引的city
     */
    private String city;
    /**
     * 对应索引的price
     */
    private String price;
}
HotelEsDao类
package com.example.myes.dao;

import com.example.myes.dto.Hotel;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

/**
 * @author: liuy
 * @date: 2022/11/9 20:45
 * @description:
 * @version: 1.0
 */

public interface HotelEsDao extends CrudRepository<Hotel, String> {
    List<Hotel> findByTitleLike(String title);
}

定义HotelEsService类

package com.example.myes.service;

import com.example.myes.dao.HotelEsDao;
import com.example.myes.dto.Hotel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: liuy
 * @date: 2022/11/9 20:48
 * @description:
 * @version: 1.0
 */
@Service
public class HotelEsService {
    @Autowired
    private HotelEsDao hotelEsDao;

    public List<Hotel> getHotelFromTitle(String keyword) {
        return hotelEsDao.findByTitleLike(keyword);
    }
}

定义TestController类
package com.example.myes.controller;

import com.example.myes.dto.Hotel;
import com.example.myes.service.HotelEsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author: liuy
 * @date: 2022/11/9 20:14
 * @description:
 * @version: 1.0
 */
@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private HotelEsService hotelEsService;

    @RequestMapping("/test1")
    public String test1(String title) {
        List<Hotel> hotelList = hotelEsService.getHotelFromTitle(title);
        if(CollectionUtils.isEmpty(hotelList)){
            return "no data";
        }
        return  hotelList.toString();
    }
}

5索引的操作

5.1创建索引

PUT /hotel
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
    
  }, 
	"mappings": {
		"properties": {
			"title": {
				"type": "text"
			},
			"city": {
				"type": "keyword"
			},
			"price": {
				"type": "double"
			}
		}
	}
}

 5.2删除索引

DELETE /hotel

5.3关闭索引

由于上一步删除了,先添加索引再关闭

PUT /hotel
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
    
  }, 
	"mappings": {
		"properties": {
			"title": {
				"type": "text"
			},
			"city": {
				"type": "keyword"
			},
			"price": {
				"type": "double"
			}
		}
	}
}


POST /hotel/_close

写入数据

POST /hotel/_doc/002 
{  
  "title":"阳光夏日酒店", 
  "city":"北京", 
  "price":788.00 
} 

 搜索数据

GET /hotel/_search 
{ 
  "query": { 
   "match": {          
      "title": "再来" 
    } 
  } 
}

5.4打开索引

POST /hotel/_open

5.5索引别名

创建三个索引

PUT /january_log 
{ 
    "mappings":{ 
        "properties":{ 
            "uid":{               
                "type":"keyword" 
            }, 
            "hotel_id":{          
                "type":"keyword" 
            }, 
            "check_in_date":{      
                "type":"keyword" 
            } 
        } 
    } 
}

PUT /february_log 
{ 
    "mappings":{ 
        "properties":{ 
            "uid":{               
                "type":"keyword" 
            }, 
            "hotel_id":{          
                "type":"keyword" 
            }, 
            "check_in_date":{      
                "type":"keyword" 
            } 
        } 
    } 
}



PUT /march_log 
{ 
    "mappings":{ 
        "properties":{ 
            "uid":{               
                "type":"keyword" 
            }, 
            "hotel_id":{          
                "type":"keyword" 
            }, 
            "check_in_date":{      
                "type":"keyword" 
            } 
        } 
    } 
}

插入数据,同一用户在不同月份的入住记录

POST /january_log/_doc/001 
{                                     
  "uid":"001", 
  "hotel_id":"92772", 
  "check_in_date":"2021-01-05" 
}
POST /february_log/_doc/001 
{                                 
  "uid":"001", 
  "hotel_id":"33224", 
  "check_in_date":"2021-02-23" 
}
POST /march_log/_doc/001 
{                                
  "uid":"001", 
  "hotel_id":"92772", 
  "check_in_date":"2021-03-28" 
}

创建别名

POST /_aliases 
{ 
  "actions": [ 
    { 
      "add": {    
        "index": "january_log", 
        "alias": "last_three_month" 
      } 
    }, 
    { 
      "add": {    
        "index": "february_log", 
        "alias": "last_three_month" 
      } 
    }, 
    { 
      "add": {    
        "index": "march_log", 
        "alias": "last_three_month" 
      } 
    } 
  ] 
}

请求在索引last_three_month中搜索uid为001的用户的入住记录

GET /last_three_month/_search 
{ 
  "query": { 
    "term": {        
      "uid": "001" 
    } 
  } 
}

需要指出的是,在默认情况下,当一个别名只指向一个索引时,写入数据的请求可以指向这个别名,如果这个别名指向多个索引(就像上面的例子),则写入数据的请求是不可以指向这个别名的

6映射 

6.1查看映射

GET /hotel/_mapping

6.2扩展映射

映射中的字段不能修改,可以扩展,新增字段tag

POST /hotel/_mapping 
{ 
  "properties": { 
    "tag": {                   
      "type": "keyword" 
    } 
  } 
}

查看 索引

GET /hotel/_mapping

 6.3基本的数据类型

keyword类型

keyword类型是不进行切分的字符串类型。这里的“不进行切分”指的是:在索引时,对keyword类型的数据不进行切分,直接构建倒排索引;在搜索时,对该类型的查询字符串不进行切分后的部分匹配。keyword类型数据一般用于对文档的过滤、排序和聚合

在现实场景中,keyword经常用于描述姓名、产品类型、用户ID、URL和状态码等。keyword类型数据一般用于比较字符串是否相等,不对数据进行部分匹配,因此一般查询这种类型的数据时使用term查询。

text类型

text类型是可进行切分的字符串类型。这里的“可切分”指的是:在索引时,可按照相应的切词算法对文本内容进行切分,然后构建倒排索引;在搜索时,对该类型的查询字符串按照用户的切词算法进行切分,然后对切分后的部分匹配打分

数字 ,日期等类型

6.4多字段

针对同一个字段,有时需要不同的数据类型,这通常表现在为了不同的目的以不同的方式索引相同的字段。例如,在订单搜索系统中,既希望能够按照用户姓名进行搜索,又希望按照姓氏进行排列,可以在mapping定义中将姓名字段先后定义为text类型和keyword类型,其中,keyword类型的字段叫作子字段,这样ES在建立索引时会将姓名字段建立两份索引,即text类型的索引和keyword类型的索引。订单搜索索引的定义如下

PUT /hotel_order 
{ 
  "mappings": { 
    "properties": { 
      "order_id": {      
        "type": "keyword" 
      }, 
      "user_id": {      
        "type": "keyword" 
      }, 
      "user_name": {      
        "type": "text", 
        "fields": {      
          "user_name_keyword": { 
            "type": "keyword" 
          } 
        } 
      }, 
      "hotel_id": {      
        "type": "keyword" 
      } 
    } 
  } 
} 

添加数据

如果报错:Malformed action/metadata line [3], expected START_OBJECT but found [VALUE_STRING]  注意不能换行

POST /_bulk 
{"index":{"_index":"hotel_order","_id":"001"}} 
{"order_id": "001","user_id":"user_00x","user_name":"Michael Jordan", "hotel_id":"h001"} 
{"index":{"_index":"hotel_order","_id":"002"}} 
{"order_id": "002","user_id":"user_00a","user_name":"Stephen Jordan", "hotel_id":"h0500"} 
{"index":{"_index":"hotel_order","_id":"003"}} 
{"order_id": "003","user_id":"user_30e","user_name":"Tim Jordan", "hotel_id":"h0520"} 
{"index":{"_index":"hotel_order","_id":"004"}} 
{"order_id": "004","user_id":"user_430","user_name":"Kobe Jordan", "hotel_id":"h0600"}

查询数据

GET /hotel_order/_search 
{ 
  "query": { 
    "match": {     
      "user_name": "Jordan" 
    } 
  }, 
  "sort": {     
    "user_name.user_name_keyword": "asc" 
  } 
}  

6.5文档操作

单条写入文档

POST /hotel/_doc/001 
{  
  "title":"好再来酒店", 
  "city":"青岛", 
  "price":578.23 
}  

批量写入文档

不指定_id

POST /_bulk                    
{"index":{"_index":"hotel"}}   
{"title": "文雅酒店","city": "北京","price": 556.00}    
{"index":{"_index":"hotel"}}    
{"title": "嘉怡假日酒店","city": "北京","price": 337.00}

指定_id

POST /_bulk                              
{"index":{"_index":"hotel","_id":"001"}}  
{"title": "文雅酒店","city": "北京","price": 556.00}  
{"index":{"_index":"hotel","_id":"002"}}   
{"title": "嘉怡假日酒店","city": "北京","price": 337.00} 

 更新单条文档

POST /hotel/_update/001 
{ 
 "doc": {                             
    "title": "好再来酒店", 
    "city": "北京", 
    "price": 659.45 
  } 
}

 查看更新后的结果

 GET /hotel/_doc/001

 upsert 存在更新,不存在新增

POST /hotel/_update/001 
{ 
  "doc": { 
    "title": "好再来酒店", 
    "city": "北京", 
    "price": 659.45 
  }, 
  "upsert": { 
    "title": "好再来酒店", 
    "city": "北京", 
    "price": 659.45 
  } 
}

批量更新文档

POST /_bulk    
{"update":{"_index":"hotel","_id":"001"}}  
{"doc":{"title": "文雅豪情酒店","city": "北京","price": 556.00}} 
{"update":{"_index":"hotel","_id":"002"}} 
{"doc":{"title": "嘉怡七天酒店","city": "北京","price": 337.00}}

根据条件更新文档

query用于指定更新数据的匹配条件,相当于SQL中的where语句;script用于指定具体的更新操作,相当于SQL的set内容

POST  /hotel/_update_by_query 
{ 
 "query": {              
    "term": { 
      "city": { 
        "value": "北京" 
      } 
    } 
  }, 
 "script": {             
    "source": "ctx._source['city']='上海'", 
    "lang": "painless" 
  } 
}

执行以上DSL后,ES将先搜索城市为“北京”的酒店,然后把这些酒店的城市字段的值改为“上海”。

删除单条文档

DELETE /hotel/_doc/001

批量删除文档

POST /_bulk 
{"delete":{"_index":"hotel","_id":"001"}} 
{" delete ":{"_index":"hotel","_id":"002"}} 

根据条件删除文档

条件删除文档的查询条件:城市为“北京”的文档 

POST /hotel/_delete_by_query 
{ 
 "query": {              
    "term": { 
      "city": { 
        "value": "北京" 
      } 
    } 
  } 
} 

7丰富的搜索功能

7.1搜索辅助功能

指定返回的字段

在ES中,通过_source子句可以设定返回结果的字段。_source指向一个JSON数组,数组中的元素是希望返回的字段名称。

因为已存在,所以先删后创建索引

DELETE /hotel 

PUT /hotel 
{ 
  "mappings": { 
    "properties": { 
      "title": {      
        "type": "text" 
      }, 
      "city": {      
        "type": "keyword"  
      }, 
      "price": {      
        "type": "double" 
      }, 
      "create_time": {   
        "type": "date", 
        "format": "yyyy-MM-dd HH:mm:ss" 
      }, 
      "amenities": {   
        "type": "text" 
      }, 
      "full_room": {   
        "type": "boolean" 
      }, 
      "location": {    
        "type": "geo_point" 
      }, 
      "praise": {  
        "type": "integer" 
      } 
    } 
  } 
}  

造数据

POST /_bulk 
{"index":{"_index":"hotel","_id":"001"}} 
{"title": "文雅酒店","city": "青岛","price": 556.00,"create_time":"2020-04-18 12:00:00", "amenities":"浴池,普通停车场/充电停车场","full_room":false,"location":{"lat":36.083078,"lon": 120.37566},"praise":10} 
{"index":{"_index":"hotel","_id":"002"}} 
{"title": "金都嘉怡假日酒店","city": "北京","price": 337.00,"create_time": "2021-03-15 20:00:00","amenities":"wifi,充电停车场/可升降停车场","full_room":false, "location":{"lat": 39.915153,"lon": 116.4030},"praise":60} 
{"index":{"_index":"hotel","_id":"003"}} 
{"title": "金都欣欣酒店","city": "天津","price": 200.00,"create_time":"2021-05-09 16:00:00","amenities":"提供假日party,免费早餐,可充电停车场","full_room": true,"location":{"lat": 39.186555,"lon": 117.162007},"praise":30} 
{"index":{"_index":"hotel","_id":"004"}} 
{"title": "金都酒店","city": "北京","price": 500.00,"create_time":"2021-02-18 08:00:00", "amenities":"浴池(假日需预定),室内游泳池,普通停车场","full_room":true,"location": {"lat": 39.915343,"lon": 116.4239},"praise":20} 
{"index":{"_index":"hotel","_id":"005"}} 
{"title": "文雅精选酒店","city": "北京","price": 800.00,"create_time": "2021-01-01 08:00:00","amenities":"浴池(假日需预定),wifi,室内游泳池,普通停车场", "full_room":true,"location":{"lat": 39.918229,"lon": 116.422011},"praise":20} 

指定搜索结果只返回title和city字段

GET /hotel/_search 
{ 
  "_source": ["title","city"],    
 "query": {                      
    "term": { 
      "city": { 
        "value": "北京" 
      } 
    } 
  } 
} 

 结果计数

返回城市为“北京”的酒店个数

GET /hotel/_count 
{ 
 "query": {       
    "term": { 
      "city": { 
        "value": "北京" 
      } 
    } 
  } 
}  

结果分页

用户可以通过设置from和size来定义搜索位置和每页显示的文档数量,from表示查询结果的起始下标,默认值为0,size表示从起始下标开始返回的文档个数,默认值为10

GET /hotel/_search 
{ 
 "from": 0,        
 "size": 20,        
 "query": {         
    "term": { 
      "city": { 
        "value": "北京" 
      } 
    } 
  } 
}

在默认情况下,用户最多可以取得10 000个文档,即from为0时,size参数最大为10 000

对于普通的搜索应用来说,size设为10 000已经足够用了。如果确实需要返回多于10 000条的数据,可以适当修改max_result_window的值。以下示例将hotel索引的最大窗口值修改为了20 000。

PUT /hotel/_settings 
{                                    
  "index": { 
    "max_result_window": 20000 
  } 
}

注意,如果将配置修改得很大,一定要有足够强大的硬件作为支撑。

性能分析

 "profile": "true",            //打开性能剖析开关 

GET /hotel/_search 
{ 
 "profile": "true",               
 "query": {     
    "match": { 
      "title": "金都" 
    } 
  } 
} 

{ 
   "query":{                     
        "match":{                  
            "title":"金都" 
        } 
    } 
} 

评分分析

GET /hotel/_explain/002 
{ 
  "query": { 
   "match": {                            
      "title": "金都" 
    } 
  } 
}

7.2丰富的搜索匹配功能

查询所有文档

当需要查询所有文档的数据时,对应的SQL语句为select*form table_name

使用ES的match_all查询可以完成类似的功能

默认每个文档分值1.0,可以通过boost参数设定该分值

GET /hotel/_search 
{ 
 "_source": [         
    "title", 
    "city" 
  ], 
  "query": { 
   "match_all": {     
      "boost": 2      
    } 
  } 
}   

term级别查询

搜索住宿价格为500元的酒店,price字段为数值型数据

GET /hotel/_search 
{ 
  "query": { 
    "term": { 
      "price": {            
        "value": "500"      
      } 
    } 
  } 
} 

 

以下示例是搜索城市为北京的酒店,city字段为关键字类型数据keyword

GET /hotel/_search 
{ 
  "query": { 
    "term": { 
     "city": {               
        "value": "北京"    
      } 
    } 
  } 
} 

搜索没有满房的酒店,full_room(满房状态)字段为布尔型数据

POST hotel/_search
{
	"query":{ 
					"term":{
						"full_room":false
					}	 
			 
		}
 
}

搜索北京不满的

POST hotel/_search
{
	"query":{
		"bool":{
			"filter":[
				{
					"term":{
						"city":{
							"value":"北京"
						}
					}	
				},
				{
					"term":{
						"full_room":false
					}	
				}
				
			]
		}
	}
}

对于日期型的字段查询,需要按照该字段在mappings中定义的格式进行查询

GET /hotel/_search 
{ 
  "query": { 
    "term": { 
      "create_time": {               
        "value": "2021-03-15 20:00:00" 
      } 
    } 
  } 
} 

 terms查询用于查询一个或多个值与待查字段是否完全匹配

搜索城市为“北京”或者“天津”的酒店示例

GET /hotel/_search 
{ 
  "query": { 
    "terms": { 
     "city": [              
        "北京", 
        "天津" 
      ] 
    } 
  } 
} 

range查询

 range查询用于范围查询,一般是对数值型和日期型数据的查询。使用range进行范围查询时,用户可以按照需求中是否包含边界数值进行选项设置,可供组合的选项如下:·gt:大于;·lt:小于;·gte:大于或等于;·lte:小于或等于。

查询住宿价格在300~500(包含边界值)元的酒店

查询住宿价格大于300(不包含边界值)元的酒店

GET /hotel/_search 
{ 
  "query": { 
   "range": {        
      "price": {     
        "gte": 300, 
        "lte": 500 
      } 
    } 
  } 
} 


GET /hotel/_search 
{ 
  "query": { 
   "range": {        
      "price": {     
        "gt": 300 
      } 
    } 
  } 
} 

exists查询

在某些场景下,我们希望找到某个字段不为空的文档,则可以用exists搜索。字段不为空的条件有:·值存在且不是null;·值不是空数组;·值是数组,但不是[null]。

DELETE /hotel_1 

PUT /hotel_1 
{ 
  "mappings": { 
    "properties": { 
      "title": {              
        "type": "text" 
      }, 
     "tag": {                
        "type": "keyword" 
      } 
    } 
  } 
}

POST /hotel_1/_doc/006 
{                            
  "title": "环球酒店", 
  "tag": null 
} 

POST /hotel_1/_doc/007 
{                          
  "title": "环球酒店", 
  "tag": [] 
} 

POST /hotel_1/_doc/008 
{                
  "title": "环球酒店", 
  "tag": [null] 
} 

都不命中

GET /hotel_1/_search 
{ 
  "query": { 
   "exists": {            
      "field": "tag" 
    } 
  } 
} 

布尔查询

must查询,计算得分

使用must查询城市为北京并且价格在350~400元的酒店

GET /hotel/_search 
{ 
  "query": { 
    "bool": { 
      "must": [                
        {                      
          "term": { 
            "city": { 
              "value": "北京" 
            } 
          } 
        }, 
        {                    
          "range": { 
            "price": { 
              "gte": 350, 
              "lte": 400 
            } 
          } 
        } 
      ] 
    } 
  } 
} 

should查询 计算得分

使用should查询城市为北京或者天津的酒店

GET /hotel/_search 
{ 
  "query": { 
    "bool": { 
      "should": [                   
        {                           
          "term": { 
            "city": { 
              "value": "北京" 
            } 
          } 
        }, 
        {                          
          "term": { 
            "city": { 
              "value": "天津" 
            } 
          } 
        } 
      ] 
    } 
  } 
} 

must not查询 计算得分

must not查询城市不是北京也不是天津的酒店

GET /hotel/_search 
{ 
  "query": { 
    "bool": { 
      "must_not": [                  
        {                            
          "term": { 
            "city": { 
              "value": "北京" 
            } 
          } 
        }, 
        {                            
          "term": { 
         "city": { 
              "value": "天津" 
            } 
          } 
        } 
      ] 
    } 
  } 
} 

filter查询关注的是查询条件和文档是否匹配,不进行相关的打分计算,但是会对部分匹配结果进行缓存

请求城市为北京并且未满房的酒店的查询结果

GET /hotel/_search 
{ 
  "query": { 
    "bool": { 
      "filter": [             
        {                     
          "term": { 
            "city": "北京" 
          } 
        }, 
        {                     
          "term": { 
            "full_room": false 
          } 
        } 
      ] 
    } 
  } 
}

如果不想让检索词频率TF(Term Frequency)对搜索结果排序有影响,只想过滤某个文本字段是否包含某个词,可以使用Constant Score将查询语句包装起来

查询amenities字段包含关键词“停车场”的酒店,满足条件打1分

GET /hotel/_search 
{ 
  "_source": ["amenities"],  
  "query": { 
   "constant_score": {     
      "filter": { 
        "match": {         
          "amenities": "停车场" 
        } 
      } 
    } 
  } 
} 

 参数boost可以控制命中文档的得分,默认值为1.0

GET /hotel/_search 
{ 
  "_source": ["amenities"],  
  "query": { 
    "constant_score": { 
      "boost": 2.0,                
      "filter": { 
        "match": { 
          "amenities": "停车场" 
        } 
      } 
    } 
  } 
} 

当使用ES进行搜索时,命中的文档默认按照相关度进行排序。有些场景下用户需要干预该“相关度”,此时就可以使用Function Score查询。使用时,用户必须定义一个查询以及一个或多个函数,这些函数为每个文档计算一个新分数

GET /hotel/_search 
{ 
  "_source": ["title","city"],  
  "query": { 
    "function_score": { 
      "query": {                 //查询符合条件的文档 
        "term": { 
          "city": { 
            "value": "北京" 
          } 
        } 
      }, 
      "functions": [             //定义函数 
        {                        //此处只定义了一个函数:随机数函数 
          "random_score": {} 
        } 
      ], 
      "score_mode": "sum"        //最终分数是各个函数的加和值 
    } 
  } 
}  

全文搜索(数据类型是text)

match查询

match查询是全文搜索的主要代表。对于最基本的math搜索来说,只要分词中的一个或者多个在文档中存在即可。例如搜索“金都酒店”,查询词先被分词器切分为“金”“都”“酒”“店”,因此,只要文档中包含这4个字中的任何一个字,都会被搜索到,默认使用的是标准分词器,不适合中文搜索。

按照标题搜索“金都酒店”

GET /hotel/_search 
{ 
  "_source": ["title"],      //只返回title字段 
  "query": { 
   "match": {                //匹配title字段为“金都酒店”的文档 
      "title": "金都酒店" 
    } 
  } 
} 



GET /hotel/_search 
{ 
  "_source": ["title"],      //只返回title字段 
  "query": { 
    "match": {               //匹配title字段为“金都酒店”的文档 
      "title": { 
        "query": "金都酒店" 
      } 
    } 
  } 
}  

match搜索可以设置operator参数,该参数决定文档按照分词后的词集合进行“与”还是“或”匹配。在默认情况下,该参数的值为“或”关系,即operator的值为or,这也解释了搜索结果中包含部分匹配的文档。如果希望各个词之间的匹配结果是“与”关系,则可以设置operator参数的值为and

 查询同时包含 金 和都的文档

GET /hotel/_search 
{ 
  "_source": ["title"],  
  "query": { 
    "match": { 
      "title":{ 
        "query": "金都", 
        "operator":"and"            //查询词之间的匹配结果为“与”关系 
      } 
    } 
  } 
}  

有时搜索多个关键字,关键词和文档在某一个比例上匹配即可,如果使用“与”操作过于严苛,如果使用“或”操作又过于宽松。这时可以采用minimum_should_match参数,该参数叫作最小匹配参数,其值为一个数值,意义为可以匹配上的词的个数。在一般情况下将其设置为一个百分数,因为在真实场景中并不能精确控制具体的匹配数量。以下示例设置最小匹配为80%的文档:

GET /hotel/_search 
{ 
  "_source": ["title"],  
  "query": { 
    "match": { 
      "title": {                          //match搜索条件 
        "query": "金都", 
        "operator": "or", 
        "minimum_should_match": "80%"     //设置最小匹配度为80% 
      } 
    } 
  } 
} 

 

 multi_match查询

有时用户需要在多个字段中查询关键词,除了使用布尔查询封装多个match查询之外,可替代的方案是使用multi_match。可以在multi_match的query子句中组织数据匹配规则,并在fields子句中指定需要搜索的字段列表。

在title和amenities两个字段中同时搜索“假日”关键词

GET /hotel/_search 
{ 
  "_source": ["title","amenities"],  
  "query": { 
    "multi_match": { 
      "query": "假日",            //匹配关键字为“假日” 
      "fields": [                 //设置匹配的字段为title和amenities 
        "title", 
        "amenities" 
      ] 
    } 
  } 
}  

match_phrase查询

match_phrase用于匹配短语,与match查询不同的是,match_phrase用于搜索确切的短语或邻近的词语。假设在酒店标题中搜索“文雅酒店”,希望酒店标题中的“文雅”与“酒店”紧邻并且“文雅”在“酒店”前面,则使用match_phrase查询的DSL如下:

GET /hotel/_search 
{ 
  "query": { 
    "match_phrase": { 
      "title": "文雅酒店" 
    } 
  } 
}  

使用match_phrase进行查询时,ES将查询文本“精选酒店”切分为“精选”“酒店”,“文雅”匹配时命中了文档001和文档005,但是“酒店”匹配时要求“酒店”必须在“文雅”之后并且索引位置和“文雅”之差为1,而文档001符合匹配要求但是文档005不符合要求。如果需要文档005也命中上述查询,则可以设置match_phrase查询的slop参数,它用来调节匹配词之间的距离阈值。下面的DSL将slop设置为2 

GET /hotel/_search 
{ 
  "query": { 
    "match_phrase": { 
      "title": { 
        "query": "文雅酒店", 
        "slop": 2              //将“文雅”和“酒店”之间的最大匹配距离设置为2 
      } 
    } 
  } 
}  

基于地理位置查询

支持两种类型的地理查询:一种是地理点(geo_point),即经纬度查询,另一种是地理形状查询(geo_shape),即支持点、线、圆形和多边形查询等。

对应于geo_point字段类型的查询方式有3种,分别为geo_distance查询、geo_bounding_box查询和geo_polygon。

geo_distance查询方式需要用户指定一个坐标点,在指定距离该点的范围后,ES即可查询到相应的文档。假设北京天安门的经纬度为[116.4039,39.915143],以下为使用geo_distance查询所找到的天安门5km范围内的酒店:

GET /hotel/_search 
{ 
 "_source": [                    //只返回部分字段 
    "title", 
    "city", 
    "location" 
  ], 
  "query": { 
    "geo_distance": { 
      "distance": "5km",        //设置距离范围为5km 
      "location": {             //设置中心点经纬度 
        "lat": "39.915143",     //设置纬度 
        "lon": "116.4039"       //设置经度 
      } 
    } 
  } 
}  

geo_bounding_box查询提供的是矩形内的搜索,需要用户给出左上角的顶点地理坐标和右下角的顶点地理坐标。假设定义国贸商圈为一个矩形,其左上角顶点的经纬度为[116.457044,39.922821],右下角顶点的经纬度为[116.479466,39.907104]

GET /hotel/_search 
{ 
  "query": { 
    "geo_bounding_box": { 
      "location": { 
        "top_left": {        //设置左上角的顶点坐标 
          "lat": "39.922821", 
          "lon": "116.457044" 
        }, 
        "bottom_right": {    //设置右下角的顶点坐标 
          "lat": "39.907104", 
          "lon": "116.479466" 
        } 
      } 
    } 
  } 
}  

geo_polygon比geo_bounding_box提供的地理范围功能更加灵活,它支持多边形内的文档搜索,使用该查询需要提供多边形所有顶点的地理坐标。假设北京地坛公园商圈的地形为三角形,该三角形的三个顶点的经纬度分别为[116.417088,39.959829]、[116.432035,39.960272]和[116.421399,39.965802]

GET /hotel/_search 
{ 
  "query": { 
    "geo_polygon": { 
      "location": { 
        "points": [ 
          {        //设置三角形的第1个顶点坐标 
            "lat": "39.959829", 
            "lon": "116.417088" 
          }, 
          {        //设置三角形的第2个顶点坐标 
            "lat": "39.960272", 
            "lon": "116.432035" 
          }, 
          {        //设置三角形的第3个顶点坐标 
            "lat": "39.965802", 
            "lon": "116.421399" 
          } 
        ] 
      } 
    } 
  } 
} 

搜索建议

ES中的Completion Suggester是比较合适的。为了使用Completion Suggester,其对应的字段类型需要定义为completion类型。在以下示例中定义了一个酒店搜索建议的索引:

PUT /hotel_sug 
{ 
  "mappings": { 
    "properties": { 
      "query_word": { //定义query_word字段,类型为completion 
        "type": "completion" 
      } 
    } 
  } 
}  

POST /_bulk 
{"index":{"_index":"hotel_sug","_id":"001"}} 
{"query_word": "如家酒店"} 
{"index":{"_index":"hotel_sug","_id":"002"}} 
{"query_word": "如家快捷酒店"} 
{"index":{"_index":"hotel_sug","_id":"003"}} 
{"query_word": "如家精选酒店"} 
{"index":{"_index":"hotel_sug","_id":"004"}} 
{"query_word": "汉庭假日酒店"}  

假设用户输入“如家”关键词,需要ES给出前缀为该词的酒店查询词

GET /hotel_sug/_search 
{ 
  "suggest": { 
    "hotel_zh_sug": {           //定义搜索建议名称 
      "prefix": "如家",         //设置搜索建议的前缀 
      "completion": {           //设置搜索建议对应的字段名称 
        "field": "query_word" 
      } 
    } 
  } 
} 

在上述查询中,hotel_zh_sug定义的是搜索建议的名称,prefix定义的是用户输入的关键词,completion.field定义的是搜索建议的候选集对应的字段名称。

7.3按字段值排序

在默认情况下,ES对搜索结果是按照相关性降序排序的

ES提供了sort子句可以对数据进行排序。使用sort子句一般是按照字段信息进行排序,不受相关性影响,而且打分步骤需要耗费一定的硬件资源和时间,因此默认情况下,不对文档进行打分。使用sort排序分为两种类别,一种是按照字段值的大小进行排序,另一种是按照给定地理坐标的距离远近进行排序。

按普通字段值排序

使用sort子句对字段值进行排序时需要指定排序的字段。ES默认是按照字段值进行升序排序,可以设置order参数为asc或desc,指定按照字段值进行升序或者降序排序。以下示例为搜索名称包含“金都”的酒店,并对酒店按照价格进行降序排列。

GET /hotel/_search 
{ 
 "_source": [              //只返回部分字段 
    "title", 
    "price" 
  ], 
 "query": {                //搜索条件 
    "match": { 
      "title": "金都" 
    } 
  }, 
  "sort": [ 
   {                      //按照价格降序排列 
      "price": { 
        "order": "desc" 
      } 
    } 
  ] 
} 

默认情况下ES查询时使用sort对结果排序是不计算分数的。也可以使用sort对搜索结果按照多个字段进行排序。例如,用户可以按照价格进行降序排列,然后再按照口碑值进行降序排列 

GET /hotel/_search 
{ 
 "_source": [              //返回部分字段 
    "title", 
    "price", 
    "praise" 
  ], 
 "query": {                //查询条件 
    "match": { 
      "title": "金都" 
    } 
  }, 
  "sort": [ 
    { 
      "price": {          //按照价格进行降序排列 
        "order": "desc" 
      },  
      "praise": {        //按照口碑进行降序排列 
        "order": "desc" 
      } 
    } 
  ] 
} 

按地理距离排序

前面我们介绍了ES提供的基于地理位置的查询功能,使用geo_distance查询,配合sort可以指定另一种排序规则,即按照文档坐标与指定坐标的距离对结果进行排序。使用时,需要在sort内部指定排序名称为geo_distanc,并指定目的地坐标。除了可以指定升序或者降序排列外,还可以指定排序结果中sort子句中的距离的计量单位,默认值为km即千米。在进行距离计算时,系统默认使用的算法为arc,该算法的特点是计算精准但是耗费时间较长,用户可以使用distance_type参数选择另一种计算速度快但经度略差的算法,名称为plane。如下示例使用geo_distance查询天安门5km范围内的酒店,并按照距离由近及远进行排序

GET /hotel/_search 
{ 
 "_source": [                         //返回部分字段 
    "title", 
    "city", 
    "location" 
  ], 
  "query": { 
    "geo_distance": { 
      "distance": "5km",             //设置地理范围为5km 
      "location": {                  //设置中心点坐标 
        "lat": "39.915143", 
        "lon": "116.4039" 
      } 
    } 
  }, 
 "sort": [                           //设置排序逻辑 
    { 
      "_geo_distance": { 
        "location": {                //设置排序的中心点坐标 
          "lat": "39.915143", 
          "lon": "116.4039" 
        }, 
        "order": "asc",             //按距离由近到远进行排序 
        "unit": "km",               //排序所使用的距离的计量单位 
        "distance_type": " plane "  //排序所使用的距离计算算法 
      } 
    } 
  ] 
}   

8聚合

 定义酒店索引,存在的话先删除DELETE /hotel

PUT /hotel 
{ 
  "settings": { 
    "number_of_shards": 1                //指定主分片个数为1 
  },  
  "mappings": { 
    "properties": { 
      "title": {                         //定义title字段的类型为text 
        "type": "text" 
      }, 
     "city": {                           //定义city字段的类型为keyword 
        "type": "keyword" 
      }, 
      "price": {                         //定义price字段的类型为double 
        "type": "double" 
      }, 
      "create_time": {                   //定义create_time字段的类型为date 
        "type": "date" 
      }, 
            "full_room": {               //定义full_room字段的类型为boolean 
        "type": "boolean" 
      }, 
      "location": {                      //定义location字段的类型为geo_point 
        "type": "geo_point" 
      }, 
      "tags": {                          //定义tags字段的类型为keyword 
        "type": "keyword"  
      }, 
      "comment_info": {                  //定义comment_info字段的类型为object 
        "properties": { 
          "favourable_comment": {        //定义favourable_comment字段的类型为integer 
            "type": "integer" 
          }, 
          "negative_comment": {          //定义negative_comment字段的类型为integer 
            "type": "integer" 
          } 
        } 
      } 
    } 
  } 
}

添加数据

POST /_bulk 
{"index":{"_index":"hotel","_id":"001"}} 
{"title": "文雅酒假日酒店","city":"北京","price": 556.00,"create_time":"20200418120000","full_room":true,"location":{"lat": 39.938838,"lon": 116.449112}, "tags":["wifi","小型电影院"],"comment_info":{"favourable_comment":20,"negative_comment":10}} 
{"index":{"_index":"hotel","_id":"002"}} 
{"title": "金都嘉怡假日酒店","city":"北京","create_time":"20210315200000","full_room":false,"location":{"lat": 39.915153,"lon": 116.4030},"tags": ["wifi","免费早餐"],"comment_info":{"favourable_comment":20,"negative_ comment":10}} 
{"index":{"_index":"hotel","_id":"003"}} 
{"title": "金都假日酒店","city":"北京","price": 200.00,"create_time":"20210509160000","full_room":true,"location":{"lat": 40.002096,"lon":  116.376673},"comment_info":{"favourable_comment":20,"negative_comment":10}} 
{"index":{"_index":"hotel","_id":"004"}} 
{"title": "金都假日酒店","city":"天津","price": 500.00,"create_time":"20210218080000","full_room":false,"location":{"lat":39.155004,"lon":  117.203976},"tags":["wifi","免费车位"]} 
{"index":{"_index":"hotel","_id":"005"}} 
{"title": "文雅精选酒店","city":"天津","price": 800.00,"create_time":"20210101080000","full_room":true,"location":{"lat": 39.178447,"lon":  117.219999},"tags":["wifi","充电车位"],"comment_info":{"favourable_comment" :20,"negative_comment":10}}

8.1聚合指标

常见的统计指标

计算平均值 

当使用avg子句进行平均值的聚合时,可以在avg子句中指定聚合的字段。在默认情况下,查询将匹配所有文档,如果不需要返回匹配的文档信息,最好将返回的文档个数设置为0。这样既可以让结果看起来更整洁,又可以提高查询速度

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
   "my_agg": {       //聚合名称 
      "avg": { 
        "field": "price"     //计算文档的平均价格 
      } 
    } 
  } 
}  

如果聚合的指标字段不是ES的基本类型,例如object类型,则可以使用点运算符进行引用

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
      "avg": { 
        //使用点运算符引用object类型字段的数据 
        "field": "comment_info.favourable_comment" 
      } 
    } 
  } 
} 

与平均值类似,最大值、最小值及加和值分别使用max、min和sum子句进行聚合

为了避免多次请求,ES还提供了stats聚合。stats聚合可以将对应字段的最大值、最小值、平均值及加和值一起计算并返回计算结果

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
      "stats": {             //使用stats运算符计算多个指标 
        "field": "price" 
      } 
    } 
  } 
}  

 空值处理

使用value_count聚合统计了price字段中非空值的个数

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
     "value_count": {                //统计price字段中非空值的个数 
        "field": "price" 
      } 
    } 
  } 
} 

如果判断的字段是数组类型,则value_count统计的是符合条件的所有文档中该字段数组中非空元素个数的总和,而不是数组的个数总和

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
      "value_count": {              //统计tags字段数组中非空元素的个数 
        "field": "tags" 
      } 
    } 
  } 
} 

如果需要以空值字段的数据作为聚合指标对其进行聚合,可以在指标统计中通过missing参数指定填充值对空值进行填充。以下示例演示了对price字段进行聚合,并设定了当字段值为空值时使用100进行替代的查询请求

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
      "sum": { 
        "field": "price", 
        "missing":100            //计算加和值时将price字段中的空值用100代替 
      } 
    } 
  } 
}  

8.2桶聚合

单维度桶聚合

匹配的方式有terms、filter和ranges等。本节只介绍比较有代表性的terms查询和ranges查询,对其他匹配方式感兴趣读者可以阅读相关文档进行学习,这里不再赘述。

terms聚合是按照字段的实际完整值进行匹配和分组的,它使用的维度字段必须是keyword、bool、keyword数组等适合精确匹配的数据类型,因此不能对text字段直接使用terms聚合,如果对text字段有terms聚合的需求,则需要在创建索引时为该字段增加多字段功能

因为ES支持多桶聚合,所以每个桶聚合需要定义一个名字,此处定义了一个桶聚合,名字为my_agg。在这个桶聚合中使用了一个terms聚合,聚合字段选择了城市,目的是统计各个城市的酒店的文档个数。在聚合外面,因为不希望返回任何文档,所以指定查询返回的文档为0

在默认情况下,进行桶聚合时如果不指定指标,则ES默认聚合的是文档计数

GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
      "terms": {                    //按照城市进行聚合 
        "field": "city" 
      } 
    } 
  } 
} 
GET /hotel/_search 
{ 
  "size": 0,  
  "aggs": { 
    "my_agg": { 
      "terms": { 
        "field": "full_room"            //按照满房状态进行聚合 
      } 
    } 
  } 
}   

ranges聚合也是经常使用的一种聚合。它匹配的是数值字段,表示按照数值范围进行分组。用户可以在ranges中添加分组,每个分组用from和to表示分组的起止数值。注意该分组包含起始数值,不包含终止数值

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
    "my_agg": { 
      "range": { 
        "field": "price", 
        "ranges": [          //多个范围桶 
          { 
            "to": 200        //不指定from,默认from为0 
          }, 
          { 
            "from": 200, 
            "to": 500 
          }, 
          { 
            "from": 500      //不指定to,默认to为该字段最大值 
          } 
        ] 
      } 
    } 
  } 
}  

有时还需要对单维度桶指定聚合指标,聚合指标单独使用子aggs进行封装,该aggs子句的使用方式和上一节介绍的聚合指标相同。以下请求表示按照城市维度进行聚合,统计各个城市的平均酒店价格

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
   "my_agg": {                    //单维度聚合名称 
      "terms": {                  //定义单维度桶 
        "field": "city" 
      }, 
     "aggs": {                    //用于封装单维度桶下的聚合指标 
        "my_sum": {               //聚合指标名称 
          "sum": {                //对price字段进行加和 
            "field": "price", 
            "missing": 200 
          } 
        } 
      } 
    } 
  } 
}  

多维度桶嵌套聚合

统计各个城市的满房和非满房状态下的酒店平均价格。ES支持嵌套桶聚合,进行嵌套时,可以使用aggs子句进行子桶的继续嵌套,指标放在最里面的子桶内

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
   "group_city": {               //多维度桶名称 
      "terms": { 
        "field": "city" 
      }, 
     "aggs": {                  //单维度桶 
        "group_full_room": { 
          "terms": { 
            "field": "full_room" 
          }, 
          "aggs": {             //聚合指标 
            "my_sum": { 
              "avg": { 
                "field": "price", 
                "missing": 200 
              } 
            } 
          } 
        } 
      } 
    } 
  } 
}  

地理距离聚合

给定了一个地理位置,此处使用ranges聚合对距离该位置的酒店划分了3个分组的桶:第1个桶为3km范围内;第2个桶为3~10km;第3个桶为大于等于10km

GET /hotel/_sear
{ 
  "size": 0, 
  "aggs": { 
    "my_agg": { 
      "geo_distance": { 
        "field": "location", 
        "origin": {                //指定聚合的中心点经纬度 
          "lat":  39.915143, 
          "lon": 116.4039 
        }, 
        "unit": "km",              //指定聚合时的距离计量单位 
        "ranges": [                //指定每一个聚合桶的距离范围 
          { 
            "to": 3 
          }, 
          { 
            "from": 3, 
            "to":10 
          }, 
          { 
            "from": 10 
          } 
        ] 
      } 
    } 
  } 
}  

可以指定聚合指标进行地理距离聚合,下面的DSL将按照bucket分桶聚合酒店的最低价格

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
    "my_agg": { 
      "geo_distance": {             //按地理距离聚合 
        "field": "location", 
        "origin": {                 //指定中心点经纬度坐标 
          "lat": 39.915143, 
          "lon": 116.4039 
        }, 
        "unit": "km", 
        "ranges": [                 //指定距离范围桶 
          { 
            "to": 3 
          }, 
          { 
            "from": 3, 
            "to": 10 
          }, 
          { 
            "from": 10 
          } 
        ] 
      }, 
     "aggs": {                      //指定聚合指标 
        "my_min": {                 //聚合指标名称 
          "min": {                  //计算每个桶内price字段的最小值 
            "field": "price", 
            "missing": 100 
          } 
        } 
      } 
    } 
  } 
}

8.3聚合方式

ES支持灵活的聚合方式,它不仅支持聚合和查询相结合,而且还可以使聚合的过滤条件不影响搜索条件,并且还支持在聚合后的结果中进行过滤筛选。本节将介绍这些聚合方式。

直接聚合

直接聚合指的是聚合时的DSL没有query子句,是直接对索引内的所有文档进行聚合。8.2介绍的示例都属于直接聚合

先查询再聚合

与直接聚合相对应,这种查询方式需要增加query子句,query子句和普通的query查询没有区别,参加聚合的文档必须匹配query查询

GET /hotel/_search 
{ 
  "size": 0,  
 "query": {                     //指定查询query逻辑 
    "term": { 
      "city": { 
        "value": "北京" 
      } 
    } 
  }, 
 "aggs": {                     //指定聚合逻辑 
    "my_agg": { 
      "avg": { 
        "field": "price" 
      } 
    } 
  } 
}  

前过滤器

有时需要对聚合条件进一步地过滤,但是又不能影响当前的查询条件。例如用户进行酒店搜索时的搜索条件是天津的酒店,但是聚合时需要将非满房的酒店平均价格进行聚合并展示给用户。此时不能变更用户的查询条件,需要在聚合子句中添加过滤条件

GET /hotel/_search 
{ 
  "size": 0, 
 "query": {                   //指定查询的query逻辑 
    "term": { 
      "city": { 
        "value": "天津" 
      } 
    } 
  }, 
  "aggs": { 
    "my_agg": { 
     "filter": {              //指定过滤器逻辑 
        "term": { 
          "full_room": false 
        } 
      }, 
     "aggs": {                //指定聚合逻辑 
        "my_avg": { 
          "avg": { 
            "field": "price" 
          } 
        } 
      } 
    } 
  } 
} 

后过滤器

使用match匹配title中包含“假日”的酒店,并且查询出这些酒店的平均价格,最后使用post_filter设置后过滤器的条件,将酒店的城市锁定为“北京”

GET /hotel/_search 
{ 
  "size": 0,  
 "query": {                      //指定查询的query逻辑 
    "match": { 
      "title": "假日" 
    } 
  }, 
 "post_filter": {                //指定后过滤器逻辑 
    "term": { 
      "city": "北京" 
    } 
  },  
 "aggs": {                       //指定聚合逻辑 
    "my_agg": { 
      "avg": { 
        "field": "price", 
        "missing":200 
      } 
    } 
  } 
}  

8.4聚合排序

根据前面的介绍可知,ES对于聚合结果的默认排序规则有时并非是我们期望的。可以使用ES提供的sort子句进行自定义排序,有多种排序方式供用户选择:可以按照聚合后的文档计数的大小进行排序;可以按照聚合后的某个指标进行排序;还可以按照每个组的名称进行排序

按文档计数排序

按照城市的酒店平均价格进行聚合,并按照聚合后的文档计数进行升序排列的请求

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
    "group_city": { 
      "terms": { 
        "field": "city", 
        "order": {                     //按照文档计数进行升序排列 
          "_count": "asc" 
        } 
      }, 
      "aggs": { 
        "my_avg": { 
          "avg": {                    //使用价格平均值作为聚合指标 
            "field": "price", 
            "missing": 200 
          } 
        } 
      } 
    } 
  } 
}  

按聚合指标排序

按照城市的酒店平均价格进行聚合,并按照聚合后的平均价格进行升序排列

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
    "group_city": { 
      "terms": { 
        "field": "city", 
        "order": {                   //按照聚合指标进行升序排列 
          "my_avg": "asc" 
        } 
      }, 
      "aggs": { 
        "my_avg": {                  //定义聚合指标 
          "avg": { 
            "field": "price", 
            "missing": 200 
          } 
        } 
      } 
    } 
  } 
} 

按照城市的酒店平均价格进行聚合,并按照聚合后的分组名称进行升序排列

GET /hotel/_search 
{ 
  "size": 0, 
  "aggs": { 
    "group_city": { 
      "terms": { 
        "field": "city", 
        "order": {                 //按照分组key的自然顺序升序排列 
          "_key": "asc" 
        } 
      }, 
      "aggs": { 
        "my_avg": {                //定义聚合指标 
          "avg": { 
            "field": "price", 
            "missing": 200 
          } 
        } 
      } 
    } 
  } 
}  

8.5聚合分页

Top hits聚合

搜索“金都”时,如果希望按照城市分组,每组按照匹配分数降序展示3条文档数据

GET /hotel/_search 
{ 
  "size": 0, 
  "query": { 
    "match": { 
      "title": "金都" 
    } 
  },  
  "aggs": { 
   "group_city": {                //按照城市进行桶聚合 
      "terms": { 
        "field": "city" 
      }, 
      "aggs": { 
        "my_avg": { 
          "top_hits": {          //指定返回每个桶的前3个文档 
            "size": 3 
          } 
        } 
      } 
    } 
  } 
}  

Collapse聚合

当在索引中有大量数据命中时,Top hits聚合存在效率问题,并且需要用户自行排序。针对上述问题,ES推出了Collapse聚合,即用户可以在collapse子句中指定分组字段,匹配query的结果按照该字段进行分组,并在每个分组中按照得分高低展示组内的文档。当用户在query子句外指定from和size时,将作用在Collapse聚合之后,即此时的分页是作用在分组之后的

GET /hotel/_search 
{ 
 "from": 0,                      //指定分页的起始位置 
 "size": 5,                      //指定每页返回的数量 
 "query": {                      //指定查询的query逻辑 
    "match": { 
      "title": "金都" 
    } 
  },  
 "collapse": {                   //指定按照城市进行Collapse聚合 
    "field": "city" 
  } 
} 

Linux相关命令

查看各个目录下的大小

du -sh /opt/liuy/*

Linux后台启动Kibana

nohup ./bin/kibana   --allow-root &

参考文献:

     Elasticsearch搜索引擎构建入门与实战---高印会

Elasticsearch: 权威指南 | Elastichttps://www.elastic.co/guide/cn/elasticsearch/guide/current/index.htmlData in: documents and indices | Elasticsearch Guide [7.17] | Elastichttps://www.elastic.co/guide/en/elasticsearch/reference/7.17/documents-indices.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
elasticsearch 学习笔记包括以下内容: 一、Elasticsearch概述: - Elasticsearch是一种开源的分布式搜索和分析引擎,可以用于快速搜索、分析和存储大量的结构化和非结构化数据。 - Elasticsearch与Solr相比有一些区别,包括用户、开发和贡献者社区的规模和成熟度等方面。 二、Elasticsearch安装: 1. 下载Elasticsearch,可以从官方网站或华为云镜像下载。 2. 安装Elasticsearch。 三、安装head插件: - head插件是一个可视化的管理界面,可以方便地管理和监控Elasticsearch集群。 四、安装Kibana: 1. Kibana是一个开源的数据可视化工具,用于展示和分析Elasticsearch中的数据。 2. 下载Kibana并安装。 3. 启动Kibana并进行访问测试。 4. 可选的汉化操作。 五、ES核心概念理解: - 学习ES的核心概念,包括索引、文档、映射、查询等。 以上是elasticsearch学习笔记的主要内容,希望对你有帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Elasticsearch 学习笔记(上)](https://blog.csdn.net/m0_52691962/article/details/127064350)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值