DSL查询语法
DSLQuery的分类
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
DSLQuery基本语法
查询的基本语法如下
去查询酒店所有信息
全文检索查询
全文检索查询,会对用户输入内容分词,常用于搜索框搜索
match查询
match查询:全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索,语法
all中我们之前加入了有brand、business、name这些字段,“外滩如家”会在这几个字段中进行匹配,选出含有本词条的酒店信息
multi_match查询
multi_match:与match查询类似,只不过允许同时查询多个字段,语法
使用multi_match对词条“外滩如家”在brand、business、name这三个字段中去匹配查询,故此时和上面的match查询对all字段进行匹配查询效果一样。
match和multi_match的区别是什么?
match: 根据一个字段查询
mult_match: 根据多个字段查询,参与查询字段越多,查询性能越差
精确查询
精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有
term:根据词条精确值查询
range:根据值的范围查询
精确查询一般是根据id、数值、keyword类型、或布尔字段来查询。语法如下
term查询
根据词条精确值查询
对城市为“上海”的酒店进行精确查询,查出所有的所在城市都是上海的的酒店信息
将term查询中的精确查询内容的城市值换成“杭州上海”,因为所有的酒店城市没有是“杭州上海”的,所以查询结果为空
range查询
根据值的范围查询
根据酒店的价格price进行range查询,查出所有价格price在区间100-300元之间的酒店信息。
地理查询
根据经纬度查询。常见的使用场景包括
携程:搜索我附近的酒店
滴滴:搜索我附近的出租车
微信:搜索我附近的人
根据经纬度查询。例如
geo_bounding_box查询
geo_bounding_box:查询geo_point值落在某个矩形范围的所有文档
geo_distance查询
geo_distance:查询到指定中心点小于某个距离值的所有文栏
查出在31.21,123.5这个点15km以内的所有酒店信息
复合查询
复合(compound)查询: 复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑,例如
fuction_score查询
fuction_score: 算分函数查询,可以控制文档相关性算分,控制文档排名。例如百度竞价
相关性算分
当我们利用match查询时,文档结果会根据与搜索词条的关联度打分( score),返回结果时按照分值降序排列。例如,我们搜索“虹桥如家",结果如下
fuction_score查询,可以修改文档的相关性算分 (query score),根据新得到的算分排序。
案例:给“如家”这个品牌的酒店排名靠前一些
把这个问题翻译一下,function score需要的三要素:
1.哪些文档需要算分加权? 品牌为如家的酒店
2.算分函数是什么? weight就可以
3.加权模式是什么? 求和
先普通的全文检索查询,“外滩”在brand、business、name这些字段中匹配,查询出符合要求的酒店信息
我们使得品牌为“如家”的酒店的最后得分,再乘以weight,可以看到刚才的那个酒店因为名字为”如家“,所以得分变成了38.828926,变成了以前的10倍
再定义权重的方式我们再查询,方式定义为sum,也就是其本来得分加上weight(10),故那个酒店的最后得分变成了13.882893,比最早的得分增加了10
复合查询 Boolean Query
布尔查询是一个或多个查询子句的组合。子查询的组合方式有
must: 必须匹配每个子查询,类似“与”
should:选择性匹配子查询,类似“或
must not:必须不匹配,不参与算分,类似“非'
filter: 必须匹配,不参与算分
案例:利用bool查询实现功能
需求:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店
搜索结果处理
排序
elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序字段类型有: keyword类型、数值类型、地理坐标类型、日期类型等
案例:对酒店数据按照用户评价降序排序,评价相同的按照价格升序排序
评价是score字段,价格是price字段,按照顺序添加两个排序规则即可
写出DSL
案例:实现对酒店数据按照到你的位置坐标的距离升序排序
写出DSL,看到返回的酒店信息里,每条增加了一个sort字段,4.8541199685347785意思就是距离你的位置有4.85km远,查到的酒店顺序是按照这个值升序排列的,越近的排的位置越靠前。
分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果
深度分页问题
ES是分布式的,所以会面临深度分页问题。例如按price排序后,获取from = 990,size =10的数据
1.首先在每个数据分片上都排序并查询前1000条文档
2.然后将所有节点的结果聚合,在内存中重新排序选出前1000条文档
3.最后从这1000条中,选取从990开始的10条文档
如果搜索页数过深,或者结果集 (rom+size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000
深度分页解决方案
针对深度分页,ES提供了两种解决方案:
search after: 分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式
scroll:原理将排序数据形成快照,保存在内存。官方已经不推荐使用
高亮
高亮:就是在搜索结果中把搜索关键字突出显示。
原理是这样的:
将搜索结果中的关键字用标签标记出来
在页面中给标签添加css样式
语法:
编写DSL查询语句
搜索结果处理整体语法
RestClient查询文档
快速入门
我们通过match all来演示下基本的API,先看请求DSL的组织:
编写一个测试类,先将客户端进行初始化
编写match all查询的测试方法
查询结果
我们通过match_all来演示下基本的API,再看结果的解析
在刚才编写的测试方法里增加对结果的解析
查看结果,结果被解析成了一个个对应的hotelDoc对象
全文检索查询
全文检索的match和multi_match查询与match_all的API基本一致。差别是查询条件,也就是query的部分同样是利用QueryBuilders提供的方法
编写match查询的测试方法
可以发现,对与结果的解析这部分的代码每个查询都需要用到,故我们用ctrl+alt+m将这一块的代码抽取成一个单独的方法
抽取出的方法如下,在解析响应结果时,可直接使用
刚才的match查询的测试方法简化成如下所示
精确查询
精确查询常见的有term查询和range查询,同样利用QueryBuilders实现
复合查询-boolean query
精确查询常见的有term查询和range查询,同样利用QueryBuilders实现
编写boolean query查询测试方法
查询出所有city=杭州,price小于250的酒店信息
要构建查询条件,只要记住一个类: QueryBuilders
排序和分页
搜索结果的排序和分页是与query同级的参数,对应的API如下
编写排序和分页查询的测试方法
查询出价格按照升序排列,第一页且一页有5条的酒店信息。
高亮
高亮DSL构建
高亮API包括请求DSL构建和结果解析两部分。我们先看请求的DSL构建
编写高亮查询的测试方法
高亮结果解析
高亮的结果处理相对比较麻烦
在解析结果后面添加对高亮结果的处理
可以看到,返回的结果中name=如家的已经带上了高亮标签,前端接收到后对带有该标签的结果进行高亮显示即可
所有搜索DSL的构建,记住一个API:
SearchRequest的source()方法
高亮结果解析是参考JSON结果,逐层解析
黑马旅游案例
案例1: 实现黑马旅游的酒店搜索功能,完成关键字搜索和分页
我们课前提供的hotel-demo项目中,自带了前端页面,启动后可以看到
先实现其中的关键字搜索功能,实现步骤如下
1.定义实体类,接收前端请求
2.定义controller接口,接收页面请求,调用IHotelService的search方法
3.定义IHotelService中的search方法,利用match查询实现根据关键字搜索酒店信息
步骤1:定义类,接收前端请求参数
格式如下:
步骤2: 定义controller接口,接收前端请求
定义一个HotelController,声明查询接口,满足下列要求:
请求方式:Post
请求路径: /hotel/list
请求参数:对象,类型为RequestParam
返回值:PageResult,包含两个属性 Long total: 总条数 List<HotelDoc> hotels: 酒店数据
编写返回值PageResult类
编写Controller
服务层生成该业务方法
在这个项目的主启动类中初始化客户端
编写具体的业务方法
在返回的结果类中添加一个有参构造器
抽取出来的解析结果那个方法修改一些内容
以下是该查询的完整业务方法
运行后,发现方法成功运行
案例2:添加品牌、城市、星级、价格等过滤功能
需求效果如图
步骤:
1.修改RequestParams类,添加brand、city、starName、minPrice、maxPrice等参数
2.修改search方法的实现,在关键字搜索时,如果brand等参数存在,对其做过滤
步骤一: 拓展IUserService的search方法的参数列表
修改RequestParams类,接收所有参数
步骤二: 修改search方法,在match查询基础上添加过滤条件
过滤条件包括:
city精确匹配
brand精确匹配
starName精确匹配
price范围过滤
注意事项:
多个条件之间是AND关系,组合多条件用BooleanQuery
参数存在才需要过滤,做好非空判断
编写该业务方法
将构建BooleanQuery的内容抽取出来成为一个单独的方法
运行方法,业务成功运行
案例3: 我附近的酒店
前端页面点击定位后,会将你所在的位置发送到后台
我们要根据这个坐标,将酒店结果按照到这个点的距离升序排序
实现思路如下:
修改RequestParams参数,接收location字段
修改search方法业务逻辑,如果location有值,添加根据geo distance排序的功能
距离排序
距离排序与普通字段排序有所差异,API如下
将接收类扩展接收location字段
扩写之前的业务方法
sort中显示的是该酒店对自己位置的距离
在返回的结果类中扩展这个距离属性
在解析结果的方法中扩展解析sort距离值的内容,将该距离内容赋值给结果类
运行起来,业务成功实现
案例4: 让指定的酒店在搜索结果中排名置顶
我们给需要置顶的酒店文档添加一个标记。然后利用function_score给带有标记的文档增加权重
实现步骤分析:
1.给HotelDoc类添加isAD字段,Boolean类型
2.挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true
3.修改search方法,添加function score功能给isAD值为true的酒店增加权重
给结果类中填加isAD字段
挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true,就是模拟这几个酒店买广告了
组合查询-function score
Function score查询可以控制文档的相关性算分,使用方式如下
在业务方法中扩展该业务
启动方法,业务成功运行