我的手机微信为什么搜索不到附近人用不了怎么回事的人呢?


选择擅长的领域继续答题?
{@each tagList as item}
${item.tagName}
{@/each}
手机回答更方便,互动更有趣,下载APP
提交成功是否继续回答问题?
手机回答更方便,互动更有趣,下载APP
方法11、点击【微信】右下角的【我】功能,点击进入【设置】;- 2、点击【通用】设置;- 3、点击进入【功能】选项;- 4、点击【附近的人】按钮;- 5、点击【启动该功能】启动附近的人;- 6、点击【开始查看附近的人】即可;方法2- 1、点击【发现】功能,进入【附近的人】;- 2、即可查看到附近的人;- 3、如果其他的人看不到自己,可以点击右上角【···】按钮并点击查看【附近打招呼的人】;方法3- 1、点击进入手机的【设置】;- 2、进入【微信】设置;- 3、点击进入【微信-位置】;- 4、将位置设置成【使用应用期间】或者【始终】即可,可以看到官方说明如果不开启此功能附近的人将无法使用。关于微信搜索附近的人的时候,别人看不到自己,但是自己能看到别人怎么办?很多人都遇到这样的问题,总是说被人举报了,才搜索不到;其实你没被举报,而是你的行为让微信系统把你屏蔽了。被举报了的帐号,是连“附近的人”这个功能都用不了的,点进去直接说:你的帐号违反什么什么,或者说被人举报,暂时禁止使用。大家需要思考,首先你的微信涉及到违规的信息了吗?违反了微信设立的规则吗?(违规信息比较常见的有:就是你的微信发布黄赌毒,还有违法信息,乱发广告等等)别人为什么要举报你(无聊的人可能没事做去举报人家,不过那种举报,只要你不违规,微信一般不会屏蔽你的),如果是正常的帐号,正常的用户,你会发布违规信息吗?我想很多人都不会发布违规信息,还有,你没发布违规信息,就算别人举报你了,别人举报你会成功吗?这个肯定不会。说这些,只是让你们走出这个误区,大多数用户并不是因为被举报而搜索不到的。1、经常使用微信搜索附近的人,走到哪,搜到哪例如:你在家没有出门之前搜索了一下附近的人,过了一会,你出去逛街,你走到车站等车,你又在车站这里搜索一下,然后没多久上了车,等车到目的地了,你又搜索附近的人,等你在逛街的时候,休息期间你又搜索附近的人,或者每到一个地方都搜索一次,这时候你就要注意了,因为你已经频繁的使用“附近的人”这个功能,微信的系统会把你认为你是个不按正常微信用户。2、一个用户有2个或者2个以上微信号,用同一台手机登录。(这个是最常见的被屏蔽的了,N多的朋友都是遇到这样的问题)例如:你刚刚登录完第一个帐号,马上使用“附近的人”这个功能,然后马上用这台手机更换另外一个微信帐号,又搜索 “附近的人”,看看能不能搜索到你之前登录的那个帐号。3、用2台手机登录2个微信帐号,但是你的2台手机是用同一个WIFI热点上网的,这个的话在家里或者公司,就用得比较多的。例如:你有2个智能手机,都装有微信软件,你2个手机的微信都是在登录状态,然后你使用A手机先搜索附近的人,然后在用B手机搜索附近的人,你其实就是想搜索看看你的另外一个微信号能不能搜索到,如果你经常这样,你帐号被屏蔽的概率也增加了4、新注册的微信号,才注册了没多久,就马上迫不及待的搜索“附近的人”,结果也被屏蔽了。特别是使用女性的身份搜索,你本人是女的除外。建议不要那么频繁的搜索,被屏蔽了不太好。
本回答被网友采纳你这微信附近的人干得好,你能看到别人,别人看不到,你真会隐身。
如今手机正变成我们的日记、账本、钱包等等而与之相关的危险也越来越多“利用微信‘附近的人’功能,连续变换三次以上位置,再辅以电子地图,就能定位出用户在哪儿。”这一网络传言已被确认属实。1微信这些开关要关掉① 微信“附近的人”功能可定位你的位置,依次点击“设置-通用-功能-附近的人”,选择“清空并停用”,必要时可重新开启;②微信“隐私”选项中关闭“允许陌生人查看十张照片”;③微信“隐私”中关闭“通过QQ好友搜索到我”和“可通过手机号搜索到我”。2被索要微信验证码,不要给!有人佯装手机刷机,要求对方发送手机号与验证码给他,以通过好友验证,如果发送验证码,微信可能立马会被盗!一旦中招,可能波及很多人!3iPhone别记录我常去哪iPhone手机中有“常去地点”功能,可用地图显示机主经常去的位置。不想让人知道?可以依次点击“设置-隐私-定位服务-系统服务-常去地点”,关闭该选项即可。4及时关闭手机wifi功能发现免费wifi不要随便登录,这可能是黑客用来入侵你手机的工具!江西一位先生使用没有设置密码就能直接登录的免费wifi,并用手机输入自己网银卡号密码,结果发现账户被人转走3.4万元!5装软件少“允许”手机安装游戏等软件时,常被要求“使用你的位置”,一旦点击“好”,这些应用便可扫描并把手机信息上传到互联网云服务器,一旦资料泄露,别人就可能知道您的位置、跟谁通话、玩啥游戏、家在哪……6别随便晒孩子照片有些家长爱在社交网络发孩子的照片,会提到孩子的名字、学校,图片也可能透露不少居住小区的线索。根据这些信息很容易汇总出孩子的名字、家庭住址、学校,可能会让孩子有潜在的安全风险。7网上测试小心有诈“测测你的心理年龄”、“测测你前世是谁”、“测测你的出轨概率”……测试时输入的姓名、生日、手机号码,会被存入后台,对其梳理,有可能拼凑出完整个人信息。8小心!陌生链接谨慎点击苏州张女士接到一条短信,“这是你做的事的照片,太恶心了吧!” 她随手点击链接,之后数张银行卡里的4万多元竟不翼而飞。警方查明,手机中的apk病毒可拦截短信,开通第三方快捷支付后无需银行卡密码,仅凭手机验证码就能轻松盗刷。9旧手机丢弃前必须做的事①将手机恢复出厂设置或格式化,再存入一些其他无关紧要的内容,将手机的存储空间占满。重复几次后,原有数据很难被恢复;②将手机卖给相对正规的厂家,或参加官方的以旧换新活动;③不要将手机作为一般的生活垃圾扔掉。10手机号注销前要做的事①将U盾、网银、手机银行、短信通知等银行卡业务解绑;②将证券、基金账户等金融业务解绑;③将淘宝、微信等第三方支付平台解绑;④变更微信、微博、QQ等服务的关联电话。一定要完成所有业务解绑后再销号!来源:网信广东、中国好网民
分布式搜索引擎1.初识elasticsearch1.1.了解ES1.1.1.elasticsearch的作用elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容例如: 在GitHub搜索代码
在电商网站搜索商品
在百度搜索答案
在打车软件搜索附近的车
1.1.2.ELK技术栈elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域:而elasticsearch是elastic stack的核心,负责存储、搜索、分析数据。1.1.3.elasticsearch和luceneelasticsearch底层是基于lucene来实现的。Lucene是一个Java语言的搜索引擎类库,是Apache公司的顶级项目,由DougCutting于1999年研发。官网地址:https://lucene.apache.org/ 。elasticsearch的发展历史:2004年Shay Banon基于Lucene开发了Compass2010年Shay Banon 重写了Compass,取名为Elasticsearch。1.1.4.为什么不是其他搜索技术?目前比较知名的搜索引擎技术排名:虽然在早期,Apache Solr是最主要的搜索引擎技术,但随着发展elasticsearch已经渐渐超越了Solr,独占鳌头:1.1.5.总结什么是elasticsearch?一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能什么是elastic stack(ELK)?是以elasticsearch为核心的技术栈,包括beats、Logstash、kibana、elasticsearch什么是Lucene?是Apache的开源搜索引擎类库,提供了搜索引擎的核心API1.2.倒排索引倒排索引的概念是基于MySQL这样的正向索引而言的。1.2.1.正向索引那么什么是正向索引呢?例如给下表(tb_goods)中的id创建索引:如果是根据id查询,那么直接走索引,查询速度非常快。但如果是基于title做模糊查询,只能是逐行扫描数据,流程如下:1)用户搜索数据,条件是title符合"%手机%"2)逐行获取数据,比如id为1的数据3)判断数据中的title是否符合用户搜索条件4)如果符合则放入结果集,不符合则丢弃。回到步骤1逐行扫描,也就是全表扫描,随着数据量增加,其查询效率也会越来越低。当数据量达到数百万时,就是一场灾难。1.2.2.倒排索引倒排索引中有两个非常重要的概念:文档(Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条创建倒排索引是对正向索引的一种特殊处理,流程如下:将每一个文档的数据利用算法分词,得到一个个词条创建表,每行数据包括词条、词条所在文档id、位置等信息因为词条唯一性,可以给词条创建索引,例如hash表结构索引如图:倒排索引的搜索流程如下(以搜索"华为手机"为例):1)用户输入条件"华为手机"进行搜索。2)对用户输入内容分词,得到词条:华为、手机。3)拿着词条在倒排索引中查找,可以得到包含词条的文档id:1、2、3。4)拿着文档id到正向索引中查找具体文档。如图:虽然要先查询倒排索引,再查询倒排索引,但是无论是词条、还是文档id都建立了索引,查询速度非常快!无需全表扫描。1.2.3.正向和倒排那么为什么一个叫做正向索引,一个叫做倒排索引呢?是不是恰好反过来了?那么两者方式的优缺点是什么呢?正向索引:优点:
可以给多个字段创建索引根据索引字段搜索、排序速度非常快缺点:
根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描。倒排索引:优点:
根据词条搜索、模糊搜索时,速度非常快缺点:
只能给词条创建索引,而不是字段无法根据字段做排序1.3.es的一些概念elasticsearch中有很多独有的概念,与mysql中略有差别,但也有相似之处。1.3.1.文档和字段elasticsearch是面向**文档(Document)**存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中:而Json文档中往往包含很多的字段(Field),类似于数据库中的列。1.3.2.索引和映射索引(Index),就是相同类型的文档的集合。例如:所有用户文档,就可以组织在一起,称为用户的索引;所有商品的文档,可以组织在一起,称为商品的索引;所有订单的文档,可以组织在一起,称为订单的索引;因此,我们可以把索引当做是数据库中的表。数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。1.3.3.mysql与elasticsearch我们统一的把mysql与elasticsearch的概念做一下对比:MySQLElasticsearch说明TableIndex索引(index),就是文档的集合,类似数据库的表(table)RowDocument文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式ColumnField字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)SchemaMappingMapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)SQLDSLDSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD是不是说,我们学习了elasticsearch就不再需要mysql了呢?并不是如此,两者各自有自己的擅长支出:因此在企业中,往往是两者结合使用:对安全性要求较高的写操作,使用mysql实现对查询性能要求较高的搜索需求,使用elasticsearch实现两者再基于某种方式,实现数据的同步,保证一致性1.4.安装es、kibana1.4.1.安装参考课前资料:1.4.2.分词器参考课前资料:1.4.3.总结分词器的作用是什么?创建倒排索引时对文档分词用户搜索时,对输入的内容分词IK分词器有几种模式?ik_smart:智能切分,粗粒度ik_max_word:最细切分,细粒度IK分词器如何拓展词条?如何停用词条?利用config目录的IkAnalyzer.cfg.xml文件添加拓展词典和停用词典在词典中添加拓展词条或者停用词条2.索引库操作索引库就类似数据库表,mapping映射就类似表的结构。我们要向es中存储数据,必须先创建“库”和“表”。2.1.mapping映射属性mapping是对索引库中文档的约束,常见的mapping属性包括:type:字段数据类型,常见的简单类型有:
字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)数值:long、integer、short、byte、double、float、布尔:boolean日期:date对象:objectindex:是否创建索引,默认为trueanalyzer:使用哪种分词器properties:该字段的子字段例如下面的json文档:{
"age": 21,
"weight": 52.1,
"isMarried": false,
"info": "黑马程序员Java讲师",
"email": "zy@itcast.cn",
"score": [99.1, 99.5, 98.9],
"name": {
"firstName": "云",
"lastName": "赵"
}
}
对应的每个字段映射(mapping):age:类型为 integer;参与搜索,因此需要index为true;无需分词器weight:类型为float;参与搜索,因此需要index为true;无需分词器isMarried:类型为boolean;参与搜索,因此需要index为true;无需分词器info:类型为字符串,需要分词,因此是text;参与搜索,因此需要index为true;分词器可以用ik_smartemail:类型为字符串,但是不需要分词,因此是keyword;不参与搜索,因此需要index为false;无需分词器score:虽然是数组,但是我们只看元素的类型,类型为float;参与搜索,因此需要index为true;无需分词器name:类型为object,需要定义多个子属性
name.firstName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器name.lastName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器2.2.索引库的CRUD这里我们统一使用Kibana编写DSL的方式来演示。2.2.1.创建索引库和映射基本语法:请求方式:PUT请求路径:/索引库名,可以自定义请求参数:mapping映射格式:PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}
示例:PUT /heima
{
"mappings": {
"properties": {
"info":{
"type": "text",
"analyzer": "ik_smart"
},
"email":{
"type": "keyword",
"index": "falsae"
},
"name":{
"properties": {
"firstName": {
"type": "keyword"
}
}
},
// ... 略
}
}
}
2.2.2.查询索引库基本语法: 请求方式:GET
请求路径:/索引库名
请求参数:无 格式:GET /索引库名
示例:2.2.3.修改索引库倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改mapping。虽然无法修改mapping中已有的字段,但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。语法说明:PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
示例:2.2.4.删除索引库语法: 请求方式:DELETE
请求路径:/索引库名
请求参数:无 格式:DELETE /索引库名
在kibana中测试:2.2.5.总结索引库操作有哪些?创建索引库:PUT /索引库名查询索引库:GET /索引库名删除索引库:DELETE /索引库名添加字段:PUT /索引库名/_mapping3.文档操作3.1.新增文档语法:POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
示例:POST /heima/_doc/1
{
"info": "黑马程序员Java讲师",
"email": "zy@itcast.cn",
"name": {
"firstName": "云",
"lastName": "赵"
}
}
响应:3.2.查询文档根据rest风格,新增是post,查询应该是get,不过查询一般都需要条件,这里我们把文档id带上。语法:GET /{索引库名称}/_doc/{id}
通过kibana查看数据:GET /heima/_doc/1
查看结果:3.3.删除文档删除使用DELETE请求,同样,需要根据id进行删除:语法:DELETE /{索引库名}/_doc/id值
示例:# 根据id删除数据
DELETE /heima/_doc/1
结果:3.4.修改文档修改有两种方式:全量修改:直接覆盖原来的文档增量修改:修改文档中的部分字段3.4.1.全量修改全量修改是覆盖原来的文档,其本质是:根据指定的id删除文档新增一个相同id的文档注意:如果根据id删除时,id不存在,第二步的新增也会执行,也就从修改变成了新增操作了。语法:PUT /{索引库名}/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
// ... 略
}
示例:PUT /heima/_doc/1
{
"info": "黑马程序员高级Java讲师",
"email": "zy@itcast.cn",
"name": {
"firstName": "云",
"lastName": "赵"
}
}
3.4.2.增量修改增量修改是只修改指定id匹配的文档中的部分字段。语法:POST /{索引库名}/_update/文档id
{
"doc": {
"字段名": "新的值",
}
}
示例:POST /heima/_update/1
{
"doc": {
"email": "ZhaoYun@itcast.cn"
}
}
3.5.总结文档操作有哪些?创建文档:POST /{索引库名}/_doc/文档id { json文档 }查询文档:GET /{索引库名}/_doc/文档id删除文档:DELETE /{索引库名}/_doc/文档id修改文档:
全量修改:PUT /{索引库名}/_doc/文档id { json文档 }增量修改:POST /{索引库名}/_update/文档id { “doc”: {字段}}4.RestAPIES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html其中的Java Rest Client又包括两种:Java Low Level Rest ClientJava High Level Rest Client我们学习的是Java HighLevel Rest Client客户端API4.0.导入Demo工程4.0.1.导入数据首先导入课前资料提供的数据库数据:数据结构如下:CREATE TABLE `tb_hotel` (
`id` bigint(20) NOT NULL COMMENT '酒店id',
`name` varchar(255) NOT NULL COMMENT '酒店名称;例:7天酒店',
`address` varchar(255) NOT NULL COMMENT '酒店地址;例:航头路',
`price` int(10) NOT NULL COMMENT '酒店价格;例:329',
`score` int(2) NOT NULL COMMENT '酒店评分;例:45,就是4.5分',
`brand` varchar(32) NOT NULL COMMENT '酒店品牌;例:如家',
`city` varchar(32) NOT NULL COMMENT '所在城市;例:上海',
`star_name` varchar(16) DEFAULT NULL COMMENT '酒店星级,从低到高分别是:1星到5星,1钻到5钻',
`business` varchar(255) DEFAULT NULL COMMENT '商圈;例:虹桥',
`latitude` varchar(32) NOT NULL COMMENT '纬度;例:31.2497',
`longitude` varchar(32) NOT NULL COMMENT '经度;例:120.3925',
`pic` varchar(255) DEFAULT NULL COMMENT '酒店图片;例:/img/1.jpg',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.0.2.导入项目然后导入课前资料提供的项目:项目结构如图:4.0.3.mapping映射分析创建索引库,最关键的是mapping映射,而mapping映射要考虑的信息包括:字段名字段数据类型是否参与搜索是否需要分词如果分词,分词器是什么?其中:字段名、字段数据类型,可以参考数据表结构的名称和类型是否参与搜索要分析业务来判断,例如图片地址,就无需参与搜索是否分词呢要看内容,内容如果是一个整体就无需分词,反之则要分词分词器,我们可以统一使用ik_max_word来看下酒店数据的索引库结构:PUT /hotel
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword",
"copy_to": "all"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
几个特殊字段说明:location:地理坐标,里面包含精度、纬度all:一个组合字段,其目的是将多字段的值 利用copy_to合并,提供给用户搜索地理坐标说明:copy_to说明:4.0.4.初始化RestClient在elasticsearch提供的API中,与elasticsearch一切交互都封装在一个名为RestHighLevelClient的类中,必须先完成这个对象的初始化,建立与elasticsearch的连接。分为三步:1)引入es的RestHighLevelClient依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.12.1</version>
</dependency>
2)因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本:<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
3)初始化RestHighLevelClient:初始化的代码如下:RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
这里为了单元测试方便,我们创建一个测试类HotelIndexTest,然后将初始化的代码编写在@BeforeEach方法中:package cn.itcast.hotel;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class HotelIndexTest {
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
4.1.创建索引库4.1.1.代码解读创建索引库的API如下:代码分为三步:1)创建Request对象。因为是创建索引库的操作,因此Request是CreateIndexRequest。2)添加请求参数,其实就是DSL的JSON参数部分。因为json字符串很长,这里是定义了静态字符串常量MAPPING_TEMPLATE,让代码看起来更加优雅。3)发送请求,client.indices()方法的返回值是IndicesClient类型,封装了所有与索引库操作有关的方法。4.1.2.完整示例在hotel-demo的cn.itcast.hotel.constants包下,创建一个类,定义mapping映射的JSON字符串常量:package cn.itcast.hotel.constants;
public class HotelConstants {
public static final String MAPPING_TEMPLATE = "{\n" +
"
\"mappings\": {\n" +
"
\"properties\": {\n" +
"
\"id\": {\n" +
"
\"type\": \"keyword\"\n" +
"
},\n" +
"
\"name\":{\n" +
"
\"type\": \"text\",\n" +
"
\"analyzer\": \"ik_max_word\",\n" +
"
\"copy_to\": \"all\"\n" +
"
},\n" +
"
\"address\":{\n" +
"
\"type\": \"keyword\",\n" +
"
\"index\": false\n" +
"
},\n" +
"
\"price\":{\n" +
"
\"type\": \"integer\"\n" +
"
},\n" +
"
\"score\":{\n" +
"
\"type\": \"integer\"\n" +
"
},\n" +
"
\"brand\":{\n" +
"
\"type\": \"keyword\",\n" +
"
\"copy_to\": \"all\"\n" +
"
},\n" +
"
\"city\":{\n" +
"
\"type\": \"keyword\",\n" +
"
\"copy_to\": \"all\"\n" +
"
},\n" +
"
\"starName\":{\n" +
"
\"type\": \"keyword\"\n" +
"
},\n" +
"
\"business\":{\n" +
"
\"type\": \"keyword\"\n" +
"
},\n" +
"
\"location\":{\n" +
"
\"type\": \"geo_point\"\n" +
"
},\n" +
"
\"pic\":{\n" +
"
\"type\": \"keyword\",\n" +
"
\"index\": false\n" +
"
},\n" +
"
\"all\":{\n" +
"
\"type\": \"text\",\n" +
"
\"analyzer\": \"ik_max_word\"\n" +
"
}\n" +
"
}\n" +
"
}\n" +
"}";
}
在hotel-demo中的HotelIndexTest测试类中,编写单元测试,实现创建索引:@Test
void createHotelIndex() throws IOException {
// 1.创建Request对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
// 2.准备请求的参数:DSL语句
request.source(MAPPING_TEMPLATE, XContentType.JSON);
// 3.发送请求
client.indices().create(request, RequestOptions.DEFAULT);
}
4.2.删除索引库删除索引库的DSL语句非常简单:DELETE /hotel
与创建索引库相比:请求方式从PUT变为DELTE请求路径不变无请求参数所以代码的差异,注意体现在Request对象上。依然是三步走:1)创建Request对象。这次是DeleteIndexRequest对象2)准备参数。这里是无参3)发送请求。改用delete方法在hotel-demo中的HotelIndexTest测试类中,编写单元测试,实现删除索引:@Test
void testDeleteHotelIndex() throws IOException {
// 1.创建Request对象
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
// 2.发送请求
client.indices().delete(request, RequestOptions.DEFAULT);
}
4.3.判断索引库是否存在判断索引库是否存在,本质就是查询,对应的DSL是:GET /hotel
因此与删除的Java代码流程是类似的。依然是三步走:1)创建Request对象。这次是GetIndexRequest对象2)准备参数。这里是无参3)发送请求。改用exists方法@Test
void testExistsHotelIndex() throws IOException {
// 1.创建Request对象
GetIndexRequest request = new GetIndexRequest("hotel");
// 2.发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}
4.4.总结JavaRestClient操作elasticsearch的流程基本类似。核心是client.indices()方法来获取索引库的操作对象。索引库操作的基本步骤:初始化RestHighLevelClient创建XxxIndexRequest。XXX是Create、Get、Delete准备DSL( Create时需要,其它是无参)发送请求。调用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete5.RestClient操作文档为了与索引库操作分离,我们再次参加一个测试类,做两件事情:初始化RestHighLevelClient我们的酒店数据在数据库,需要利用IHotelService去查询,所以注入这个接口package cn.itcast.hotel;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.service.IHotelService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.List;
@SpringBootTest
public class HotelDocumentTest {
@Autowired
private IHotelService hotelService;
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
5.1.新增文档我们要将数据库的酒店数据查询出来,写入elasticsearch中。5.1.1.索引库实体类数据库查询后的结果是一个Hotel类型的对象。结构如下:@Data
@TableName("tb_hotel")
public class Hotel {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String longitude;
private String latitude;
private String pic;
}
与我们的索引库结构存在差异:longitude和latitude需要合并为location因此,我们需要定义一个新的类型,与索引库结构吻合:package cn.itcast.hotel.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
}
}
5.1.2.语法说明新增文档的DSL语句如下:POST /{索引库名}/_doc/1
{
"name": "Jack",
"age": 21
}
对应的java代码如图:可以看到与创建索引库类似,同样是三步走:1)创建Request对象2)准备请求参数,也就是DSL中的JSON文档3)发送请求变化的地方在于,这里直接使用client.xxx()的API,不再需要client.indices()了。5.1.3.完整代码我们导入酒店数据,基本流程一致,但是需要考虑几点变化:酒店数据来自于数据库,我们需要先查询出来,得到hotel对象hotel对象需要转为HotelDoc对象HotelDoc需要序列化为json格式因此,代码整体步骤如下:1)根据id查询酒店数据Hotel2)将Hotel封装为HotelDoc3)将HotelDoc序列化为JSON4)创建IndexRequest,指定索引库名和id5)准备请求参数,也就是JSON文档6)发送请求在hotel-demo的HotelDocumentTest测试类中,编写单元测试:@Test
void testAddDocument() throws IOException {
// 1.根据id查询酒店数据
Hotel hotel = hotelService.getById(61083L);
// 2.转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel);
// 3.将HotelDoc转json
String json = JSON.toJSONString(hotelDoc);
// 1.准备Request对象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
// 2.准备Json文档
request.source(json, XContentType.JSON);
// 3.发送请求
client.index(request, RequestOptions.DEFAULT);
}
5.2.查询文档5.2.1.语法说明查询的DSL语句如下:GET /hotel/_doc/{id}
非常简单,因此代码大概分两步:准备Request对象发送请求不过查询的目的是得到结果,解析为HotelDoc,因此难点是结果的解析。完整代码如下:可以看到,结果是一个JSON,其中文档放在一个_source属性中,因此解析就是拿到_source,反序列化为Java对象即可。与之前类似,也是三步走:1)准备Request对象。这次是查询,所以是GetRequest2)发送请求,得到结果。因为是查询,这里调用client.get()方法3)解析结果,就是对JSON做反序列化5.2.2.完整代码在hotel-demo的HotelDocumentTest测试类中,编写单元测试:@Test
void testGetDocumentById() throws IOException {
// 1.准备Request
GetRequest request = new GetRequest("hotel", "61082");
// 2.发送请求,得到响应
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析响应结果
String json = response.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println(hotelDoc);
}
5.3.删除文档删除的DSL为是这样的:DELETE /hotel/_doc/{id}
与查询相比,仅仅是请求方式从DELETE变成GET,可以想象Java代码应该依然是三步走:1)准备Request对象,因为是删除,这次是DeleteRequest对象。要指定索引库名和id2)准备参数,无参3)发送请求。因为是删除,所以是client.delete()方法在hotel-demo的HotelDocumentTest测试类中,编写单元测试:@Test
void testDeleteDocument() throws IOException {
// 1.准备Request
DeleteRequest request = new DeleteRequest("hotel", "61083");
// 2.发送请求
client.delete(request, RequestOptions.DEFAULT);
}
5.4.修改文档5.4.1.语法说明修改我们讲过两种方式:全量修改:本质是先根据id删除,再新增增量修改:修改文档中的指定字段值在RestClient的API中,全量修改与新增的API完全一致,判断依据是ID:如果新增时,ID已经存在,则修改如果新增时,ID不存在,则新增这里不再赘述,我们主要关注增量修改。代码示例如图:与之前类似,也是三步走:1)准备Request对象。这次是修改,所以是UpdateRequest2)准备参数。也就是JSON文档,里面包含要修改的字段3)更新文档。这里调用client.update()方法5.4.2.完整代码在hotel-demo的HotelDocumentTest测试类中,编写单元测试:@Test
void testUpdateDocument() throws IOException {
// 1.准备Request
UpdateRequest request = new UpdateRequest("hotel", "61083");
// 2.准备请求参数
request.doc(
"price", "952",
"starName", "四钻"
);
// 3.发送请求
client.update(request, RequestOptions.DEFAULT);
}
5.5.批量导入文档案例需求:利用BulkRequest批量将数据库数据导入到索引库中。步骤如下: 利用mybatis-plus查询酒店数据
将查询到的酒店数据(Hotel)转换为文档类型数据(HotelDoc)
利用JavaRestClient中的BulkRequest批处理,实现批量新增文档 5.5.1.语法说明批量处理BulkRequest,其本质就是将多个普通的CRUD请求组合在一起发送。其中提供了一个add方法,用来添加其他请求:可以看到,能添加的请求包括:IndexRequest,也就是新增UpdateRequest,也就是修改DeleteRequest,也就是删除因此Bulk中添加了多个IndexRequest,就是批量新增功能了。示例:其实还是三步走:1)创建Request对象。这里是BulkRequest2)准备参数。批处理的参数,就是其它Request对象,这里就是多个IndexRequest3)发起请求。这里是批处理,调用的方法为client.bulk()方法我们在导入酒店数据时,将上述代码改造成for循环处理即可。5.5.2.完整代码在hotel-demo的HotelDocumentTest测试类中,编写单元测试:@Test
void testBulkRequest() throws IOException {
// 批量查询酒店数据
List<Hotel> hotels = hotelService.list();
// 1.创建Request
BulkRequest request = new BulkRequest();
// 2.准备参数,添加多个新增的Request
for (Hotel hotel : hotels) {
// 2.1.转换为文档类型HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 2.2.创建新增文档的Request对象
request.add(new IndexRequest("hotel")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc), XContentType.JSON));
}
// 3.发送请求
client.bulk(request, RequestOptions.DEFAULT);
}
5.6.小结文档操作的基本步骤:初始化RestHighLevelClient创建XxxRequest。XXX是Index、Get、Update、Delete、Bulk准备参数(Index、Update、Bulk时需要)发送请求。调用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk解析结果(Get时需要)分布式搜索引擎在昨天的学习中,我们已经导入了大量数据到elasticsearch中,实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。所以今天,我们研究下elasticsearch的数据搜索功能。我们会分别使用DSL和RestClient实现搜索。1.DSL查询文档elasticsearch的查询依然是基于JSON风格的DSL来实现的。1.1.DSL查询分类Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括:查询所有:查询出所有数据,一般测试用。例如:match_all全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
match_querymulti_match_query精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
idsrangeterm地理(geo)查询:根据经纬度查询。例如:
geo_distancegeo_bounding_box复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
boolfunction_score查询的语法基本一致:GET /indexName/_search
{
"query": {
"查询类型": {
"查询条件": "条件值"
}
}
}
我们以查询所有为例,其中:查询类型为match_all没有查询条件// 查询所有
GET /indexName/_search
{
"query": {
"match_all": {
}
}
}
其它查询无非就是查询类型、查询条件的变化。1.2.全文检索查询1.2.1.使用场景全文检索查询的基本流程如下:对用户搜索的内容做分词,得到词条根据词条去倒排索引库中匹配,得到文档id根据文档id找到文档,返回给用户比较常用的场景包括:商城的输入框搜索百度输入框搜索例如京东:因为是拿着词条去匹配,因此参与搜索的字段也必须是可分词的text类型的字段。1.2.2.基本语法常见的全文检索查询包括:match查询:单字段查询multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件match查询语法如下:GET /indexName/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
mulit_match语法如下:GET /indexName/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"]
}
}
}
1.2.3.示例match查询示例:multi_match查询示例:可以看到,两种查询结果是一样的,为什么?因为我们将brand、name、business值都利用copy_to复制到了all字段中。因此你根据三个字段搜索,和根据all字段搜索效果当然一样了。但是,搜索字段越多,对查询性能影响越大,因此建议采用copy_to,然后单字段查询的方式。1.2.4.总结match和multi_match的区别是什么?match:根据一个字段查询multi_match:根据多个字段查询,参与查询字段越多,查询性能越差1.3.精准查询精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:term:根据词条精确值查询range:根据值的范围查询1.3.1.term查询因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是不分词的词条。查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。语法说明:// term查询
GET /indexName/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
示例:当我搜索的是精确词条时,能正确查询出结果:但是,当我搜索的内容不是词条,而是多个词语形成的短语时,反而搜索不到:1.3.2.range查询范围查询,一般应用在对数值类型做范围过滤的时候。比如做价格范围过滤。基本语法:// range查询
GET /indexName/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10, // 这里的gte代表大于等于,gt则代表大于
"lte": 20 // lte代表小于等于,lt则代表小于
}
}
}
}
示例:1.3.3.总结精确查询常见的有哪些?term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段range查询:根据数值范围查询,可以是数值、日期的范围1.4.地理坐标查询所谓的地理坐标查询,其实就是根据经纬度查询,官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-queries.html常见的使用场景包括:携程:搜索我附近的酒店滴滴:搜索我附近的出租车微信:搜索我附近的人附近的酒店:附近的车:1.4.1.矩形范围查询矩形范围查询,也就是geo_bounding_box查询,查询坐标落在某个矩形范围的所有文档:查询时,需要指定矩形的左上、右下两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。语法如下:// 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
}
}
}
}
}
这种并不符合“附近的人”这样的需求,所以我们就不做了。1.4.2.附近查询附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。换句话来说,在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件:语法说明:// geo_distance 查询
GET /indexName/_search
{
"query": {
"geo_distance": {
"distance": "15km", // 半径
"FIELD": "31.21,121.5" // 圆心
}
}
}
示例:我们先搜索陆家嘴附近15km的酒店:发现共有47家酒店。然后把半径缩短到3公里:可以发现,搜索到的酒店数量减少到了5家。1.5.复合查询复合(compound)查询:复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑。常见的有两种:fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索1.5.1.相关性算分当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。例如,我们搜索 “虹桥如家”,结果如下:[
{
"_score" : 17.850193,
"_source" : {
"name" : "虹桥如家酒店真不错",
}
},
{
"_score" : 12.259849,
"_source" : {
"name" : "外滩如家酒店真不错",
}
},
{
"_score" : 11.91091,
"_source" : {
"name" : "迪士尼如家酒店真不错",
}
}
]
在elasticsearch中,早期使用的打分算法是TF-IDF算法,公式如下:在后来的5.1版本升级中,elasticsearch将算法改进为BM25算法,公式如下:TF-IDF算法有一各缺陷,就是词条频率越高,文档得分也会越高,单个词条对文档影响较大。而BM25则会让单个词条的算分有一个上限,曲线更加平滑:小结:elasticsearch会根据词条和文档的相关度做打分,算法由两种:TF-IDF算法BM25算法,elasticsearch5.1版本后采用的算法1.5.2.算分函数查询根据相关度打分是比较合理的需求,但合理的不一定是产品经理需要的。以百度为例,你搜索的结果中,并不是相关度越高排名越靠前,而是谁掏的钱多排名就越靠前。如图:要想认为控制相关性算分,就需要利用elasticsearch中的function score 查询了。1)语法说明function score 查询中包含四部分内容:原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)过滤条件:filter部分,符合该条件的文档才会重新算分算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数
weight:函数结果是常量field_value_factor:以文档中的某个字段值作为函数结果random_score:以随机数作为函数结果script_score:自定义算分函数算法运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
multiply:相乘replace:用function score替换query score其它,例如:sum、avg、max、minfunction score的运行流程如下:1)根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)2)根据过滤条件,过滤文档3)符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)4)将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。因此,其中的关键点是:过滤条件:决定哪些文档的算分被修改算分函数:决定函数算分的算法运算模式:决定最终算分结果2)示例需求:给“如家”这个品牌的酒店排名靠前一些翻译一下这个需求,转换为之前说的四个要点:原始条件:不确定,可以任意变化过滤条件:brand = “如家”算分函数:可以简单粗暴,直接给固定的算分结果,weight运算模式:比如求和因此最终的DSL语句如下:GET /hotel/_search
{
"query": {
"function_score": {
"query": {
.... }, // 原始查询,可以是任意条件
"functions": [ // 算分函数
{
"filter": { // 满足的条件,品牌必须是如家
"term": {
"brand": "如家"
}
},
"weight": 2 // 算分权重为2
}
],
"boost_mode": "sum" // 加权模式,求和
}
}
}
测试,在未添加算分函数时,如家得分如下:添加了算分函数后,如家得分就提升了:3)小结function score query定义的三要素是什么?过滤条件:哪些文档要加分算分函数:如何计算function score加权方式:function score 与 query score如何运算1.5.3.布尔查询布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:must:必须匹配每个子查询,类似“与”should:选择性匹配子查询,类似“或”must_not:必须不匹配,不参与算分,类似“非”filter:必须匹配,不参与算分比如在搜索酒店时,除了关键字搜索外,我们还可能根据品牌、价格、城市等字段做过滤:每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询了。需要注意的是,搜索时,参与打分的字段越多,查询的性能也越差。因此这种多条件查询时,建议这样做:搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分其它过滤条件,采用filter查询。不参与算分1)语法示例:GET /hotel/_search
{
"query": {
"bool": {
"must": [
{"term": {"city": "上海" }}
],
"should": [
{"term": {"brand": "皇冠假日" }},
{"term": {"brand": "华美达" }}
],
"must_not": [
{ "range": { "price": { "lte": 500 } }}
],
"filter": [
{ "range": {"score": { "gte": 45 } }}
]
}
}
}
2)示例需求:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。分析:名称搜索,属于全文检索查询,应该参与算分。放到must中价格不高于400,用range查询,属于过滤条件,不参与算分。放到must_not中周围10km范围内,用geo_distance查询,属于过滤条件,不参与算分。放到filter中3)小结bool查询有几种逻辑关系?must:必须匹配的条件,可以理解为“与”should:选择性匹配的条件,可以理解为“或”must_not:必须不匹配的条件,不参与打分filter:必须匹配的条件,不参与打分2.搜索结果处理搜索的结果可以按照用户指定的方式去处理或展示。2.1.排序elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。2.1.1.普通字段排序keyword、数值、日期类型排序的语法基本一致。语法:GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": "desc"
// 排序字段、排序方式ASC、DESC
}
]
}
排序条件是一个数组,也就是可以写多个排序条件。按照声明的顺序,当第一个条件相等时,再按照第二个条件排序,以此类推示例:需求描述:酒店数据按照用户评价(score)降序排序,评价相同的按照价格(price)升序排序2.1.2.地理坐标排序地理坐标排序略有不同。语法说明:GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance" : {
"FIELD" : "纬度,经度", // 文档中geo_point类型的字段名、目标坐标点
"order" : "asc", // 排序方式
"unit" : "km" // 排序的距离单位
}
}
]
}
这个查询的含义是:指定一个坐标,作为目标点计算每一个文档中,指定字段(必须是geo_point类型)的坐标 到目标点的距离是多少根据距离排序示例:需求描述:实现对酒店数据按照到你的位置坐标的距离升序排序提示:获取你的位置的经纬度的方式:https://lbs.amap.com/demo/jsapi-v2/example/map/click-to-get-lnglat/假设我的位置是:31.034661,121.612282,寻找我周围距离最近的酒店。2.2.分页elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:from:从第几个文档开始size:总共查询几个文档类似于mysql中的limit ?, ?2.2.1.基本的分页分页的基本语法如下:GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}
2.2.2.深度分页问题现在,我要查询990~1000的数据,查询逻辑要这么写:GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 990, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"price": "asc"}
]
}
这里是查询990开始的数据,也就是 第990~第1000条 数据。不过,elasticsearch内部分页时,必须先查询 0~1000条,然后截取其中的990 ~ 1000的这10条:查询TOP1000,如果es是单点模式,这并无太大影响。但是elasticsearch将来一定是集群,例如我集群有5个节点,我要查询TOP1000的数据,并不是每个节点查询200条就可以了。因为节点A的TOP200,在另一个节点可能排到10000名以外了。因此要想获取整个集群的TOP1000,必须先查询出每个节点的TOP1000,汇总结果后,重新排名,重新截取TOP1000。那如果我要查询9900~10000的数据呢?是不是要先查询TOP10000呢?那每个节点都要查询10000条?汇总到内存中?当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力,因此elasticsearch会禁止from+ size 超过10000的请求。针对深度分页,ES提供了两种解决方案,官方文档:search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。scroll:原理将排序后的文档id形成快照,保存在内存。官方已经不推荐使用。2.2.3.小结分页查询的常见实现方案以及优缺点:from + size:
优点:支持随机翻页缺点:深度分页问题,默认查询上限(from + size)是10000场景:百度、京东、谷歌、淘宝这样的随机翻页搜索after search:
优点:没有查询上限(单次查询的size不超过10000)缺点:只能向后逐页查询,不支持随机翻页场景:没有随机翻页需求的搜索,例如手机向下滚动翻页scroll:
优点:没有查询上限(单次查询的size不超过10000)缺点:会有额外内存消耗,并且搜索结果是非实时的场景:海量数据的获取和迁移。从ES7.1开始不推荐,建议用 after search方案。2.3.高亮2.3.1.高亮原理什么是高亮显示呢?我们在百度,京东搜索时,关键字会变成红色,比较醒目,这叫高亮显示:高亮显示的实现分为两步:1)给文档中的所有关键字都添加一个标签,例如<em>标签2)页面给<em>标签编写CSS样式2.3.2.实现高亮高亮的语法:GET /hotel/_search
{
"query": {
"match": {
"FIELD": "TEXT" // 查询条件,高亮一定要使用全文检索查询
}
},
"highlight": {
"fields": { // 指定要高亮的字段
"FIELD": {
"pre_tags": "<em>",
// 用来标记高亮字段的前置标签
"post_tags": "</em>" // 用来标记高亮字段的后置标签
}
}
}
}
注意:高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false示例:2.4.总结查询的DSL是一个大的JSON对象,包含下列属性:query:查询条件from和size:分页条件sort:排序条件highlight:高亮条件示例:3.RestClient查询文档文档的查询同样适用昨天学习的 RestHighLevelClient对象,基本步骤包括:1)准备Request对象2)准备请求参数3)发起请求4)解析响应3.1.快速入门我们以match_all查询为例3.1.1.发起查询请求代码解读:第一步,创建SearchRequest对象,指定索引库名第二步,利用request.source()构建DSL,DSL中可以包含查询、分页、排序、高亮等
query():代表查询条件,利用QueryBuilders.matchAllQuery()构建一个match_all查询的DSL第三步,利用client.search()发送请求,得到响应这里关键的API有两个,一个是request.source(),其中包含了查询、排序、分页、高亮等所有功能:另一个是QueryBuilders,其中包含match、term、function_score、bool等各种查询:3.1.2.解析响应响应结果的解析:elasticsearch返回的结果是一个JSON字符串,结构包含:hits:命中的结果
total:总条数,其中的value是具体的总条数值max_score:所有结果中得分最高的文档的相关性算分hits:搜索结果的文档数组,其中的每个文档都是一个json对象
_source:文档中的原始数据,也是json对象因此,我们解析响应结果,就是逐层解析JSON字符串,流程如下:SearchHits:通过response.getHits()获取,就是JSON中的最外层的hits,代表命中的结果
SearchHits#getTotalHits().value:获取总条数信息SearchHits#getHits():获取SearchHit数组,也就是文档数组
SearchHit#getSourceAsString():获取文档结果中的_source,也就是原始的json文档数据3.1.3.完整代码完整代码如下:@Test
void testMatchAll() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
request.source()
.query(QueryBuilders.matchAllQuery());
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
private void handleResponse(SearchResponse response) {
// 4.解析响应
SearchHits searchHits = response.getHits();
// 4.1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 4.2.文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println("hotelDoc = " + hotelDoc);
}
}
3.1.4.小结查询的基本步骤是: 创建SearchRequest对象
准备Request.source(),也就是DSL。 ① QueryBuilders来构建查询条件 ② 传入Request.source() 的 query() 方法
发送请求,得到结果
解析结果(参考JSON结果,从外到内,逐层解析) 3.2.match查询全文检索的match和multi_match查询与match_all的API基本一致。差别是查询条件,也就是query的部分。因此,Java代码上的差异主要是request.source().query()中的参数了。同样是利用QueryBuilders提供的方法:而结果解析代码则完全一致,可以抽取并共享。完整代码如下:@Test
void testMatch() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
request.source()
.query(QueryBuilders.matchQuery("all", "如家"));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
3.3.精确查询精确查询主要是两者:term:词条精确匹配range:范围查询与之前的查询相比,差异同样在查询条件,其它都一样。查询条件构造的API如下:3.4.布尔查询布尔查询是用must、must_not、filter等方式组合其它查询,代码示例如下:可以看到,API与其它查询的差别同样是在查询条件的构建,QueryBuilders,结果解析等其他代码完全不变。完整代码如下:@Test
void testBool() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.准备BooleanQuery
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2.2.添加term
boolQuery.must(QueryBuilders.termQuery("city", "杭州"));
// 2.3.添加range
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
request.source().query(boolQuery);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
3.5.排序、分页搜索结果的排序和分页是与query同级的参数,因此同样是使用request.source()来设置。对应的API如下:完整代码示例:@Test
void testPageAndSort() throws IOException {
// 页码,每页大小
int page = 1, size = 5;
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchAllQuery());
// 2.2.排序 sort
request.source().sort("price", SortOrder.ASC);
// 2.3.分页 from、size
request.source().from((page - 1) * size).size(5);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
3.6.高亮高亮的代码与之前代码差异较大,有两点:查询的DSL:其中除了查询条件,还需要添加高亮条件,同样是与query同级。结果解析:结果除了要解析_source文档数据,还要解析高亮结果3.6.1.高亮请求构建高亮请求的构建API如下:上述代码省略了查询条件部分,但是大家不要忘了:高亮查询必须使用全文检索查询,并且要有搜索关键字,将来才可以对关键字高亮。完整代码如下:@Test
void testHighlight() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchQuery("all", "如家"));
// 2.2.高亮
request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
3.6.2.高亮结果解析高亮的结果与查询的文档结果默认是分离的,并不在一起。因此解析高亮的代码需要额外处理:代码解读:第一步:从结果中获取source。hit.getSourceAsString(),这部分是非高亮结果,json字符串。还需要反序列为HotelDoc对象第二步:获取高亮结果。hit.getHighlightFields(),返回值是一个Map,key是高亮字段名称,值是HighlightField对象,代表高亮值第三步:从map中根据高亮字段名称,获取高亮字段值对象HighlightField第四步:从HighlightField中获取Fragments,并且转为字符串。这部分就是真正的高亮字符串了第五步:用高亮的结果替换HotelDoc中的非高亮结果完整代码如下:private void handleResponse(SearchResponse response) {
// 4.解析响应
SearchHits searchHits = response.getHits();
// 4.1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 4.2.文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, 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);
}
}
System.out.println("hotelDoc = " + hotelDoc);
}
}
4.黑马旅游案例下面,我们通过黑马旅游的案例来实战演练下之前学习的知识。我们实现四部分功能:酒店搜索和分页酒店结果过滤我周边的酒店酒店竞价排名启动我们提供的hotel-demo项目,其默认端口是8089,访问http://localhost:8089,就能看到项目页面了:4.1.酒店搜索和分页案例需求:实现黑马旅游的酒店搜索功能,完成关键字搜索和分页4.1.1.需求分析在项目的首页,有一个大大的搜索框,还有分页按钮:点击搜索按钮,可以看到浏览器控制台发出了请求:请求参数如下:由此可以知道,我们这个请求的信息如下:请求方式:POST请求路径:/hotel/list请求参数:JSON对象,包含4个字段:
key:搜索关键字page:页码size:每页大小sortBy:排序,目前暂不实现返回值:分页查询,需要返回分页结果PageResult,包含两个属性:
total:总条数List<HotelDoc>:当前页的数据因此,我们实现业务的流程如下:步骤一:定义实体类,接收请求参数的JSON对象步骤二:编写controller,接收页面的请求步骤三:编写业务实现,利用RestHighLevelClient实现搜索、分页4.1.2.定义实体类实体类有两个,一个是前端的请求参数实体,一个是服务端应该返回的响应结果实体。1)请求参数前端请求的json结构如下:{
"key": "搜索关键字",
"page": 1,
"size": 3,
"sortBy": "default"
}
因此,我们在cn.itcast.hotel.pojo包下定义一个实体类:package cn.itcast.hotel.pojo;
import lombok.Data;
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
}
2)返回值分页查询,需要返回分页结果PageResult,包含两个属性:total:总条数List<HotelDoc>:当前页的数据因此,我们在cn.itcast.hotel.pojo中定义返回结果:package cn.itcast.hotel.pojo;
import lombok.Data;
import java.util.List;
@Data
public class PageResult {
private Long total;
private List<HotelDoc> hotels;
public PageResult() {
}
public PageResult(Long total, List<HotelDoc> hotels) {
this.total = total;
this.hotels = hotels;
}
}
4.1.3.定义controller定义一个HotelController,声明查询接口,满足下列要求:请求方式:Post请求路径:/hotel/list请求参数:对象,类型为RequestParam返回值:PageResult,包含两个属性
Long total:总条数List<HotelDoc> hotels:酒店数据因此,我们在cn.itcast.hotel.web中定义HotelController:@RestController
@RequestMapping("/hotel")
public class HotelController {
@Autowired
private IHotelService hotelService;
// 搜索酒店数据
@PostMapping("/list")
public PageResult search(@RequestBody RequestParams params){
return hotelService.search(params);
}
}
4.1.4.实现搜索业务我们在controller调用了IHotelService,并没有实现该方法,因此下面我们就在IHotelService中定义方法,并且去实现业务逻辑。1)在cn.itcast.hotel.service中的IHotelService接口中定义一个方法:/**
* 根据关键字搜索酒店信息
* @param params 请求参数对象,包含用户输入的关键字
* @return 酒店文档列表
*/
PageResult search(RequestParams params);
2)实现搜索业务,肯定离不开RestHighLevelClient,我们需要把它注册到Spring中作为一个Bean。在cn.itcast.hotel中的HotelDemoApplication中声明这个Bean:@Bean
public RestHighLevelClient client(){
return
new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
3)在cn.itcast.hotel.service.impl中的HotelService中实现search方法:@Override
public PageResult search(RequestParams params) {
try {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
String key = params.getKey();
if (key == null
"".equals(key)) {
request.source().query(QueryBuilders.matchAllQuery());
} else {
request.source().query(QueryBuilders.matchQuery("all", key));
}
// 2.2.分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 结果解析
private PageResult handleResponse(SearchResponse response) {
// 4.解析响应
SearchHits searchHits = response.getHits();
// 4.1.获取总条数
long total = searchHits.getTotalHits().value;
// 4.2.文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
List<HotelDoc> hotels = new ArrayList<>();
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
// 放入集合
hotels.add(hotelDoc);
}
// 4.4.封装返回
return new PageResult(total, hotels);
}
4.2.酒店结果过滤需求:添加品牌、城市、星级、价格等过滤功能4.2.1.需求分析在页面搜索框下面,会有一些过滤项:传递的参数如图:包含的过滤条件有:brand:品牌值city:城市minPrice~maxPrice:价格范围starName:星级我们需要做两件事情:修改请求参数的对象RequestParams,接收上述参数修改业务逻辑,在搜索条件之外,添加一些过滤条件4.2.2.修改实体类修改在cn.itcast.hotel.pojo包下的实体类RequestParams:@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
// 下面是新增的过滤条件参数
private String city;
private String brand;
private String starName;
private Integer minPrice;
private Integer maxPrice;
}
4.2.3.修改搜索业务在HotelService的search方法中,只有一个地方需要修改:requet.source().query( … )其中的查询条件。在之前的业务中,只有match查询,根据关键字搜索,现在要添加条件过滤,包括:品牌过滤:是keyword类型,用term查询星级过滤:是keyword类型,用term查询价格过滤:是数值类型,用range查询城市过滤:是keyword类型,用term查询多个查询条件组合,肯定是boolean查询来组合:关键字搜索放到must中,参与算分其它过滤条件放到filter中,不参与算分因为条件构建的逻辑比较复杂,这里先封装为一个函数:buildBasicQuery的代码如下:private void buildBasicQuery(RequestParams params, SearchRequest request) {
// 1.构建BooleanQuery
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2.关键字搜索
String key = params.getKey();
if (key == null
"".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
boolQuery.must(QueryBuilders.matchQuery("all", key));
}
// 3.城市条件
if (params.getCity() != null && !params.getCity().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
// 4.品牌条件
if (params.getBrand() != null && !params.getBrand().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
// 5.星级条件
if (params.getStarName() != null && !params.getStarName().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 6.价格
if (params.getMinPrice() != null && params.getMaxPrice() != null) {
boolQuery.filter(QueryBuilders
.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice())
);
}
// 7.放入source
request.source().query(boolQuery);
}
4.3.我周边的酒店需求:我附近的酒店4.3.1.需求分析在酒店列表页的右侧,有一个小地图,点击地图的定位按钮,地图会找到你所在的位置:并且,在前端会发起查询请求,将你的坐标发送到服务端:我们要做的事情就是基于这个location坐标,然后按照距离对周围酒店排序。实现思路如下:修改RequestParams参数,接收location字段修改search方法业务逻辑,如果location有值,添加根据geo_distance排序的功能4.3.2.修改实体类修改在cn.itcast.hotel.pojo包下的实体类RequestParams:package cn.itcast.hotel.pojo;
import lombok.Data;
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
private String city;
private String brand;
private String starName;
private Integer minPrice;
private Integer maxPrice;
// 我当前的地理坐标
private String location;
}
4.3.3.距离排序API我们以前学习过排序功能,包括两种:普通字段排序地理坐标排序我们只讲了普通字段排序对应的java写法。地理坐标排序只学过DSL语法,如下:GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": "asc"
},
{
"_geo_distance" : {
"FIELD" : "纬度,经度",
"order" : "asc",
"unit" : "km"
}
}
]
}
对应的java代码示例:4.3.4.添加距离排序在cn.itcast.hotel.service.impl的HotelService的search方法中,添加一个排序功能:完整代码:@Override
public PageResult search(RequestParams params) {
try {
// 1.准备Request
SearchRequest request = new SearchRequest("hotel");
// 2.准备DSL
// 2.1.query
buildBasicQuery(params, request);
// 2.2.分页
int page = params.getPage();
int size = params.getSize();
request.source().from((page - 1) * size).size(size);
// 2.3.排序
String location = params.getLocation();
if (location != null && !location.equals("")) {
request.source().sort(SortBuilders
.geoDistanceSort("location", new GeoPoint(location))
.order(SortOrder.ASC)
.unit(DistanceUnit.KILOMETERS)
);
}
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
4.3.5.排序距离显示重启服务后,测试我的酒店功能:发现确实可以实现对我附近酒店的排序,不过并没有看到酒店到底距离我多远,这该怎么办?排序完成后,页面还要获取我附近每个酒店的具体距离值,这个值在响应结果中是独立的:因此,我们在结果解析阶段,除了解析source部分以外,还要得到sort部分,也就是排序的距离,然后放到响应结果中。我们要做两件事:修改HotelDoc,添加排序距离字段,用于页面显示修改HotelService类中的handleResponse方法,添加对sort值的获取1)修改HotelDoc类,添加距离字段package cn.itcast.hotel.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
// 排序时的 距离值
private Object distance;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
}
}
2)修改HotelService中的handleResponse方法重启后测试,发现页面能成功显示距离了:4.4.酒店竞价排名需求:让指定的酒店在搜索结果中排名置顶4.4.1.需求分析要让指定酒店在搜索结果中排名置顶,效果如图:页面会给指定的酒店添加广告标记。那怎样才能让指定的酒店排名置顶呢?我们之前学习过的function_score查询可以影响算分,算分高了,自然排名也就高了。而function_score包含3个要素:过滤条件:哪些文档要加分算分函数:如何计算function score加权方式:function score 与 query score如何运算这里的需求是:让指定酒店排名靠前。因此我们需要给这些酒店添加一个标记,这样在过滤条件中就可以根据这个标记来判断,是否要提高算分。比如,我们给酒店添加一个字段:isAD,Boolean类型:true:是广告false:不是广告这样function_score包含3个要素就很好确定了:过滤条件:判断isAD 是否为true算分函数:我们可以用最简单暴力的weight,固定加权值加权方式:可以用默认的相乘,大大提高算分因此,业务的实现步骤包括:给HotelDoc类添加isAD字段,Boolean类型挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true修改search方法,添加function score功能,给isAD值为true的酒店增加权重4.4.2.修改HotelDoc实体给cn.itcast.hotel.pojo包下的HotelDoc类添加isAD字段:4.4.3.添加广告标记接下来,我们挑几个酒店,添加isAD字段,设置为true:POST /hotel/_update/1902197537
{
"doc": {
"isAD": true
}
}
POST /hotel/_update/2056126831
{
"doc": {
"isAD": true
}
}
POST /hotel/_update/1989806195
{
"doc": {
"isAD": true
}
}
POST /hotel/_update/2056105938
{
"doc": {
"isAD": true
}
}
4.4.4.添加算分函数查询接下来我们就要修改查询条件了。之前是用的boolean 查询,现在要改成function_socre查询。function_score查询结构如下:对应的JavaAPI如下:我们可以将之前写的boolean查询作为原始查询条件放到query中,接下来就是添加过滤条件、算分函数、加权模式了。所以原来的代码依然可以沿用。修改cn.itcast.hotel.service.impl包下的HotelService类中的buildBasicQuery方法,添加算分函数查询:private void buildBasicQuery(RequestParams params, SearchRequest request) {
// 1.构建BooleanQuery
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 关键字搜索
String key = params.getKey();
if (key == null
"".equals(key)) {
boolQuery.must(QueryBuilders.matchAllQuery());
} else {
boolQuery.must(QueryBuilders.matchQuery("all", key));
}
// 城市条件
if (params.getCity() != null && !params.getCity().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("city", params.getCity()));
}
// 品牌条件
if (params.getBrand() != null && !params.getBrand().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
}
// 星级条件
if (params.getStarName() != null && !params.getStarName().equals("")) {
boolQuery.filter(QueryBuilders.termQuery("starName", params.getStarName()));
}
// 价格
if (params.getMinPrice() != null && params.getMaxPrice() != null) {
boolQuery.filter(QueryBuilders
.rangeQuery("price")
.gte(params.getMinPrice())
.lte(params.getMaxPrice())
);
}
// 2.算分控制
FunctionScoreQueryBuilder functionScoreQuery =
QueryBuilders.functionScoreQuery(
// 原始查询,相关性算分的查询
boolQuery,
// function score的数组
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
// 其中的一个function score 元素
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
// 过滤条件
QueryBuilders.termQuery("isAD", true),
// 算分函数
ScoreFunctionBuilders.weightFactorFunction(10)
)
});
request.source().query(functionScoreQuery);
}

我要回帖

更多关于 附近人用不了怎么回事 的文章

 

随机推荐