ElasticSearch学习笔记(三)RestClient操作文档、DSL查询文档、搜索结果排序

news2024/9/20 16:42:59

文章目录

  • 前言
  • 5 RestClient操作文档
    • 5.4 删除文档
    • 5.4 修改文档
    • 5.5 批量导入文档
  • 6 DSL查询文档
    • 6.1 准备工作
    • 6.2 全文检索查询
    • 6.3 精准查询
    • 6.4 地理坐标查询
    • 6.5 复合查询
      • 6.5.1 相关性算分
      • 6.5.2 布尔查询
  • 7 搜索结果处理
    • 7.1 排序
      • 7.1.1 普通字段排序
      • 7.1.2 地理坐标排序

前言

ElasticSearch学习笔记(一)倒排索引、ES和Kibana安装、索引操作
ElasticSearch学习笔记(二)文档操作、RestHighLevelClient的使用

5 RestClient操作文档

5.4 删除文档

删除文档的DSL语句示例如下:

DELETE /hotel/_doc/2

对应的Java代码如下:

@Test
public void testDeleteHotelDoc() throws IOException {
    // 1.创建Request对象
    DeleteRequest request = new DeleteRequest("hotel", "2");
    // 2.发送请求
    client.delete(request, RequestOptions.DEFAULT);
}

执行以上单元测试,在DevTools工具中查询该文档:

5.4 修改文档

上一节已经了解到,修改文档有全量修改增量修改两种方式。

在RestClient的API中,全量修改与新增文档的API完全一致,判断依据是文档ID是否已存在:

  • 新增时,如果ID已存在,则修改;
  • 如果ID不存在,则新增。

而增量修改是修改文档中指定的字段值,其DSL语句示例如下:

POST /hotel/_update/2
{
    "doc": {
        "address": "中山大道"
    }
}

对应的Java代码如下:

@Test
public void testUpdateHotelDoc() throws IOException {
    // 1.创建Request对象
    UpdateRequest request = new UpdateRequest("hotel", "2");
    // 2.设置要修改的字段
    request.doc("address", "中山大道");
    // 3.发送请求
    client.update(request, RequestOptions.DEFAULT);
}

执行以上单元测试,在DevTools工具中查询该文档:

5.5 批量导入文档

RestClient提供了一个BulkRequest类用于批量导入文档,其本质就是将多个普通的CRUD请求组合在一起发送,主要使用其add()方法:

可见,能添加的请求包括IndexRequest(新增)、UpdateRequest(修改)、DeleteRequest(删除)等。

对应的Java代码如下:

@Test
public void testBulkHotelDoc() throws IOException {
    // 1.创建Request对象
    BulkRequest request = new BulkRequest();
    // 2.准备参数
    List<Hotel> hotelList = hotelService.list();
    for (Hotel hotel : hotelList) {
        HotelDoc hotelDoc = new HotelDoc(hotel);
        request.add(new IndexRequest("hotel")
                .id(hotelDoc.getId().toString())
                .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
    }
    // 3.发送请求
    client.bulk(request, RequestOptions.DEFAULT);
}

执行以上单元测试,在DevTools工具中查询该文档:

6 DSL查询文档

6.1 准备工作

首先,向tb_hotel表插入大量数据,例如下图显示已经查了201条数据:

然后执行以下单元测试testSaveBatchHotelDoc()方法,将数据库的数据存储到ES中:

@Test
public void testSaveBatchHotelDoc() throws IOException {
    // 1.查询酒店数据,遍历处理
    List<Hotel> hotelList = hotelService.list();
    for (Hotel hotel : hotelList) {
        // 2.转换为文档类型
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 3.将HotelDoc转json
        String json = JSON.toJSONString(hotelDoc);
        // 4.准备Request对象
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
        // 5.准备Json文档
        request.source(json, XContentType.JSON);
        // 6.发送请求
        client.index(request, RequestOptions.DEFAULT);
    }
}

执行完毕后,可以在Kibana中看到此时hotel索引中有201个文档:

6.2 全文检索查询

ES的查询是基于JSON的DSL查询。而全文检索查询就是利用分词器对用户输入的内容进行分词,然后去倒排索引库中匹配,例如电商系统的商品搜索、百度关键词搜索等。

其基本流程如下:

  • 1)对用户输入的内容进行分词,得到词条;
  • 2)根据词条去倒排索引中匹配,得到文档id;
  • 3)根据文档id找到文档,返回给用户。

常见的全文检索查询包括:

  • match:单字段查询
  • multi_match:多字段查询,任意一个字段符合条件就算符合查询条件

其DSL语句示例如下:

# match查询
GET /hotel/_search
{
    "query": {
        "match": {
            "all": "外滩如家"
        }
    }
}

# multi_match查询
GET /hotel/_search
{
    "query": {
        "multi_match": {
            "query": "外滩如家",
            "fields": ["brand", "name", "business"]
        }
    }
}

例如:

可以看到,两种查询结果是一样的,这是因为在索引hotelbrandnamebusiness三个字段利用copy_to复制到了all字段中。因此根据这三个字段搜索,和根据all字段搜索效果是一样的。

因此,使用multi_match查询时,搜索字段越多,对查询性能影响越大,建议采用copy_to将多个字段复制到一个字段中,然后单字段查询的方式。

6.3 精准查询

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

常见的精准查询类型有:

  • term:根据词条精确值查询
  • range:根据值的范围查询

其DSL语句示例如下:

# term查询
GET /hotel/_search
{
    "query": {
        "term": {
            "city": {               //要查询的字段
                "value": "上海"     //字段值
            }
        }
    }
}

# range查询
GET /{索引名}/_search
{
    "query": {
        "range": {
            "price": {              //要查询的字段
                "gte": 1000,        // >=1000
                "lte": 3000         // <=3000
            }
        }
    }
}

例如:

term查询时,当搜索的内容不是词条,而是多个词语形成的短语时,反而搜索不到:

range查询例子:

6.4 地理坐标查询

地理坐标查询,就是根据经纬度查询,常见的场景有滴滴搜索附近的出租车、微信搜索附近的人等。

常见的地理坐标查询类型有:

  • geo_bounding_box:矩阵范围查询,即查询坐标落在某个矩形范围的所有文档。查询时,需要指定矩形的左上右下两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。
  • geo_distance:附近查询,也叫距离查询,查询到指定中心点小于某个距离值的所有文档。也就是在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件。

其DSL语句示例如下:

# geo_bounding_box查询
GET /hotel/_search
{
    "query": {
        "geo_bounding_box": {
            "location": {                   //要查询的字段
                "top_left": {               //左上点
                    "lat": "32.00",         //左上点纬度
                    "lon": "120.00"         //左上点经度
                },
                "bottom_right": {           //右下点
                    "lat": "30.00",         //右下点纬度
                    "lon": "122.00"         //右下点经度
                }
            }
        }
    }
}

# geo_distance查询
GET /hotel/_search
{
    "query": {
        "geo_distance": {
            "distance": "10km",             //半径
            "location": "30.00,122.00"      //要查询的字段:圆心坐标
        }
    }
}

例如:

6.5 复合查询

复合查询,就是将其它简单查询组合起来,实现更复杂的搜索逻辑。

常见的符合查询有两种:

  • fuction_score:算分函数查询,可以控制文档相关性算分,控制文档排名
  • bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索

6.5.1 相关性算分

在前面的match查询时,查询结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。

根据相关度打分再进行排序是比较合理的,但并不一定是项目需要的。

如果想人为控制相关性算分,就需要利用ES中的function score查询。

其基本语法如下:

  • query:原始查询条件,基于这个条件搜索文档并给文档打分,这个分数叫原始算分(query score)
  • filter:过滤条件,符合该条件的文档才会重新算分
  • 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数:
    • weight:函数结果是常量
    • field_value_factor:以文档中的某个字段值作为函数结果
    • random_score:以随机数作为函数结果
    • script_score:自定义算分函数算法
  • boost_mode:运算模式,即原始算分和函数算分两者之间的运算方式,包括:
    • multiply:相乘
    • replace:用function score替换query score
    • sum、avg、max、min等

function score查询的基本流程如下:

  • 1)根据原始条件搜索文档,并且计算原始算分(query score);
  • 2)根据过滤条件,过滤文档;
  • 3)符合过滤条件的文档,基于算分函数运算,得到函数算分(function score);
  • 4)将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。

例如有如下需求:让上海的“如家”这个品牌的酒店排名靠前一些。该需求转换为function score查询的要点如下:

  • 原始条件:city = “上海”
  • 过滤条件:brand = “如家”
  • 算分函数:直接给固定的算分结果,weight
  • 运算模式:求和

最终的DSL语句如下:

# function score查询
GET /hotel/_search
{
    "query": {
        "function_score": {
            "query": {
                "match": {
                    "city": "上海"
                }
            },
            "functions": [
                {
                    "filter": {
                        "term": {
                            "brand" : "如家"
                        }
                    },
                    "weight": 2 //算分权重为2
                }
            ],
            "boost_mode": "sum" //加权模式,求和
        }
    }
}

原始查询的结果如下:

使用function score查询的结果如下:

6.5.2 布尔查询

布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。

子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分

比如在搜索酒店时,除了根据关键字搜索外,还可能根据品牌、价格、城市等字段进行过滤。由于这些字段的查询方式各不相同,因此就需要用到布尔查询。

需要注意的是,进行布尔搜索时,参与打分的字段越多,查询的性能也越差。因此布尔查询一般建议:

  • 搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分;
  • 其它过滤条件,采用filter查询,不参与算分。

例如有如下需求:搜索名字包含“如家”,价格不高于400,在坐标(31.21,121.5)周围10km范围内的酒店。该需求转换为布尔查询的要点如下:

  • 名称搜索,属于全文检索查询,应该参与算分,放到must中;
  • 价格不高于400,用range查询,属于过滤条件,不参与算分,放到must_not中;
  • 周围10km范围内,用geo_distance查询,属于过滤条件,不参与算分,放到filter中。

最终的DSL语句如下:

# 布尔查询
GET /hotel/_search
{
    "query": {
        "bool": {
            "must": {
                "match": {
                    "name": "如家"
                }
            },
            "must_not": {
                "range": {
                    "price": {
                        "gt": 400
                    }
                }
            },
            "filter": {
                "geo_distance": {
                    "distance": "10km",
                    "location": {
                        "lat": 31.21,
                        "lon": 121.5
                    }
                }
            }
        }
    }
}

7 搜索结果处理

7.1 排序

ES默认是根据相关度算分(_score)来倒序排序的,但是也支持自定义方式对搜索结果进行排序。

支持排序的字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

7.1.1 普通字段排序

keyword、数值、日期类型排序的语法基本一致。

例如有以下需求:各酒店按照用户评分(score)降序排序,评分相同再按照价格(price)升序排序。其DSL语句如下:

# 普通字段排序
GET /hotel/_search
{
    "query": {
        "match_all": {              //查询条件,这里指匹配全部
        }
    },
    "sort": [
        {
            "score": "desc"         //按照用户评分(score)降序排序
        },
        {
            "price": "asc"          //按照价格(price)升序排序
        }
    ]
}

可见,排序条件是一个数组,也就是说可以同时写多个排序条件,此时会按照声明的顺序进行排序,当第1个排序条件相等时,则再按照第2个排序条件进行排序,以此类推。

7.1.2 地理坐标排序

地理坐标排序需要指定一个坐标坐标作为目标点,然后计算文档中指定字段(必须是geo_point类型)的坐标到目标点的距离,根据距离进行排序。

例如,有以下需求:实现对酒店按照到当前位置坐标的距离升序排序。其DSL语句如下:

# 地理坐标排序
GET /hotel/_search
{
    "query": {
        "match_all": {
        }
    },
    "sort": [
        {
            "_geo_distance": {
                "location": {               //指定的geo_point类型的字段
                    "lat": 31.034661,       //当前位置纬度
                    "lon": 121.612282       //当前位置经度
                },
                "order": "asc",             //asc、desc
                "unit": "km"                //距离单位
            }
        }
    ]
}

本节完,更多内容请查阅分类专栏:微服务学习笔记

感兴趣的读者还可以查阅我的另外几个专栏:

  • SpringBoot源码解读与原理分析
  • MyBatis3源码深度解析
  • Redis从入门到精通
  • MyBatisPlus详解
  • SpringCloud学习笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2103801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qmt量化交易策略小白学习笔记第59期【qmt编程之期权数据--获取指定期权品种的详细信息--原生Python】

qmt编程之获取期权数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 基于BS模型计算欧式期权理论价格 基于Black-Scholes-Merton模型&#xff0c;输入期权标的价格、期权行权价、无风险利率…

Mac 安装Hadoop教程(HomeBrew安装)

1. 引言 本教程旨在介绍在Mac 电脑上安装Hadoop&#xff0c;便于编程开发人员对大数据技术的熟悉和掌握。 2.前提条件 2.1 安装JDK 想要在你的Mac电脑上安装Hadoop&#xff0c;你必须首先安装JDK。具体安装步骤这里就不详细描述了。你可参考Mac 安装JDK8。 2.2 配置ssh环境…

从腰子的营养成分来分析腰子能否“壮阳”,健康地吃腰子。

文章目录 引言I 腰子的营养优点缺点吃腰子无“壮阳”效果II 健康地吃腰子食用前充分清洗浸泡高尿酸及痛风群体慎吃适量吃引言 很多人认为动物内脏有着“以形补形”的好处,如吃动物腰子,能补肾、壮阳,这让很多人对“腰子”非常热爱。 腰子的营养到底如何?经常吃腰子对身体…

优思学院:FMEA与FTA故障树方法对比:工程师必须知道的关键点!

故障树分析&#xff08;Fault Tree Analysis, FTA&#xff09;以某个不希望发生的产品故障事件或严重的系统风险&#xff08;即顶事件&#xff09;为分析对象&#xff0c;通过自上而下的分层次因果逻辑分析&#xff0c;逐步找出导致故障事件的必要且充分的直接原因&#xff0c;…

日程安排组件DHTMLX Scheduler v7.1 - 支持RFC-5545格式

DHTMLX Scheduler是一个类似于Google日历的JavaScript日程安排控件&#xff0c;日历事件通过Ajax动态加载&#xff0c;支持通过拖放功能调整事件日期和时间&#xff0c;事件可以按天、周、月三个种视图显示。 此版本包括几个备受期待的特性&#xff0c;可以帮助用户增强DHTMLX…

基于php+vue+uniapp的医院预约挂号系统小程序

开发语言&#xff1a;PHP框架&#xff1a;phpuniapp数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;PhpStorm 系统展示 后台登录界面 管理员功能界面 用户管理 医生管理 科室分类管理 医生信息管理 预…

30s到0.8s,记录一次接口优化成功案例!

大家好&#xff0c;我是沐子&#xff0c;推荐一个程序员免费学习的编程网站 我爱编程网&#xff08;www.love-coding.com&#xff09; ** 场景** 在高并发的数据处理场景中&#xff0c;接口响应时间的优化显得尤为重要。本文将分享一个真实案例&#xff0c;其中一个数据量达到…

(5) 归并排序

归并排序 归并排序是一种分治策略的排序算法。它是一种比较特殊的排序算法&#xff0c;通过递归地先使每个子序列有序&#xff0c;再将两个有序的序列进行合并成一个有序的序列。 归并排序首先由著名的现代计算机之父 John_von_Neumann 在 1945 年发明&#xff0c;被用在了 E…

【Python】Python 读取Excel、DataFrame对比并选出差异数据,重新写入Excel

背景&#xff1a;我在2个系统下载出了两个Excel&#xff0c;现在通过对下载的2个Excel数据&#xff0c;并选出差异数据 从新写入一个新的Excel中 differences_url rC:\Users\LENOVO\Downloads\differences.xlsx; //要生成的差异Excel的位置及名称 df1_url rC:\Users\LENOVO\Dow…

终于知道如何简化时间序列的特征工程了!

在处理时间序列数据时&#xff0c;时间特征往往是最基础且独特的要素&#xff0c;我们的目标通常是预测某种未来的响应或结果。 不过在很多情况下&#xff0c;除了时间特征之外&#xff0c;我们还能获取到一系列其他相关的特征或变量。 时间序列数据中的特征工程涉及从原始时…

进程、线程、时间片

1、操作系统中的程序&#xff08;如微信&#xff09;在运行时&#xff0c;系统会产生一个或多个进程&#xff0c;往往是一个 2、进程内可以包含多个线程&#xff0c;有一个主线程&#xff0c;主线程结束时&#xff0c;进程结束&#xff0c;进而程序结束 3、线程是cpu调度执行…

sql日期函数

目录 sql日期函数 1.获取日期时间函数 1.1 获取当前日期时间 1.2 获取当前日期 1.3 获取当前时间 2.datetime数据类型格式化 3.字符串数据类型转换成datetime数据类型 4.增加和减少时间间隔 5. 日期相差天数&#xff08;天&#xff09; 6. 相差时间&#xff08;小时&am…

GitHub Star 数量前 11 的开源内部工具

欢迎回到我们的 GitHub Star 系列文章&#xff01; 在之前的文章中&#xff0c;我们深入探讨了 GitHub 上最受欢迎的开源低代码项目《GitHub Star 数量前 15 的开源低代码项目》和开源无代码工具《GitHub Star 数量前 12 的开源无代码工具》&#xff0c;获得了热烈的反馈。本周…

【嵌入式学习笔记】---- OLED屏幕工作原理

1 驱动芯片SSD1603简介 1.1 SSD1603芯片图 SSD1603是一款点阵显示屏控制器&#xff0c;可嵌入在屏幕中&#xff0c;用于执行接收数据、显示存储、扫描刷新等任务驱动接口&#xff1a;128个SEG引脚和64个COM引脚&#xff0c;对应 128 64 128\times 64 12864像素点阵显示屏内置…

增强RAG:选择最佳的嵌入和重排模型

对于如何选择最佳的嵌入模型和重排模型&#xff0c;给出了详细的步骤和代码。 在构建检索增强生成&#xff08;RAG&#xff09;管道时&#xff0c;关键组件之一是检索器。我们有多种嵌入模型可供选择&#xff0c;包括 OpenAI、CohereAI 和开源的sentence transformers。此外&a…

排序(插入,希尔,选择,堆,冒泡,快速,归并,计数)

本文中的Swap()函数都是下面这段代码 // 交换 void Swap(int* p1, int* p2) {int tmp *p1;*p1 *p2;*p2 tmp; }文章目录 常见排序&#xff1a;一.插入排序1.直接插入排序&#xff1a;2.希尔排序&#xff1a; 二.选择排序1.选择排序&#xff1a;2.堆排序&#xff1a; 三.交换排…

C语言编译的过程

文章目录 1. 预处理&#xff08;Preprocessing&#xff09;2. 编译&#xff08;Compilation&#xff09;3. 汇编&#xff08;Assembly&#xff09;4. 链接&#xff08;Linking&#xff09;总结 c语言通过编译器直接编译成机器语言程序。 C语言程序的编译过程通常分为四个主要步…

STM32G474之TIM1输出PWM信号支持互补输出,死区时间和刹车

STM32G474之TIM1输出PWM信号&#xff0c;互补输出&#xff0c;支持死区时间和刹车。PWM第1通道输出引脚配置&#xff1a;TIM1_CH1映射到PA8,TIM1_CH1N映射到PA7&#xff0c;TIM1_BKIN映射到PA6&#xff0c;用作刹车输入信号。当刹车时&#xff0c;停止PWM波形输出。在使用“比较…

海上8km远距离无线通信模组,无人船MESH组网,飞睿WiFi助力海洋船只通信无障碍

在蔚蓝无垠的海洋世界里&#xff0c;每一次科技的进步都如同海上的灯塔&#xff0c;为我们照亮了前行的道路。今天&#xff0c;我要为大家介绍的&#xff0c;就是一款能够打破传统通信界限的模块——飞睿智能8km远距离无线通信模组。它不仅在陆地通信中展现出强大的实力&#x…

4. 第一个3D案例—创建3D场景

入门Three.js的第一步&#xff0c;就是认识场景Scene、相机Camera、渲染器Renderer三个基本概念&#xff0c;接下来&#xff0c;咱们通过三小节课&#xff0c;大家演示“第一个3D案例”完成实现过程。 学习建议&#xff1a;只要你能把第一个3D案例搞明白&#xff0c;后面学习就…