DSL查询文档
-
查询所有:查询出所有数据,一般测试用。例如:match_all
-
全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
-
match_query
-
multi_match_query
-
-
精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
-
ids
-
range
-
term
-
-
地理(geo)查询:根据经纬度查询。例如:
-
geo_distance
-
geo_bounding_box
-
-
复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
-
bool
-
function_score
-
查询的语法基本一致:
// 查询所有
GET /indexName/_search
{
"query": {
"match_all": {
}
}
}
全文检索查询
全文检索查询的基本流程如下:
-
对用户搜索的内容做分词,得到词条
-
根据词条去倒排索引库中匹配,得到文档id
-
根据文档id找到文档,返回给用户
match查询语法如下:
GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
mulit_match语法如下:
GET /indexName/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"]
}
}
}
精准查询
精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:
-
term:根据词条精确值查询
-
range:根据值的范围查询
// term查询
GET /indexName/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
// range查询
GET /indexName/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10, // 这里的gte代表大于等于,gt则代表大于
"lte": 20 // lte代表小于等于,lt则代表小于
}
}
}
}
地理坐标查询
// geo_bounding_box查询
GET /indexName/_search
{
"query": {
"geo_bounding_box": {
"FIELD": {
"top_left": { // 左上点
"lat": 31.1,
"lon": 121.5
},
"bottom_right": { // 右下点
"lat": 30.9,
"lon": 121.7
}
}
}
}
}
附近查询
// geo_distance 查询
GET /indexName/_search
{
"query": {
"geo_distance": {
"distance": "15km", // 半径
"FIELD": "31.21,121.5" // 圆心
}
}
}
复合查询
复合(compound)查询:复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑。常见的有两种:
-
fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名
-
bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索
GET /hotel/_search
{
"query": {
"function_score": {
"query": { .... }, // 原始查询,可以是任意条件
"functions": [ // 算分函数
{
"filter": { // 满足的条件,品牌必须是如家
"term": {
"brand": "如家"
}
},
"weight": 2 // 算分权重为2
}
],
"boost_mode": "sum" // 加权模式,求和
}
}
}
布尔查询
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:
-
must:必须匹配每个子查询,类似“与”
-
should:选择性匹配子查询,类似“或”
-
must_not:必须不匹配,不参与算分,类似“非”
-
filter:必须匹配,不参与算分
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{"term": {"city": "上海" }}
],
"should": [
{"term": {"brand": "皇冠假日" }},
{"term": {"brand": "华美达" }}
],
"must_not": [
{ "range": { "price": { "lte": 500 } }}
],
"filter": [
{ "range": {"score": { "gte": 45 } }}
]
}
}
}
搜索结果处理
排序
elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": "desc" // 排序字段、排序方式ASC、DESC
}
]
}
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance" : {
"FIELD" : "纬度,经度", // 文档中geo_point类型的字段名、目标坐标点
"order" : "asc", // 排序方式
"unit" : "km" // 排序的距离单位
}
}
]
}
分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:
-
from:从第几个文档开始
-
size:总共查询几个文档
GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}
深度分页问题
当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力,因此elasticsearch会禁止from+ size 超过10000的请求。
GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 9900, // 分页开始的位置,默认为0
"size": 200, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}
针对深度分页,官方文档:
-
search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。
实例:
GET /hotel/_search
{
"query": {
"match_all": {}
},
"size": 200,
"sort": [
{
"id": {
"order": "desc"
}
}
],
"search_after": [
"657192"
]
}
高亮
高亮的语法:
GET /hotel/_search
{
"query": {
"match": {
"FIELD": "TEXT" // 查询条件,高亮一定要使用全文检索查询
}
},
"highlight": {
"fields": { // 指定要高亮的字段
"FIELD": {
"pre_tags": "<em>", // 用来标记高亮字段的前置标签
"post_tags": "</em>" // 用来标记高亮字段的后置标签
}
}
}
}
RestClient查询文档
1.创建bean对象
@Configuration
public class ClientConfig {
@Bean
public RestHighLevelClient getClient() {
return new RestHighLevelClient(RestClient.builder(HttpHost.create("http://106.14.156.194:9200")));
}
}
2.具体步骤
@Autowired
private RestHighLevelClient client;
@Test
public void matchAllTest() throws IOException {
//1.创建查询请求对象
SearchRequest request = new SearchRequest("hotel");
//2.准备请求参数
request.source().query(QueryBuilders.matchAllQuery());
//3.发送请求参数
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//4.根据响应结果解析响应
ArrayList<HotelDoc> hotelDocs = parseObject(response);
for (HotelDoc hotelDoc : hotelDocs) {
System.out.println(hotelDoc);
}
// SearchHits outHits = response.getHits();
// TotalHits totalHits = outHits.getTotalHits();
// long value = totalHits.value;//条数
// System.out.println("总条数为: "+value);
// SearchHit[] innerHits = outHits.getHits(); # 由于响应结果后面需要重复使用多次,这里抽取成方法
// for (SearchHit hit : innerHits) {
// String sourceAsString = hit.getSourceAsString();
// HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
// System.out.println(hotelDoc);
// }
}
抽取方法
/**
* 解析JSON字符串为HotelDoc对象集合
* @param response
* @return
*/
public ArrayList<HotelDoc> parseObject( SearchResponse response) {
SearchHits outHits = response.getHits();
TotalHits totalHits = outHits.getTotalHits();
long value = totalHits.value;//条数
System.out.println("总条数为: "+value);
SearchHit[] innerHits = outHits.getHits();
ArrayList<HotelDoc> hotelDocArrayList = new ArrayList<>();
for (SearchHit hit : innerHits) {
String sourceAsString = hit.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
hotelDocArrayList.add(hotelDoc);
}
return hotelDocArrayList;
}
具体查询对应方法