ElasticSearch入门到掌握,用心看完这三篇就够了【完结2】

「章节总览」
 
      【ElasticSearch 第一篇 https://blog.csdn.net/weixin_45404884/article/details/137402463】
      【ElasticSearch 第二篇 https://blog.csdn.net/weixin_45404884/article/details/137505489】
      【ElasticSearch 第三篇 https://blog.csdn.net/weixin_45404884/article/details/137548120】

二、ElasticSearch详解

1.DSL 查询文档

(1)DSL 查询语法分类

Elasticsearch 提供了基于 JSON 的 DSL ( Domain Specific Language)来定义查询。常见的查询类型包括:

  • 查询所有:查询出所有数据,一般测试用。例如: match_all
  • 全文检索( full text )查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
    • match_query
    • multi_match_query
  • 精确查询:根据精确词条值查找数据,一般是查找 keyword 、数值、日期、 boolean 等类型字段。例如:
    • ids
    • range
    • term
  • 地理( geo )查询:根据经纬度查询。例如:
    • geo_distance
    • geo_bounding_box
  • 复合( compound )查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
    • bool
    • function_score

(2)DSL Query 基本语法

GET /indexName/_search
{
  "query": {
    " 查询类型 ": {
      " 查询条件 ": " 条件值 "
    }
  }
}

(3)查询所有

GET /hotel/_search
{
  "query": {
    "match_all": {}
  }
}

(4)全文检索查询

全文检索查询,会对用户输入内容分词,常用于搜索框搜索:

  • match查询,根据一个字段查询。
GET /hotel/_search
{
  "query": {
    "match": {
      "city": "杭州"
    }
  }
}
  • multi_match查询,根据多个字段查询,参与查询字段越多,查询性能越差。
GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "四",
      "fields": [
        "star_name",
        "name"
      ]
    }
  }
}

搜索结果:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 1.2039728,
    "hits": [
      {
        "_index": "hotel",
        "_id": "2",
        "_score": 1.2039728,
        "_source": {
          "name": "李四",
          "email": "lisi@itcast.cn",
          "price": 43,
          "score": 56,
          "brand": "如家",
          "city": "北京",
          "star_name": "三星",
          "business": "天安门",
          "location": "23.4234234,34.32131",
          "pic": "/1/2/3"
        }
      },
      {
        "_index": "hotel",
        "_id": "3",
        "_score": 0.6931471,
        "_source": {
          "name": "王五",
          "email": "wangwu@itcast.cn",
          "price": 78,
          "score": 43,
          "brand": "贝壳",
          "city": "杭州",
          "star_name": "四星",
          "business": "西湖",
          "location": "23.4234234,34.32131",
          "pic": "/1/2/3"
        }
      },
      {
        "_index": "hotel",
        "_id": "4",
        "_score": 0.6931471,
        "_source": {
          "name": "赵六",
          "email": "zhaoliu@itcast.cn",
          "price": 78,
          "score": 43,
          "brand": "贝壳",
          "city": "杭州",
          "star_name": "四星",
          "business": "西湖",
          "location": "23.4234234,34.32131",
          "pic": "/1/2/3"
        }
      }
    ]
  }
}

(5)精确查询

精确查询一般是查找 keyword 、数值、日期、 boolean 等类型字段。所以不会对搜索条件分词。常见的有:

  • term :根据词条精确值查询,根据词条精确匹配,一般搜索 keyword 类型、数值类型、布尔类型、日期类型字段。
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "上海"
      }
    }
  }
}
  • range :根据值的范围查询
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 70,
        "lte": 80
      }
    }
  }
}

(6)地理查询

根据经纬度查询。常见的使用场景包括:

  • 携程:搜索我附近的酒店
  • 滴滴:搜索我附近的出租车
  • 微信:搜索我附近的人

例如:

  • geo_bounding_box :查询 geo_point 值落在某个矩形范围的所有文档

请添加图片描述

GET /hotel/_search
{
  "query": {
    "geo_bounding_box": {
      "location": {
        "top_left": {
          "lat": 24,
          "lon": 35
        },
        "bottom_right": {
          "lat": 22,
          "lon": 33
        }
      }
    }
  }
}
  • geo_distance :查询到指定中心点小于某个距离值的所有文档
    请添加图片描述
GET /hotel/_search
{
  "query": {
    "geo_distance": {
      "distance": "500km",
      "location": "22,33"
    }
  }
}

(7)复合查询

相关性算分

当我们利用 match 查询时,文档结果会根据与搜索词条的关联度打分( _score ),返回结果时按照分值降序排列。

 - F-IDF :在 elasticsearch5.0 之前,会随着词频增加而越来越大。
 - BM25 :在 elasticsearch5.0 之后,会随着词频增加而增大,但增长曲线会趋于水平。

TF算法:
T F ( 词条频率 ) = 词条出现次数 文档中词条总数 TF(词条频率) = \dfrac{词条出现次数}{文档中词条总数} {} TF(词条频率)=文档中词条总数词条出现次数

TF- IDF算法:
I D F ( 逆文档频率 ) = log ⁡ ( 文档总数 包含词条的文档总数 ) IDF(逆文档频率) =\log( \dfrac{文档总数}{包含词条的文档总数} {}) IDF(逆文档频率)=log(包含词条的文档总数文档总数)
s c o r e = ∑ i n T F ( 词条频率 ) ∗ I D F ( 逆文档频率 ) score =\sum \limits_{i}^nTF(词条频率) * IDF(逆文档频率) score=inTF(词条频率)IDF(逆文档频率)

BM25算法
s c o r e ( Q , d ) = ∑ i n log ⁡ ( 1 + N − n + 0.5 n + 0.5 ∗ f i f i + k 1 ∗ ( 1 − b + b ∗ d l a v g d l ) ) score(Q,d) =\sum \limits_{i}^n \log(1+ \dfrac{N-n+0.5}{n+0.5} * \dfrac{f_i}{f_i+k_1 * (1-b+b*\dfrac{dl}{avgdl})}) score(Q,d)=inlog(1+n+0.5Nn+0.5fi+k1(1b+bavgdldl)fi)

fuction score

算分函数查询,可以控制文档相关性算分,控制文档排名。
- 过滤条件:哪些文档要加分
- 算分函数:如何计算 function score
- 加权方式: function score 与 query score 如何运算

GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "all": " 外滩 "
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "id": "1"
            }
          },
          "weight": 10
        }
      ],
      "boost_mode": "multiply"
    }
  }
}

请添加图片描述

bool 查询

布尔查询是一个或多个查询子句的组合。子查询的组合方式有:

  • must :必须匹配每个子查询,类似“与”
  • should :选择性匹配子查询,类似“或”
  • must_not :必须不匹配,不参与算分,类似“非”
  • filter :必须匹配,不参与算分
##搜索品牌包含“如家”,价格不高于400 ,在坐标 23.21,33.5 周围 1000km范围内的酒店。
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "brand": "如家"
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "price": {
              "gt": 400
            }
          }
        }
      ],
      "filter": [
        {
          "geo_distance": {
            "distance": "1000km",
            "location": {
              "lat": 23.21,
              "lon": 33.5
            }
          }
        }
      ]
    }
  }
}

2. 搜索结果处理

(1)排序

elasticsearch 支持对搜索结果排序,默认是根据相关度算分( _score )来排序。可以排序字段类型有: keyword类型、数值类型、地理坐标类型、日期类型等。

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": "desc" // 排序字段和排序方式 ASC 、 DESC
    }
    ]
}

(2)分页

elasticsearch 默认情况下只返回 top10 的数据。而如果要查询更多数据就需要修改分页参数了。
elasticsearch 中通过修改 from 、 size 参数来控制要返回的分页结果:

GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0, // 分页开始的位置,默认为0
  "size": 10, // 期望获取的文档总数
  "sort": [
    {"price": "asc"}
    ]
}

(3)深度分页问题

ES 是分布式的,所以会面临深度分页问题。例如按 price 排序后,获取 from = 990 , size =10 的数据:
聚合所有结果,重新排序选取前 1000 个。

  1. 首先在每个数据分片上都排序并查询前1000 条文档。
  2. 然后将所有节点的结果聚合,在内存中重新排序选出前 1000 条文档。
  3. 最后从这 1000 条中,选取从 990 开始的10 条文档。
    如果搜索页数过深,或者结果集( from +size )越大,对内存和 CPU 的消耗也越高。因此 ES 设定结果集查询的上限是 10000。
    针对深度分页, ES 提供了两种解决方案,官方文档:
    • search after :分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。
    • scroll :原理将排序数据形成快照,保存在内存。官方已经不推荐使用。

(4)高亮

高亮:就是在搜索结果中把搜索关键字突出显示。
原理是这样的:

  • 将搜索结果中的关键字用标签标记出来
  • 在页面中给标签添加 css 样式
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "上海"
      }
    }
  },
  "highlight": {
    "fields": {
      "city": {
        "pre_tags": "<em>",
        "post_tags": "<em>"
      }
    }
  }
}

返回结果以高亮形式返回:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1.6486585,
    "hits": [
      {
        "_index": "hotel",
        "_id": "1",
        "_score": 1.6486585,
        "_source": {
          "name": "张三",
          "email": "zy@itcast.cn",
          "price": 12,
          "score": 12,
          "brand": "汉庭",
          "city": "上海",
          "star_name": "五星",
          "business": "虹桥",
          "location": "23.423424, 34.32131",
          "pic": "/1/2/3"
        },
        "highlight": {
          "city": [
            "<em>上海<em>"
          ]
        }
      }
    ]
  }
}

3.RestClient 查询文档

(1)查询所有

@Test
    public void testMatchAll() throws IOException {
        RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://localhost:9200")
        ));
        try {
            // 1.准备 Request
            SearchRequest request = new SearchRequest("hotel");
            // 2.组织 DSL参数
            request.source().query(QueryBuilders.matchAllQuery());
            // 3.发送请求,得到响应结果
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析响应结果
            SearchHits searchHits = response.getHits();
            // 4.1.查询的总条数
            long total = searchHits.getTotalHits().value;
            // 4.2.查询的结果数组
            SearchHit[] hits = searchHits.getHits();
            for (SearchHit hit : hits) {
                // 4.3.得到 source
                String json = hit.getSourceAsString();
                System.out.println(json);
            }
        }finally {
            client.close();
        }

    }

(2)全文检索查询

全文检索的 match 和 multi_match 查询与 match_all 的 API 基本一致。差别是查询条件,也就是 query 的部分。
同样是利用 QueryBuilders 提供的方法:

//单字段查询
QueryBuilders.matchQuery("all", " 如家 ");
//多字段查询
QueryBuilders.multiMatchQuery(" 如家 ", "name", "business");

(3)精确查询

精确查询常见的有 term 查询和 range 查询,同样利用 QueryBuilders 实现:

//词条查询
QueryBuilders.termQuery("city", " 杭州 ");
//范围查询
QueryBuilders.rangeQuery("price").gte(100).lte(150);

(4)复合查询

精确查询常见的有 term 查询和 range 查询,同样利用 QueryBuilders 实现:

//创建布尔查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//添加 must条件
boolQuery.must(QueryBuilders.termQuery("city", " 杭州 "));
//添加 filter条件
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));

(5)排序和分页

//查询
request.source().query(QueryBuilders.matchAllQuery());
//分页
request.source().from(0).size(5);
//价格排序
request.source().sort("price", SortOrder.ASC);

(6)高亮

请求request

request.source().highlighter(new HighlightBuilder().field("name")
//是否需要与查询字段匹配
.requireFieldMatch(false));

高亮处理

//获取 source
HotelDoc hotelDoc = JSON.parseObject(hit.getSourceAsString(),
HotelDoc.class);
//处理高亮
Map<String, HighlightField> highlightFields =
hit.getHighlightFields();
if (!CollectionUtils.isEmpty(highlightFields)) {
//获取高亮字段结果
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
//取出高亮结果数组中的第一个,就是酒店名称
String name = highlightField.getFragments()[0].string();
hotelDoc.setName(name);
}
}

下一篇
https://blog.csdn.net/weixin_45404884/article/details/137548120

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值