• 06_es分布式搜索引擎2


    一、DSL查询文档

    1.DSL查询分类

    ①查询所有:match_all

    全文检索:利用分词器对用户输入的内容分词,倒排索引去匹配

        match_query

        multi_match_query

    ③精确查询:根据精确词条查找数据,查找的是keyword,数值,日期,boolean类型字段

        ids,range,term

    ④地理geo查询:根据经纬度查询

        geo_distance

        geo_bounding_box

    ⑤复合查询:将各种条件组合起来,合并查询条件

        bool

        function_score

     总结:查询DSL的基本语法是什么?

    GET /索引库名/_search

    { "query": { "查询类型": { "FIELD": "TEXT"}}}

    2.全文检索

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

     ①match查询:对用户输入的内容分词,然后倒排索引库查询。一个字段

    查询三钻的酒店

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "match": {
    5. "starName": "三钻"
    6. }
    7. }
    8. }

    ②multi_match:多个字段查询。参与的字段越多,查询性能越差。

     查询品牌,酒店名,商业圈有“外滩如家”

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "multi_match": {
    5. "query": "外滩如家",
    6. "fields": ["brand","name","business"]
    7. }
    8. }
    9. }

    3.精确查询

    查询的keyword,不进行分词的字段

    ①term:根据词条准确值查询

    ②range:范围查询(价格)

     ①term:

    查询品牌是“7天酒店”

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "term": {
    5. "brand": {
    6. "value": "7天酒店"
    7. }
    8. }
    9. }
    10. }

    ②range:

    查询200-250酒店

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "range": {
    5. "price": {
    6. "gte": 200,
    7. "lte": 250
    8. }
    9. }
    10. }
    11. }

    总结:精确查询常见的有哪些?

    • term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
    • range查询:根据数值范围查询,可以是数值、日期的范围

    4.地理查询

    场景:

    查询附近的酒店,附近的人,打车附近的出租车

     ①矩形范围内:geo_bounding_box

     ②以指定中心点为半径:

     查询这个点15公里范围内的酒店

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "geo_distance":{
    5. "distance":"15km",
    6. "location":"31.282444,121.479385"
    7. }
    8. }
    9. }

    5.相关性算分:竞价排名

    ①fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名

     

     ②词条频率越高,得分越高,排名越靠前

    ③elasticsearch中的相关性打分算法是什么?

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

     

    6.修改相关性算分:竞价排名

    使用 function score query,可以修改文档的相关性算分(query score),根据新得到的算分排序。

    ①原始条件查询,搜索文档并根据相关性打分(query score)

    ②过滤条件:符合条件的文档才重新算分

    ③算分函数:

    算分函数,算分函数的结果称为function score ,将来会与query score运算,得到新算分,常见的算分函数有:

    • weight:给一个常量值,作为函数结果(function score)
    • field_value_factor:用文档中的某个字段值作为函数结果
    • random_score:随机生成一个值,作为函数结果
    • script_score:自定义计算公式,公式结果作为函数结果

    ④加权模式,定义function score与query score的运算方式,包括:

    • multiply:两者相乘。默认就是这个
    • replace:用function score 替换 query score
    • 其它:sum、avg、max、min

    案例:搜索外滩的酒店,“如家”品牌给公司充钱了,让他的排名靠前一些。

    分析:

    ①文档为品牌是“如家”的

    ②算分函数是weight

    ③加权模式是求和sum

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "function_score": {
    5. "query": {
    6. "match": {
    7. "all": "外滩"
    8. }
    9. },
    10. "functions": [
    11. {
    12. "filter": {
    13. "term": {
    14. "brand": "如家"
    15. }
    16. },
    17. "weight": 2
    18. }
    19. ],
    20. "boost_mode": "sum"
    21. }
    22. }
    23. }

    7.复合查询Boolean Query

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

    must:”与”,必须匹配每个子查询

    should:“或”选择性匹配子查询

    must_not:必须不匹配,不参与算分,类似“非”

    filter:必须匹配,不算分。

    案例1:查询上海的酒店,品牌是皇冠假日或华美达。价格不低于500,评分是大于45分的

     

     案例2:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "bool": {
    5. "must": [
    6. {
    7. "match": {
    8. "name": "如家"
    9. }
    10. }
    11. ],
    12. "must_not": [
    13. {
    14. "range": {
    15. "price": {
    16. "gte": 400
    17. }
    18. }
    19. }
    20. ],
    21. "filter": [
    22. {
    23. "geo_distance": {
    24. "distance": "10km",
    25. "location": {
    26. "lat": 31.21,
    27. "lon": 121.5
    28. }
    29. }
    30. }
    31. ]
    32. }
    33. }
    34. }

    二、搜索结果处理

    1.排序

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

    排序语法

     地理坐标排序语法

     案例1:对酒店数据按照用户评价降序排序,评价相同的按照价格升序排序

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "sort": [
    7. {
    8. "score":"desc"
    9. },
    10. {
    11. "price": "asc"
    12. }
    13. ]
    14. }

    案例2:实现对酒店数据按照到你的位置坐标的距离升序排序

    获取经纬度的方式:https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/

    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "sort": [
    7. {
    8. "_geo_distance": {
    9. "location": {
    10. "lat": 31.220393,
    11. "lon": 121.544427
    12. },
    13. "order": "asc",
    14. "unit": "km"
    15. }
    16. }
    17. ]
    18. }

    2.分页

    es的搜索结果默认是top10条。

    es通过修改from,size参数控制返回的分页结果

    深度分页问题

    ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from = 990,size =10的数据:

    ①首先在每个数据分片上都排序并查询前1000条文档。

    ②然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档

    ③最后从这1000条中,选取从990开始的10条文档

    如果搜索页数过深,或者结果集(from + size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000

    总结

    from + size:

    • 优点:支持随机翻页
    • 缺点:深度分页问题,默认查询上限(from + size)是10000
    • 场景:百度、京东、谷歌、淘宝这样的随机翻页搜索

    after search:

    • 优点:没有查询上限(单次查询的size不超过10000)
    • 缺点:只能向后逐页查询,不支持随机翻页
    • 场景:没有随机翻页需求的搜索,例如手机向下滚动翻页

    3.高亮

    搜索关键字突出显示。

    原理:

    ①搜索关键字标记出来

    ②页面加css样式

    案例:如家酒店高亮

     

    三、RestClient查询文档

    1.快速入门

    ①请求DSL的组织

     RestAPI中其中构建DSL是通过HighLevelRestClient中的resource()来实现的,其中包含了查询、排序、分页、高亮等所有功能

     RestAPI中其中构建查询条件的核心部分是由一个名为QueryBuilders的工具类提供的,其中包含了各种查询方法

     ②解析结果response

    ③查询全部酒店的完整代码

    1. @Test
    2. void testMatchAll() throws IOException {
    3. // 1.准备查询请求,参数是索引库名
    4. SearchRequest request = new SearchRequest("hotel");
    5. // 2.组织DSL参数
    6. request.source().query(QueryBuilders.matchAllQuery());
    7. // 3.发送请求,得到响应
    8. SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    9. // 4.解析结果
    10. SearchHits searchHits = response.getHits();
    11. // 4.1 获取查询的条数
    12. long total = searchHits.getTotalHits().value;
    13. // 4.2 获取查询的集合
    14. SearchHit[] hits = searchHits.getHits();
    15. // 4.3 遍历
    16. ListhotelDocList = new ArrayList<>();
    17. for (SearchHit hit : hits) {
    18. // 转换为Json
    19. String json = hit.getSourceAsString();
    20. // 转换为java对象
    21. HotelDoc hotelDoc = JSONObject.parseObject(json, HotelDoc.class);
    22. // 保存在集合中
    23. hotelDocList.add(hotelDoc);
    24. }
    25. System.out.println(hotelDocList);
    26. }

    查询的基本步骤是:

    • 创建SearchRequest对象
    • 准备Request.source(),也就是DSL。
      • QueryBuilders来构建查询条件
      • 传入Request.source() 的 query() 方法
    • 发送请求,得到结果
    • 解析结果(参考JSON结果,从外到内,逐层解析)

    2.构建查询条件,只要记住一个类:QueryBuilders

    ①全文检索查询(分词,模糊查询)

    单字段:QueryBuilders.matchQuery(字段名,值)

    多字段:QueryBuilders.multiMatchQuery(值, 字段1,字段2);

    演示:酒店名字带有“如家“的有哪些?

    request.source().query(QueryBuilders.termQuery("name","如家"));

    ②精确查询,不分词

    精确查询常见的有term查询和range查询

    ③复合查询boolean query

    查询品牌为如家,价格在200元内的酒店

    1. // 创建bool查询
    2. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    3. // 添加must条件
    4. boolQuery.must(QueryBuilders.termQuery("brand","如家"));
    5. // 添加filter条件
    6. boolQuery.filter(QueryBuilders.rangeQuery("price").lte(200));
    7. request.source().query(boolQuery);

    3.分页和排序

     演示:查询名为“如家“的酒店,查询结果进行价格降序,每页显示3条

    1. // 页码
    2. int page = 1,size=3;
    3. // 2.组织DSL
    4. // 2.1 查询
    5. request.source().query(QueryBuilders.termQuery("name","如家"));
    6. // 2.2 分页 从from序号数size个
    7. request.source().from((page-1)*size).size(size);
    8. // 2.3 价格排序
    9. request.source().sort("price", SortOrder.DESC);

    4.高亮

    根据name搜索高亮

     代码

    1. @Test
    2. void testHight() throws IOException{
    3. // 1.请求request
    4. SearchRequest request = new SearchRequest("hotel");
    5. // 2. 组织DSL
    6. request.source().query(QueryBuilders.matchQuery("all","如家"));
    7. request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
    8. // 3.发送请求,得到响应
    9. SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    10. // 4.分析结果
    11. SearchHits searchHits = response.getHits();
    12. // 5.解析
    13. SearchHit[] hitss = searchHits.getHits();
    14. // 6.遍历
    15. for (SearchHit hit : hitss) {
    16. // 转换为json
    17. String json = hit.getSourceAsString();
    18. // 得到对象
    19. HotelDoc hotelDoc = JSONObject.parseObject(json, HotelDoc.class);
    20. // 获取高亮结果
    21. Map highlightFields = hit.getHighlightFields();
    22. // 根据字段获取
    23. HighlightField highlightField = highlightFields.get("name");
    24. // 获取高亮值
    25. String name = highlightField.getFragments()[0].string();
    26. // 覆盖结果
    27. hotelDoc.setName(name);
    28. System.out.println(name);
    29. }
    30. }

    四、黑马旅游案例

    1. @Override
    2. public PageResult search(RequestParams params) throws IOException {
    3. // 1.得到请求参数
    4. String key = params.getKey();
    5. Integer page = params.getPage();
    6. Integer size = params.getSize();
    7. String sortBy = params.getSortBy();
    8. String brand = params.getBrand();
    9. String starName = params.getStarName();
    10. String city = params.getCity();
    11. Integer minPrice = params.getMinPrice();
    12. Integer maxPrice = params.getMaxPrice();
    13. String location = params.getLocation();
    14. // 2.创建搜索请求
    15. SearchRequest request = new SearchRequest(HotelConstants.HOTEL_INDEX);
    16. // 3.编写DSL 组合查询boolQuery
    17. BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    18. // 3.1.1 输入框关键字must
    19. if (key != null && !"".equals(key)) { //输入框不为空,模糊查询
    20. boolQuery.must(QueryBuilders.matchQuery("all", key));
    21. } else {//输入框为空,查询全部
    22. boolQuery.must(QueryBuilders.matchAllQuery());
    23. }
    24. // 3.1.2 城市--filter
    25. if (city != null && !"".equals(city)) {
    26. boolQuery.filter(QueryBuilders.termQuery("city", city));
    27. }
    28. // 3.1.3 品牌--filter
    29. if (brand != null && !"".equals(brand)) {
    30. boolQuery.filter(QueryBuilders.termQuery("brand", brand));
    31. }
    32. // 3.1.4 星级--filter
    33. if (starName != null && !"".equals(starName)) {
    34. boolQuery.filter(QueryBuilders.termQuery("starName", starName));
    35. }
    36. // 3.1.5 价格--filter
    37. if (minPrice != null && maxPrice != null) {
    38. boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice));
    39. }
    40. // 3.1.6 查询条件
    41. // 3.1.7 =====广告推荐算分查询(查询条件,算分条件)
    42. FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(boolQuery,
    43. new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
    44. new FunctionScoreQueryBuilder.FilterFunctionBuilder(
    45. QueryBuilders.termQuery("isAD",true),// 算分条件
    46. ScoreFunctionBuilders.weightFactorFunction(10) // 算分比例
    47. )
    48. });
    49. request.source().query(functionScoreQuery);
    50. // 3.2 页码
    51. request.source().from((page - 1) * size).size(size);
    52. // 3.3 排序
    53. if (location != null && !"".equals(location)) {
    54. request.source().sort(SortBuilders.geoDistanceSort("location", new GeoPoint(location))
    55. .order(SortOrder.ASC)
    56. .unit(DistanceUnit.KILOMETERS));
    57. }
    58. if (!SortConstants.DEFAULT.equals(sortBy)) {
    59. request.source().sort(sortBy, SortOrder.DESC);
    60. }
    61. //4. 发送请求得到响应
    62. SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    63. // 5.解析响应
    64. PageResult pageResult = handleResponse(response);
    65. return pageResult;
    66. }

  • 相关阅读:
    WPF控件4
    mmsegmentation 训练自制数据集
    Vue基础理论篇
    ubuntu16.04安装低版本cmake(安装cmake安装)
    js中Promise的常见用法 比如resolve、race都是啥意思
    「SpringCloud」01 Eureka服务注册与发现
    使用Mybatis-plus清空表数据
    【python】网络爬虫与信息提取--scrapy爬虫框架介绍
    基于matlab求两个数最大公约数函数gcd
    微服务必须要有的组件和必须要考虑的问题
  • 原文地址:https://blog.csdn.net/jbkjhji/article/details/134185188