DSL语法、搜索结果处理

简介: 本文介绍了Elasticsearch(ES)的数据搜索功能,涵盖DSL查询语法、全文检索、精确查询、地理坐标查询及复合查询等类型。通过RestClient实现搜索,并结合黑马旅游案例,演示了酒店搜索、过滤、竞价排名等功能的实现过程,帮助读者掌握ES在实际项目中的应用。

在前面的学习中,笔者带领大家完成海量数据导入ES,实现了ES基本的存储功能,但是我们知道ES最擅长的还是搜索、数据分析。所以本节笔者将继续带领大家研究一下ES的数据搜索功能,同上节一样,继续分别采用DSL和RestClient实现搜索。
1.DSL查询文档
elasticsearch的查询依然是基于JSON风格的DSL来实现的。
1.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
查询的语法基本一致:
我们以查询所有为例,其中:
查询类型为match_all
没有查询条件
其它查询无非就是查询类型、查询条件的变化。
1.2.全文检索查询
1.2.1.使用场景
全文检索查询的基本流程如下:
对用户搜索的内容做分词,得到词条
根据词条去倒排索引库中匹配,得到文档id
根据文档id找到文档,返回给用户
比较常用的场景包括:
商城的输入框搜索
百度输入框搜索
例如京东:

上海

你好,请登录免费注册

我的订单

客户服务

京东会员

企业采购

我的京东

西门子冰箱

我的购物车

生鲜五折购

食品饮料MATE40RS游戏新品小米善存钙时令生鲜服饰520当季限定

京东

京东国际

京东生鲜

京东超市

PLUS会员

品牌闪购

京东云

京东家电

秒杀

优惠券

拍卖


因为是拿着词条去匹配,因此参与搜索的字段也必须是可分词的text类型的字段。
1.2.2.基本语法
常见的全文检索查询包括:
match查询:单字段查询
multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件
match查询语法如下:
mulit_match语法如下:
1.2.3.示例
match查询示例:

15

"MAX_SCORE":6.512146,

#MATCH查询

"HITS":[

16

GET/HOTEL/_SEARCH

17

INDEX":"HOTEL".

18

"QUERY":

19

_TYPE":"-DOC"

"MATCH":F

20

ID*:"434082*.

"ALL":"外滩如家"

_SCORE":6.512146,

_SOURCE":1 : 1 SOURCE"SOURCE

"ADDRESS":"复兴东路260号",

"BRAND":"如家",

"BUSINESS":"豫园地区",

"CITY":"上海",

"ID":434082,

:"31.220706, 121.498769".

"LOCATION"

如家酒店NEO(上海外滩城隍庙小南门地铁站店)

'NAME"

38

"PIC":"HTTPS://M.TUNIUCDN.COM/FB2/T1/G6/M00/52/B6/CII

-U13EXLGIDHFZAAIG-5CEWDEAAGRFQNNIVOAAGCT627_W200_H200_C1_TE

-JPG"

"PRICE":392.

3733

"SCORE":44,

"STARNAME":"二钻"

34

35



multi_match查询示例:

'434082"

:4.755627.

SCORE":

SOURCE

#MATCH查询

"复兴东路260号".

'ADDRESS

GET /HOTEL/_SEARCH

如家"

"BRAND"

J

":"豫园地区",

"BUSINESS'

"QUERY":{

:"上海",

"CITY":

"MATCH":

"ID":434082,

"ALL":"外滩如家"

"LOCATION"

31.220706, 121.498769",

如家

酒店:NEO(上海外滩城隍庙小南门地铁站店)"

"NAME'

"HTTPS://M.TUNIUCDN.COM/FB2/T1/G6/M00/52/B6/CII

"PIC"

-U13EXLGIDHFZAAIG-5CEWDEAAGRFQNNIVOAAGCT627_W200_H200_C1_T0

JPG"

# MULTI MATCH查询

"PRICE":392

GET/HOTEL/_SEARCH

"SCORE":44,

:"二钻"

STARNAME

'QUERY':

"MULTI_MATCH":

"QUERY":"外滩如家",

"FIELDS":["BRAND", "NAME", "BUSINESS"]

_INDEX":"HOTEL".

_TYPE":"-DOC"

39

ID":"60487",

40

:4.527972,

SCORE'*


可以看到,两种查询结果是一样的:因为我们将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查询
因为精确查询的字段搜是不分词的字段,因此查询的条件也必须是不分词的词条。查询时,用户输入的内容跟自动值完全匹配时才认为符合条件。如果用户输入的内容过多,反而搜索不到数据。语法说明:
示例:当我搜索的是精确词条时,能正确查询出结果:

"MAX_SCORE":1.004499,

15

16

17

18

_INDEX":"HOTEL",

#TERM查询

-----IS699994

GET /HOTEL/_SEARCH

_SCORE":1.004499,

QUERY":1

SOURCE

TERM":1

"静安交通路40号",

''ADDRESS

"CITY":

"7天酒店",

"BRAND""

"上海"

"VALUE

"BUSINESS":"四川北路商业区",

"CITY":"上海",

"ID":36934,

"LOCATION" : "31.251433, 121.47522",

"NAME":"7天连锁酒店(上海宝山路地铁站店)",

"PIC":"HTTPS://M.TUNIUCDN.COM/FB2/T1/G1/M

/CII9EVKYLRKIXO1VAAHGRXO_PUCAALCKQLD688AAE

_TO.JPG".

"PRICE":336.

"SCORE":37.

"二钻"

"STARNAME":


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

1234567

"TOOK":0,

"TIMED_OUT"

FALSE,

SHARDS":{

#TERM查询

"TOTAL":1,

GET /HOTEL/_SEARCH

"SUCCESSFUL" : 1,

"SKIPPED":0,

"QUERY":{

8

"FAILED":0

TERM

CITY":

"HITS":{

10

"VALUE":"杭州上海"

11

"TOTAL":

"VALUE":O

12

"EG"

"RELATION"

13

14

15

NU11,

"MAX  SCORE"

"HITS":[]

16

17

18 丁


1.3.2.range查询
范围查询,一般应用在对数值类型做范围过滤的时候。比如做价格范围过滤。基本语法:
示例:

20

'60223'

#RANGE查询

:1.0,

21

GET /HOTEL/_SEARCH

SCORE

22

SOURCE"

"静安华山路250号",

"ADDRESS

"QUERY":

23

"希尔顿".

"BRAND":

"RANGE":

24

"BUSINESS":"静安寺地区",

"PRICE":

25

"CITY":"上海",

26

"GTE":1000.

"ID": 60223,

27

''1TE":3000

"LOCATION" : "31.219306, 121.445427",

28

"NAME":"上海希尔顿酒店",

29

30

"PIC":"HTTPS:Z/M.TUNIUCDN.COM/FILEBR

LL

/9210E7442ACECEAF6E196D61FC3B6B1_W2

31

PRICE":2688,

32

:37

SCORE

"STARNAME":"五星级"

33

34


1.3.3.总结
精确查询常见的有哪些?
term查询:根据词条精确匹配,一般搜索keyword类型、数值类型、布尔类型、日期类型字段
range查询:根据数值范围查询,可以是数值、日期的范围
1.4.地理坐标查询
所谓的地理坐标查询,其实就是根据经纬度查询,官方文档:链接,常见的使用场景包括:
携程:搜索我附近的酒店
滴滴:搜索我附近的出租车
微信:搜索我附近的人
附近的酒店:

image.svg

图片加载失败

地图预览

六灶镇

ST23

上海中优城市万豪酒店

黑马程序员上海校区

S122

S123

S227

5324

上海宝华喜来登酒店

大团镇

S123

S227

G1503

S325

$221

高德地区

亚安镇


附近的车:

郭家

点我领车费

黑马程序员训练营(上

海校区)

为您推荐附近适合的上车点

航头路/航都路(路*.

口)东南侧

沪南公路

五一出行必看知识点!点我

上海市今日天气阴,17C3~23C

你将从黑马程序员训练营(上海校区)上车>

输入你的目的地


1.4.1.矩形范围查询
矩形范围查询,也就是geo_bounding_box查询,查询坐标落在某个矩形范围的所有文档:

华泾镇

周浦镇

闵行区

吃镇

S227

广东海镇

浙江镇

颜枝拍

S122

南镇

S123

吴泾镇

航头饼

新场镇

川路街道

金汇镇

S324

324


查询时,需要指定矩形的左上、右下两个点的坐标,然后画出一个矩形,落在该矩形内的都是符合条件的点。
语法如下:
这种并不符合“附近的人”这样的需求,所以我们就不做了。
1.4.2.附近查询
附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。
换句话来说,在地图上找一个点作为圆心,以指定距离为半径,画一个圆,落在圆内的坐标都算符合条件:

image.svg

图片加载失败

海滨新村

S106

杨行镇

外冈镇

即村镇

S224

S5

322

G40

横沙乡

高桥镇

酒塘新村

马隆缺

微沙港

G1501

口股行街道

S6

高东镇

临汾路

S322

五角场镇

S202

S221

横沙岛

G15

安字镇

高行缺

自阳路

大场镇

槐浦铁

香路镇

黄渡镇

G312

杨浦区

芷江西路

普陀区

江桥镇

海市

清东新区

华酒拍]

仙露新村街道

花桥街镇

1沙新镇

徐汇区

北蔡镇

自涂泾镇

龙柏街道虹梅路街道

G50

G1501

赵巷镇

古美路街

G50

周海钠

华泾镇

六灶镇

围行区

祝桥镇

梅陇镇

G60

S227

浦江镇

S224

三灶镇

S122

额桥轴

新桥镇

墨南镇

庆头镇

S123

老港镇

宣桥镇

厂关经换

新场镇

马杨华

黄路镇

车辆辆

川路街道


语法说明:
示例,我们先搜索陆家嘴附近15km的酒店:

AT

N1TS

"TOTAL":

11

DISTANCE查询

12

/HOTEL/_SEARCH

"VALUE":47

GET

7

13

"RELATION":""EG"

14

'QUERY":F

"MAX_SCORE":1.0

15

"GEO_DISTANCE":{

"HITS":[

16

DIST

LISTANCE":"15KM".

17

"LOCATION": "31.21, 121.5"

18

INDEX""

"HOTEL",

__DOC",

19

TYPE

"36934"

20

ID":

21

1.0元

SCORE'

22

SOURCE

"静安交通路40号",

23

ADDRESS"

"BRAND":"7天酒店",

24

"BUSINESS":"四川北路商业区",

25

"CITY":"上海",

26

27

"ID":36934,

"LOCATION" : "31.251433, 121.47522",

28


发现共有47家酒店。然后把半径缩短到3公里:

NITS

AT

#DISTANCE查询

11

'TOTAL"

GET /HOTEL/_SEARCH

12

"VALUE":SL

"RELATION":"EG"

13

"QUERY":{

14

子,

GEO_DISTANCE":

"MAX_SCORE":1.0

15

"DISTANCE":"3KM".

16

"HITS":

:"31.21,121.5"

J

17

"IN

"_INDEX":"HOTEL",

18

_TYPE":"_DOC",

19

'_ID":"60214",

20

21

_SCORE":1.0,

22

SOURCE":

"世纪大道88号(54楼办理入住)

"ADDRESS

23

"BRAND""

君悦"

24

"BUSINESS":"浦东陆家嘴金融贸易区",

25

"CITY":"上海",

26

"ID":6

60214,

27

"LOCATION" : "31.235152, 121.506082",

28


可以发现,搜索到的酒店数量减少到了5家。
1.5.复合查询
复合(compound)查询:复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑。常见的有两种:
fuction score:算分函数查询,可以控制文档相关性算分,控制文档排名
bool query:布尔查询,利用逻辑关系组合多个其它的查询,实现复杂搜索
1.5.1.相关性算分
当我们利用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。
例如,我们搜索 "虹桥如家",结果如下:
在elasticsearch中,早期使用的打分算法是TF-IDF算法,公式如下:

词条出现次数

TF(词条频率)

文档中词条总数

TF-IDF算法

文档总数

IDF(逆文档频率)  LOG(可含词条的文档总数

Z'TF(词条频率)*IDF(逆文档频率)

SCORE


在后来的5.1版本升级中,elasticsearch将算法改进为BM25算法,公式如下:

BM25算法

N-N+0.5

SCORE(Q,D):2"LOG(

1P

N+0.5

FI+KI(1-B+B.

IPBAN


TF-IDF算法有一各缺陷,就是词条频率越高,文档得分也会越高,单个词条对文档影响较大。而BM25则会让单个词条的算分有一个上限,曲线更加平滑:

TE SCORE

CLASSIC TF SCORE

BM25 TF SCORE

TERM FREQUENCY


小结:elasticsearch会根据词条和文档的相关度做打分,算法由两种:
TF-IDF算法
BM25算法,elasticsearch5.1版本后采用的算法
1.5.2.算分函数查询
根据相关度打分是比较合理的需求,但合理的不一定是产品经理需要的。
以百度为例,你搜索的结果中,并不是相关度越高排名越靠前,而是谁掏的钱多排名就越靠前。如图:

image.svg

图片加载失败

百度一下

JAVA培训

BAIGU百度

Q网页

口文库

口地图

国资讯

图片

知道

更多

采购

贴贴吧

口视频

百度为您找到相关结果约33,000,000个

搜索工具

名企

JAVA培训

优就业,专注JAVA培训15年,轻理论重实践,渐进

式学习,面对面讲授,切合企业用..

价格:0元试学

培训类型:JAVA培训

班型:重磅升级

适合人群:大专以上

广告

2021-05

保障


要想认为控制相关性算分,就需要利用elasticsearch中的function score 查询了。
1)语法说明

GET /HOTEL/_SEARCH

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

"QUERY":[

"FUNCTION_SCORE": {

过滤条件,符合条件的文档才会被重新算分

"MATCH":{"ALL":"外滩"了

"QUERY":I "MA

"FUNCTIONS": [

算分函数,算分函数的结果称为FUNCTION SCORE,将来会与QUERY

SCORE运算,得到新算分,常见的算分函数有:

WEIGHT:给一个常量值,作为函数结果(FUNCTION SCORE)

"FILTER": "TERM": {"ID": "1"J了,

FIELD VALUE FACTOR:用文档中的某个字段值作为函数结果

'WEIGHT":10

RANDOM_SCORE:随机生成一个值,作为函数结果

SCRIPT_SCORE:自定义计算公式结果作为函数结果

],

BOOST_MODE":'MULTIPLY

加权模式,定义FUNCTION SCORE与QUERY SCORE的运算方式,包括:

MULTIPLY:两者相乘.默认就是这个

REPLACE:用FUNCTION SCORE 替换 QUERY SCORE

其它:SUM,AVG,MAX,MIN


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、min
function score的运行流程如下:
1)根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
2)根据过滤条件,过滤文档
3)符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
4)将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。
因此,其中的关键点是:
过滤条件:决定哪些文档的算分被修改
算分函数:决定函数算分的算法
运算模式:决定最终算分结果
2)示例
需求:给“如家”这个品牌的酒店排名靠前一些,翻译一下这个需求,转换为之前说的四个要点:
原始条件:不确定,可以任意变化
过滤条件:brand = "如家"
算分函数:可以简单粗暴,直接给固定的算分结果,weight
运算模式:比如求和
因此最终的DSL语句如下:
测试,在未添加算分函数时,如家得分如下:

PIJUC

'SCORE

35,

'STARNAME'

#FUNCTION SCORE查询

GET /HOTEL/_SEARCH

INDEX":"HOTEL",

"QUERY":

PE": DOC",

TYPE

"FUNCTION_SCORE":{

"434082

ID'

原始查询

'QUERY:

3.8828926,

59

SCORE

"MATCH":

60-

SOURCE

"ALL":"外滩"

:"复兴东路260号",

?"ADDRESS"

61

"如家",

"BRAND":

62

"BUSINESS":"豫园地区",

63

"CITY":"上海",

64

"ID":434082,

65

LOCATION":"31.220706,121.498769"

"10C

66

"NAME":"如家酒店NEO(上海外滩城隍

67


image.svg

图片加载失败

添加了算分函数后,如家得分就提升了(注意weight位置,与filter平级):

1 /10LC1/_OCAI CIL

N1TS

AT

TOTAL"

'QUERY":{

"VALUE":3,3,

12

"FUNCTION SCORE":

"RELATION":"EQ"

13

'QUERY":

14

了,

"MATCH":

"MAX_SCORE" : 13.882893,

15

外滩"

ALL"

"HITS":[

16

17

"HOTEL"

INDEX"

18

对BRAND:"如家"的酒店加权

"FUNCTIONS":[

DOC"

TYPE

19

ID

'434082"

20

"FILTER":

13.882893.

21

SCORE

"TERM":

22

}:

SOURCE

233333322

算分函数是固定值,WEIGHT10

"BRAND":"如家"

:"复兴东路260号",

"ADDRESS'

"BRAND":"如家",

"BUSINESS":"豫园地区",

"WEIGHT":10

"CITY":"上海",

"ID":434082,

"LOCATION" : "31.220706, 121.498769",

算分模式是SUM,即

"BOOST_MODE":"SUM"

"NAME":"如家酒店.NEO(上海外滩城隍庙小南

原始算分+WEIGHT

30

"PIC":"HTTPS://M.TUNIUCDN.COM/FB2/T1/G6

-5CEWDEAAGRFQNNIVOAAGCT627_W200_H200_C1

E":392


这里算分模式如果替换成:replace,所有的query score都将失效,被function score所替代,都变成10
3)小结
function score query定义的三要素是什么?
过滤条件:哪些文档要加分
算分函数:如何计算function score
加权方式:function score 与 query score如何运算

1.5.3.布尔查询
布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:
must:必须匹配每个子查询,类似“与”
should:选择性匹配子查询,类似“或”
must_not:必须不匹配,不参与算分,类似“非”
filter:必须匹配,不参与算分
比如在搜索酒店时,除了关键字搜索外,我们还可能根据品牌、价格、城市等字段做过滤:

image.svg

图片加载失败

黑马旅游

WWW.ITHEIMA.COM

搜索

全部结果:

杭州

城市

北京

深圳

上海

二钻

三钻

五星级

星级

五钻

四钻

四星级

万怡喜来登

皇冠假日

7天酒店

希尔顿

和颐

华美达

如家

品牌

速8

1500元以上

600-1500元

300-600元

100元以下

100-300元

价格


每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询了。
需要注意的是,搜索时,参与打分的字段越多,查询的性能也越差。因此这种多条件查询时,建议这样做:
搜索框的关键字搜索,是全文检索查询,使用must查询,参与算分
其它过滤条件,采用filter查询。不参与算分
1)语法示例:
2)示例
需求:搜索名字包含“如家”,价格不大于400,在坐标31.21,121.5周围10km范围内的酒店。
分析:
名称搜索,属于全文检索查询,应该参与算分。放到must中
价格不大于400,用range查询,属于过滤条件,可以不参与算分。放到must_not中
周围10km范围内,用geo_distance查询,属于过滤条件,可以不参与算分。放到filter中

HELP

SETTINGS

MUST

"LOCATION" : "31.220706, 121.498769".

43

":"如家酒店.NEO(上海外滩城隍庙小南门地铁站店)",

'NAME"

"MATCH":

49

"PIC": "HTTPS://M.TUNIUCDN.COM/FB2/T1/G6/M00/52/B6/CII

NAME包含如家

"NAME":"如家"

-5CEWDEAGRFQNNIVOAAGCT627_W200_H200_C1_T0.JPG",

"PRICE":392,

50

51

"SCORE":44,

"STARNAME":"二钻"

"MUST_NOT": [

54

"RANGE":

55

价格不大于400

"PRICE":

56

_INDEX":"HOTEL".

"GT":400

57

_TYPE":" DOC".

SSSBABEHSS

"1584362548",

ID"

: 1.4916427,

SCORE"

SOURCE

"御青路315-317号",

"ADDRESS

FILTER":[

"BRAND""

如家

:"周浦康桥地区",

"BUSINESS"

"GEO_DISTANCE":{

"CITY"

二海

"DISTANCE":"10KM",

"ID"

1584362548,

"LOCATION":

10KM范围内

"LOCATION" : "31.15719, 121.572392",

66

"LAT":31.21,

67

:"如家酒店(上海浦东国际旅游度假区御桥地铁站店)

,NAME

"1ON":121.5

"HTTPS://M.TUNIUCDN.COM/FB3/S1/2N9C

68

'PIC"

/2VHD3WADOR+PEKCPYMVSOOV1HNYE W2AA H2AA C


3)小结
bool查询有几种逻辑关系?
must:必须匹配的条件,可以理解为“与”
should:选择性匹配的条件,可以理解为“或”
must_not:必须不匹配的条件,不参与打分
filter:必须匹配的条件,不参与打分
2.搜索结果处理
2.1.排序
elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。
2.1.1.普通字段排序
keyword、数值、日期类型排序的语法基本一致。
语法:
排序条件是一个数组,也就是可以写多个排序条件,并且与query标签保持平行。按照声明的顺序,当第一个条件相等时,再按照第二个条件排序,以此类推。示例:
需求描述:酒店数据按照用户评价(score)降序排序,评价相同的按照价格(price)升序排序
语法一:官方自己默认
语法二:简化排序格式,跟字段保持平齐
此时可以查看到数据:评分最高是49分,接着两个一样的48里,价格是升序返回的。
2.1.2.地理坐标排序
地理坐标排序略有不同。语法说明:
这个查询的含义是:
指定一个坐标,作为目标点
计算每一个文档中,指定字段(必须是geo_point类型)的坐标 到目标点的距离是多少
根据距离排序
示例:
需求描述:实现对酒店数据按照到你的位置坐标的距离升序排序。提示:获取你的位置的经纬度的方式:链接
假设我的位置是:31.034661,121.612282,寻找我周围距离最近的酒店。

# 找到121.612282,31.034661周围的酒店,距离升序排序

:"沪南公路7688弄1号",

23

"ADDRESS"

GET/HOTEL/_SEARCH

"BRAND":"万豪",

24

"BUSINESS":"南汇/野生动物园",

25

"QUERY":I

"CITY":"上海",

26

"MATCH_ALL": {}

"ID":2056298828,

27

"LOCATION" :L"31.030053, 121.662943",

28

"NAME":"上海中优城市万豪酒店",

29

"PIC" : "HTTPS://M.TUNIUCDN.COM/FB3/S1/2N9C

30

_GEO_DISTANCE":{

/2GBATEYYSYQWMW3WZL863HGDQJAQ_W200_H200_C3

"LOCATION":

31

"PRICE":1200,

"LAT":31.034661,

32

"SCORE":45,

"LON": 121.612282

"STARNAME":"五钻"

33

了,

34

"ORDER":"ASC",

35

'SORT"

"UNIT":"KM"

36

4.8541199685347785

37

38

39

"H "HOT"


这里的排序语法也可以跟上面的组合在一起,形成三个排序:得分、价格、地理坐标。
2.2.分页
elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:
from:从第几个文档开始
size:总共查询几个文档
类似于mysql中的limit ?, ?
2.2.1.基本的分页
分页的基本语法如下:
2.2.2.深度分页问题
现在,我要查询990~1000的数据,查询逻辑要这么写:
这里是查询990开始的数据,也就是 第990~第1000条数据。不过,elasticsearch内部分页时,必须先查询 0~1000条,然后截取其中的990 ~ 1000的这10条:

身身

1000 DOCS

截取990~1000条文档

排序,获取前1000条数据


查询TOP1000,如果es是单点模式,这并无太大影响。但是elasticsearch将来一定是集群,例如我集群有5个节点,我要查询TOP1000的数据,并不是每个节点查询200条就可以了。因为节点A的TOP200,在另一个节点可能排到10000名以外了。
因此要想获取整个集群的TOP1000,必须先查询出每个节点的TOP1000,汇总结果后,重新排名,重新截取TOP1000。

ES集群

SHARD1

SHARD3

SHARD3

SHARD2

1000  DOCS

1000 DOCS

1000 DOCS

1000 DOCS

-------------------

聚合所有结果,

重新排序选取前1000个

1000 DOCS


那如果我要查询9900~10000的数据呢?是不是要先查询TOP10000呢?那每个节点都要查询10000条?汇总到内存中?当查询分页深度较大时,汇总数据过多,对内存和CPU会产生非常大的压力,因此elasticsearch会禁止from+ size 超过10000的请求。针对深度分页,ES提供了两种解决方案,官方文档
search after:分页时需要排序,原理是从上一次的排序值开始,查询下一页数据。官方推荐使用的方式。
scroll:原理将排序后的文档id形成快照,保存在内存。官方已经不推荐使用。

ORY SETTINGS

130MS

HELP

400-BAD REGUEST

MAX_RESULT_WINDOW] IN

INDEX LEVEL SETTING

#ES分页

"CAUSED BY":

: "ILLEGAL ARGUMENT EXCEPTION",

GET /HOTEL18483/_SEARCH

"TYPE"

"REASON" : "RESULT WINDOW IS TOO LARGE, FROM + SIZE MUST BE LESS

"QUERY":{

THAN OR EQUAL TO: [1000E] BUT WAS [100019]. SEE THE SCROLL API

"MATCH_ALL"; {}

FOR A MORE EFFICIENT WAY TO REQUEST LARGE DATA SETS. THIS LIMIT.

CAN BE SET BY CHANGING THE [INDEX.MAX RESULT WINDOW] INDEX LEVEL

当数据超过1000,会自动提示异常

99999

FROM

SETTING.",

CAUSEDBY":!

SORT":

"TYPE" : "ILLEGAL_ARGUMENT_EXCEPTION".

LARGE, FROM+SIZE MUST BE LESS

PT OOY EF MONITM ITNSAU. . UNCEA

"PRICE":F

THAN OR EQUAL TO: [10000] BU

[100019]

] BUT WAS

SEE THE SCROLL API

"DESC"

ORDER

FOR A MORE ERRICIENT WAY TO REQUEST IA

E DATA SETS. THIS

ST LARGE DA

NG THE [INDEX.MAX_RESULT_WINDOW]

LIMIT CAN BE SET BY CHANGING

INDEX LEVEL SETTING."

SUNGS

子,

#高亮查询

"STATUS":400

GET /HOTEL18483/_SEARCH


2.2.3.小结
分页查询的常见实现方案以及优缺点:
from + size:
优点:支持随机翻页
缺点:深度分页问题,默认查询上限(from + size)是10000
场景:百度、京东、谷歌、淘宝这样的随机翻页搜索
after search:
优点:没有查询上限(单次查询的size不超过10000)
缺点:只能向后逐页查询,不支持随机翻页
场景:没有随机翻页需求的搜索,例如手机向下滚动翻页
scroll:
优点:没有查询上限(单次查询的size不超过10000)
缺点:会有额外内存消耗,并且搜索结果是非实时的
场景:海量数据的获取和迁移。从ES7.1开始不推荐,建议用 after search方案。
2.3.高亮
2.3.1.高亮原理
什么是高亮显示呢?我们在百度,京东搜索时,关键字会变成红色,比较醒目,这叫高亮显示:

JAVA

百度

JAVA(计算机编程语言)-百度百科

JAVA是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃

了C++里难以理解的多继承,指针等概念,因此JAVA语言具有功能强大和

简单易用两个特征,JAVA语言作为静态面向对象编程语言的代表,极好

地实现了面向对象理论,允许程序员以优雅的思维方式进行...

发展历程编程开发语言特点工作原理语言基础更多>

BAIKE.BAIDU.COM/

JAVA / ORACLE

JAVA与您,立即下载免费JAVA下载,什么是JAVA?"是否需要帮助?所有JAVA下载关于JAVA(英

语站点)选择语言关于JAVAL支持|开发人员隐私政策1I使用条款

百度快照

WWW.JAVA.COM/

SECURITY

APPLICATION

ELEMENTS

NETWORK

PERFOMANCE

CONSOLE

MEMORY

SOURCES

\ 6UP VASTOM ONUAT / / MI

COMPUTED

STYLES

<DIV CLASS-"C-ROW C-GAP-TOP-SMALL">

STYLE-"POSITION:RELATIVE;TOP:2PX;">...

<DIV CLASS"C-SPAN3"

FILTER

Y<DIV CLASS,"C-SPAN9 C-SPAN-LAST OP-BK-POLYSEMY-PICCONTENT"

ELEMENT.STYLE{

<EM>JAVA</EM> $0

"是一门面向对象编程语言,不仅吸收了C+语言的各种优点,还摒多

EM{

#F73131;

COLOR:

念,因此

<EM>JAVA</EM>


高亮显示的实现分为两步:
1)给文档中的所有关键字都添加一个标签,例如<em>标签
2)页面给<em>标签编写CSS样式
2.3.2.实现高亮
高亮的语法:
注意:
高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。
默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false
示例:

6327383370008

"HOTE1182".

"PRICE":[

INDEX"

" : " DOC"

"ORDER":"DESC"

_TYPE"

_ID":"415600"

1.8905408,

SCORE

SOURCE

主间房乡裕裤坡村青年沟西侧558号",

"ADDRESS

"BRAND""

"如家"

:"传媒大学/管庄地区",

"BUSINESS"

#高亮查询

北京",

"CITY"

GET /HOTEL182/_SEARCH

"ID":415600,

"LOCATION":"39.923212,116.560023",

"QUERY":

"NAME":"如家酒店(北京朝阳北路传媒大学裕裤坡地铁站店)",

搜索与高亮字段一致,可直接高亮展示

"PIC":"HTTPS://M.TUNIUCDN.COM/FB3/S1/2N9C

"如家"

"BRAND"

/3NEZPXNZWOMDNXIBWBMKQUAZJDYJ_W200_H200_C1_T0.JPG",

"PRICE"

":259,

"SCORE"

2":47.

"HIGHLIGHT": {

"STARNAME" :"二钻"

"FIELDS":

"BRAND

"HIGHLIGHT": {

<EM>

PRE_TAGS

BRAND

"</EM>"

_TAGS

<EM>如家</EM>"


200

SETTINGS

ISTORY

HELP

"HOTEL182".

INDEX"

193

"ORDER": "DESC"

TYPE"

[DOC"

194

"339952837",

195

2.7875905,

196

SCORE'

197

SOURCE":+

"ADDRESS":"良乡西路7号",

198

"BRAND":"如家",","

199

#高亮查询

"BUSINESS":"房山风景区",

200

GET /HOTEL182/_SEARCH

"CITY":"北京",

201

"ID" : 339952837,

202

"QUERY":[

"LOCATION" : "39.73167, 116.132482",

203

MATCH"

搜索与高亮字段不一致,需要增加属性声明:FALSE

230

"NAME":"如家酒店(北京良乡西路店)",

"如家"

204

"ALL"

"PIC": "HTTPS://M.TUNIUCDN.COM/FB3/S1/2N9C

205

/3DPGF5RTTZRXPEN5Y3RLNRVTXMEA_W200_H200_C1_T0.JPG",

206

31

"PRICE": 159,

207

"HIGHLIGHT": F

32

"SCORE":46,

208

FIELDS":

"STARNAME":"二钻"

33

209

'BRAND'':

34

210

"<EM>"

PRE_TAGS

"HIGHLIGHT": {

35

36

"BRAND":[

"REQUIRE FIELD MATCH"; "FALSE"

<EM>如家</EM>"

37

38

214


2.4.总结
查询的DSL是一个大的JSON对象,包含下列属性:
query:查询条件
from和size:分页条件
sort:排序条件
highlight:高亮条件
示例:

GET /HOTEL/_SEARCH

'QUERY':

查询条件

"MATCH":

"如家"

NAME":

分页条件

"FROM":0,//分页开始的位置

"SIZE":20,//期望获取的文档总数

SORT''

"ASC" }, /

]

普通排序

"PRICE":

:{//距离排序

"_GEO_DISTANCE"

"31.040699,121.618075"!

"LOCATION"

排序条件

"ORDER"

'ASC??

"KM"

"UNIT" :

"HIGHLIGHT":

{//高亮字段

"FIELDS":

高亮条件

3

"NAME"

"PRE_TAGS": "<EM>",

//用来标记高亮字段的前置标签

用来标记高亮字段的后置标签

POST_TAGS":"</EM>


3.RestClient查询文档
文档的查询同样适用昨天学习的 RestHighLevelClient对象,基本步骤包括:
1)准备Request对象
2)准备请求参数
3)发起请求
4)解析响应
3.1.快速入门
在我们昨天学习的课程基础之上,我们继续在单测中练习。我们以match_all查询为例(这里也可新建一个单测类)
3.1.1.发起查询请求

@TEST

VOID TESTMATCHALL() THROWS IOEXCEPTION {

GET /INDEXNAME/_SEARCH

//1.准备REQUEST

SEARCHREQUEST REQUEST - NEW SEARCHREQUEST("HOTELU);

"QUERY":

//2.组织DSL参数

"MATCH_ALL": {}

REQUEST.SOURCE()

QUERY(QUERYBUILDERS.MATCHALLQUERY());

1/3.发送请求,得到响应结果

SEARCHRESPONSE RESPONSE - CLIENT.SEARCH(REQUEST, REQUESTOPTIONS.DEFAULT);

//...解析响应结果


代码解读:
第一步,创建SearchRequest对象,指定索引库名
第二步,利用request.source()构建DSL,DSL中可以包含查询、分页、排序、高亮等
query():代表查询条件,利用QueryBuilders.matchAllQuery()构建一个match_all查询的DSL
第三步,利用client.search()发送请求,得到响应
这里关键的API有两个,一个是request.source(),其中包含了查询、排序、分页、高亮等所有功能:

W SEARCHREQUEST( .....INDICES:");

SEARCHREQUEST

三 NEW

REQUEST

REQUEST.SOURCE

AGGREGATION(AGGREGATIONBUILDER AGGREGATION)

VAR

SORT(SORTBUILDER<?>

SORT)

EST

HIGHLIGHTER(HIGHLIGHTBUILDER

HIGHLIGHTBUILDER)

SORTORTORDER ORDER)

SORT(STRING NAME, SO

AGGREGATION(PIPELINEAGGREGATIONBUILDER AGGREGATION)

HIGHLIGHTER()

T(STRING NAME)

SORT

SORT(LIST<SORTBUILDER<?>> SORTS)

SIZE(INT SIZE)

SIZE()

FROM(INT FROM)

FROM()

QUERY(QUERYBUITDER QUERY)

QUERY


image.svg

图片加载失败

另一个是QueryBuilders,其中包含match、term、function_score、bool等各种查询:

QUERYBUILDERS

MATCHALLQUERY():MATCHALLQUERYBUILDER

MATCHQUERY(STRING,OBJECT):MATCHQUERYBUILDER

IDSQUERY():IDSQUERYBUILDER

TERMQUERY(STRING, STRING):TERMQUERYBUILDER

M

RANGEQUERY(STRING):RANGEQUERYBUILDER

WILDCARDQUERY(STRING, STRING):WILDCARDQUERYBUILDER

SIMPLEQUERYSTRINGQUERY(STRING):SIMPLEQUERYSTRINGBUILDER

BOOSTINGQUERY(QUERYBUILDER,QUERYBUILDER):BOOSTINGQUERYBUILDER

BOOLQUERY():BOOLQUERYBUILDER

FUNCTIONSCOREQUERY(QUERYBUILDER):FUNCTIONSCOREQUERYBUILDER

GEODISTANCEQUERY(STRING):GEODISTANCEQUERYBUILDER

GEOBOUNDINGBOXQUERY(STRING): GEOBOUNDINGBOXQUERYBUILDER


3.1.2.解析响应
响应结果的解析:

"TOOK!?

0,

我们通过MATCH_ALL来演示下基本的API,再看结果的解析:

"TIMED_OUT":FALSE,

"HITS":

"TOTAL":{

@TEST

"VALUE" : 2,

VOID TESTMATCHALL() THROWS IOEXCEPTION T

"RELATION" : "EQ"

了.

1/4.解析结果

"MAX_SCORE" : 1.0,

"HITS"

SEARCHHITS SEARCHHITS RESPONSE.GETHITS();

//4.1.查询的总条数

"INDEX"

"HEIMA",

LONG TOTAL - SEARCHHITS.GETTOTALHITS().VALUE;

'DOC'

_TYPE?

//4.2.查询的结果数组

_ID"

_SCORE':1.0,

SEARCHHITS.GETHITS();

SEARCHHITL]HITSEA

]:

SOURCE

FOR (SEARCHHIT HIT : HITS){

"INFO"

"JAVA讲师"

//4.3.得到SOURCE

"赵云"

''NAME

STRING JSON : HIT.GETSOURCEASSTRING();

//4.4.打印

SYSTEM.OUT.PRINTLN(JSON);


image.svg

图片加载失败

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.完整代码
完整代码如下:
代码和DSL语句映射关系

A3X2

14

ATEST

PUBLIC VOID TESTMATCHALLQUERY() TH

THROWS IOEXCEPTION

GET/INDEXNAME/_SEARCH

11实例化请求对象,参数为索引库名

SEARCHREQUEST SEARCHREQUEST - NEW SEARCHREQUEST( ..... INDICES: "HOTEL182");

"QUERY":

准备DSL语句

"MATCH_ALL":"

SEARCHREQUEST.SOURCE()

,QUERY(QUERYBUILDERS.MATCHALLQUERY();-

1/发起请求

SEARCHRESPONSE SEARCHRESPONSE ; CLIENT.SEARCH(SEARCHREQUEST, REQUESTOPTIONS.DEFAULD;

"VALUE":201,

1/响应数据解析

"RELATION":"EG"

1/1-拿到最外层HITS

:1.0.

THITS

SEARCHHITS HITS ; SEARCHRESPONSE.GETHITS();

INDEX"

HOTE1182

2-循环遍历内层HITS

FOR (SEARCHHIT HIT : HITS){

"SOURCE

3-获取每一个里面的SOUCE

"静安交通路40号".

STRING SOURCEJSON ; HIT.GETSOURCEASSTRING();

7天酒店",""

四川北路商业区

1/4-输出SOURCE转换成HOTELDOC

DOC HOTELDOC - JSON.PARSEOBJECT(SOURCEJSON, HOTELDOC.CLASS);

HOTELDOC HO

M.OUT.PRINTLN("HOTELDOC - " + HOTELDOC);

SYSTEM


3.1.4.小结
查询的基本步骤是:
1 创建SearchRequest对象
2 准备Request.source(),也就是DSL。① QueryBuilders来构建查询条件② 传入Request.source() 的 query() 方法
3 发送请求,得到结果
4 解析结果(参考JSON结果,从外到内,逐层解析)
3.2.match查询
全文检索的match和multi_match查询与match_all的API基本一致。差别是查询条件,也就是query的部分。

image.svg

图片加载失败

GET /HOTEL/_SEARCH

QUERY' :

"MATCH_ALL": {}

GET /HOTEL/_SEARCH

QUERY :

"MATCH":

"如家"

"ALL":

GET /HOTEL/_SEARCH

"QUERY":[

'MULTI

MATCH

"QUERY":"如家",

"FIELDS": ["BRAND", "NAME"]


因此,Java代码上的差异主要是request.source().query()中的参数了。同样是利用QueryBuilders提供的方法:

/单字段查询

QUERYBUILDERS.MATCHQUERY("ALL","如家");

/多字段查询

QUERYBUILDERS.MULTIMATCHQUERY("如家", "NAME", "BUSINESS");


而结果解析代码则完全一致,可以抽取并共享。完整代码如下:
3.3.精确查询
精确查询主要是两者:
term:词条精确匹配
range:范围查询
与之前的查询相比,差异同样在查询条件,其它都一样。查询条件构造的API如下:

/|词条查询

QUERYBUILDERS.TERMQUERY("CITY","杭州");

范围查询

QUERYBUILDERS.RANGEQUERY("PRICE").G

).GTE(100).LTE(150);


3.4.布尔查询
布尔查询是用must、must_not、filter等方式组合其它查询,代码示例如下:

GET /HOTEL/_SEARCH

创建布尔查询

BOOLQUERYBUILDER BOOLQUERY - QUERYBUILDERS.BOOLQUERY();

"QUERY":[

"BOOL":{

添加MUST条件

MUST

BOOLQUERY.MUST(QUERYBUILDERS.TERMQUERY("CITY","杭州("杭州);

{"CITY":"杭州"

"TERM":

添加FILTER条件

BOOLQUERY.FILTER(QUERYBUILDERS.RANGEQUERY("PRICE").LTE(250);

"FILTER":

"RANGE":[

"PRICE":{ "LTE": 250 7


可以看到,API与其它查询的差别同样是在查询条件的构建,QueryBuilders,结果解析等其他代码完全不变。完整代码如下:
会发现查不到数据,可以把城市换成:上海,价格2000,再试试就有数据返回了
3.5.排序、分页
搜索结果的排序和分页是与query同级的参数,因此同样是使用request.source()来设置。对应的API如下:

GET /INDEXNAME/_SEARCH

查询

REQUEST.SOURCE().QUERY(QUERYBUILDERS.MATCHALLQUERY());

'QUERY':

1/分页

"MATCH_ALL": {}

REQUEST.SOURCE().FROM(0).SIZE(5);

FROM": O.

//价格排序

'SIZE''S,

REQUEST.SOURCE().SORT("PRICE", SORTORDER.ASC);

SORT :

"DESC!"

"FIELD":


完整代码示例:
3.6.高亮
高亮的代码与之前代码差异较大,有两点:
查询的DSL:其中除了查询条件,还需要添加高亮条件,同样是与query同级。
结果解析:结果除了要解析_source文档数据,还要解析高亮结果
3.6.1.高亮请求构建
高亮请求的构建API如下:

GET /HOTEL/_SEARCH

"QUERY": [

REQUEST.SOURCE().HIGHLIGHTER(NEW HIGHLIGHTBUILDER()

"MATCH":[

"ALL":"如家"

. FIELD("NAME")

//是否需要与查询字段匹配

子,

REQUIREFIELDMATCH(FALSE)

"HIGHLIGHT": [

);

"FIELDS":[

"NAME": {

REQUIRE FIELD MATCH":"FALSE"


上述代码省略了查询条件部分,但是大家不要忘了:高亮查询必须使用全文检索查询,并且要有搜索关键字,将来才可以对关键字高亮。完整代码如下:
3.6.2.高亮结果解析
高亮的结果与查询的文档结果默认是分离的,并不在一起。

THOTEL".

INDEX"

DOC'"

TYPE"

ID"

'339952837"

获取SOURCE

2.8947515,

_SCORE

HOTELDOC HOTELDOC - JSON.PARSEOBJECT(HIT.GETSOURCEASSTRING(),

_SOURCE :

HOTELDOC.CLASS);

"ID":339952837,

"NAME":"如家酒店(北京良乡西路店)",

//处理高亮

159,

"PRICE"

MAP<STRING,HIGHLIGHTFIELD> HIGHLIGHTFIELDS

"SCORE" : 46,

HIT.GETHIGHLIGHTFIELDS();

"BRAND":"如家",

IF (!COLLECTIONUTILS.ISEMPTY(HIGHLIGHTFIELDS)) (!

"CITY":"北京",

1/获取高亮字段结果

"LOCATION" : "39.73167, 116.132482",

"PIC" :"TO.JPG"

HIGHLIGHTFIELD HIGHLIGHTFIELD - HIGHLIGHTFIELDS.GET( "NAMEV);

IF (HIGHLIGHTFIELD !: NULL) {

"HIGHLIGHT": {

1/取出高亮结果数组中的第一个,就是酒店名称

NAME

- HIGHLIGHTFIELD.GETFRAGMENTS()[0].STRING();

<EM>如家</EM>酒店(北京良乡西路店)"

STRING

G NAME

HOTELDOC.SETNAME(NAME)


因此解析高亮的代码需要额外处理:
代码解读:
第一步:从结果中获取source。hit.getSourceAsString(),这部分是非高亮结果,json字符串。还需要反序列为HotelDoc对象
第二步:获取高亮结果。hit.getHighlightFields(),返回值是一个Map,key是高亮字段名称,值是HighlightField对象,代表高亮值
第三步:从map中根据高亮字段名称,获取高亮字段值对象HighlightField
第四步:从HighlightField中获取Fragments,并且转为字符串。这部分就是真正的高亮字符串了
第五步:用高亮的结果替换HotelDoc中的非高亮结果
完整代码如下:
4.黑马旅游案例
下面,我们通过黑马旅游的案例来实战演练下之前学习的知识。我们实现四部分功能:
酒店搜索和分页
酒店结果过滤
酒店竞价排名
启动我们提供的hotel-demo项目,其默认端口是8089,访问http://localhost:8090,就能看到项目页面了:

LOCALHOST:8089

网关

工作

邮箱

推广

权限

乐优

云服务

前端

SX

黑马旅游

测试

WWW.ITHEIMA.COM

搜索

全部结果:

深圳

城市

杭州

北京

三钻

星级

五钻

四钻

四星

二钻

五星

万豪

希尔顿

和颐

皇冠假日

喜来登

7天酒店

华美达

万怡

品牌

如家

速8

价格

100元以下

1500元以上

300-600元

100-300元

600-1500元

默认评价格|

271家酒店

1/28

地图预览

上海希尔顿酒店

2688起

五星级

立即预定

位于静安寺地区周边,静安华山路250号

3.7分/5分

1分钟前有人预订了该酒店

杨浦区

上海站

普陀区


4.1.酒店搜索和分页
案例需求:实现黑马旅游的酒店搜索功能,完成关键字搜索和分页
启动项目后访问:http://localhost:8089/
4.1.1.需求分析
在项目的首页,有一个大大的搜索框,还有分页按钮:

测试

黑马旅游

WWW.ITHEIMA.COM

如家

搜索

全部结果:

城市

上海

北京

杭州

三钻四钻

五星

二钻

星级

四星

万豪

7天酒店

皇冠假日

品牌

万怡

速8

如家

华美达

300-600元

100-300元

600-1500元

100元以下

价格

1500元以上

默认

评价|

价格|

共271家酒店

128


点击搜索按钮,可以看到浏览器控制台发出了请求:

GENERAL

REQUEST URL:

HTTP://LOCALHOST:8089/HOTEL/LIST

POST

REQUEST METHOD:

STATUS CODE:

404

REMOTE ADDRESS:[

:[:1]:8089

REFERRER POLICY: STRICT-ORIGIN-WHEN-CROSS-ORIGIN


请求参数如下:

REQUEST PAYLOAD

VIEW SOURCE

"{KEY: "", PAGE: 1, SIZE:5, SORTBY: "DEFAULT"}

KEY: ""

PAGE:1

SIZE: 5

SORTBY:"DEFAULT"


由此可以知道,我们这个请求的信息如下:
请求方式:POST
请求路径:/hotel/list
请求参数:JSON对象,包含4个字段:
key:搜索关键字
page:页码
size:每页大小
sortBy:排序,目前暂不实现
返回值:分页查询,需要返回分页结果PageResult,包含两个属性:
total:总条数
List<HotelDoc>:当前页的数据
因此,我们实现业务的流程如下:
步骤一:定义实体类,接收请求参数的JSON对象
步骤二:编写controller,接收页面的请求
步骤三:编写业务实现,利用RestHighLevelClient实现搜索、分页
4.1.2.定义实体类
实体类有两个,一个是前端的请求参数实体,一个是服务端应该返回的响应结果实体。
1)请求参数
前端请求的json结构如下:
因此,我们在cn.itcast.hotel.pojo包下定义一个实体类:
2)返回值
分页查询,需要返回分页结果PageResult,包含两个属性:
total:总条数
List<HotelDoc>:当前页的数据
因此,我们在cn.itcast.hotel.pojo中定义返回结果:
4.1.3.定义controller
定义一个HotelController,声明查询接口,满足下列要求:
请求方式:Post
请求路径:/hotel/list
请求参数:对象,类型为RequestParam
返回值:PageResult,包含两个属性
Long total:总条数
List<HotelDoc> hotels:酒店数据
因此,我们在cn.itcast.hotel.web中定义HotelController:
4.1.4.实现搜索业务
我们在controller调用了IHotelService,并没有实现该方法,因此下面我们就在IHotelService中定义方法,并且去实现业务逻辑。
1)在cn.itcast.hotel.service中的IHotelService接口中定义一个方法:
2)实现搜索业务,肯定离不开RestHighLevelClient,我们需要把它注册到Spring中作为一个Bean。在cn.itcast.hotel中的HotelDemoApplication中声明这个Bean:(注意更改IP)
3)在cn.itcast.hotel.service.impl中的HotelService中实现search方法:
4)重启项目后访问,发现搜索结果会出来,同时分页信息也产生了变化

黑马旅游

如家

抢型

全部贴品果

上海

北京

深圳

城市

杭州

四品

四站

王星

二站

五站

星级

三站

万豪

7天酒店

万怡

速8

华黄达

若尔赖

喜来金

如家

价相

600-1500元

100元CL下

1500元以上

300-600元

190-300元

搜索结果:30

每页大小:5,因此6页

默认评价格!价格!

共30家酒店1/6

地国预览

如家酒店

如家酒店(北京良乡西路店)

159

南宫温泉水世界

立即预定

二站

4.6分/5分

位于河山风景区网边,良乡西路7号

青龙湖镇

RECORDER X PERFORMANCE INSIGHTS &

X

SECURITY

NETWORK

LIGHTHOUSE

MEMORY

APPLICATION

PERFOMANCE

ELEMENTS

CONSOLE

SOURCES

O

NO THROTTLING

7

O

RESERVE LOG

DISABLE CACHE

FETCHR IS CSS CSS ING MADIO FONT DOC WS WANFFARFFART OTHER OTHER  D  BLACTED  BLOCTED  ERDPARDPARY RE

HIDE DATA URLS ALL

FILTER

LNVERT

7000MS 800000MS  90000MS 10000MS 11000MS 12000MS 12000MS 13000MS

170000 MS

60000 MS

150000 MS

160000 MS

30000 MS

20000 MS

50000 MS

40000 MS

10000MS

X

HEADERS

PAYLOAD

TIMING

RESPONSEINITIATOR

PREVIEW

NAME

COOKIES

后端返回结果集

VT

LIST

[TOTAL:30,..~]

VHOTELS:[ID:339952837,NAME:"如家酒店(北京良乡西路店)",

",ADDRESS:"良乡西路7号",PRICE:159,SCORE:46,BRAND:"如家",-],-],-]

FILTERS

路店)",ADDRESS:"良乡西路7号",PRICE:159,SCORE:46,BRAND:"如家","如家","

B:[ID:339952837,NAME:"如家酒店(北京良乡西路店)",ADD

MARK BS.PNG

11:(ID:2359697,NAME:"如家游店(北京上地安宁压东路店)",ADDRESS:"清阿小营安宁丘东路18号2

中),西兴

2:(ID:1455383931,NAME;"如家酒店(深圳宝安客运中心站后)",ADDRESS:"西乡河西金雅新苑34栋"

1.4 MB TRANSFERRED

2.1 MB RESOURCES

406 REQUESTS


4.2.酒店结果过滤
需求:添加品牌、城市、星级、价格等过滤功能
4.2.1.需求分析
在页面搜索框下面,会有一些过滤项:

黑马旅游

WWW.ITHEIMA.COM

搜索

全部结果:

北京

上海

城市

三钻四星级

五星级

星级

二钻

如家

7天酒店

华美达  喜来登万豪

品牌

价格

元以下100-300元

1500元以上


image.svg

图片加载失败

传递的参数如图:

PREVIEW

HEADERS

RESPONSE

INI

{KEY:"如家",PAGE:1,SIZE:5,

BRAND:"华美达"

CITY:"上海"

KEY:"如家"

MAXPRICE:1500

MINPRICE:

600

PAGE:1

SIZE: 5

"DEFAULT"

SORTBY:

"五钻"

STARNAME:


image.svg

图片加载失败

包含的过滤条件有:
brand:品牌值
city:城市
minPrice~maxPrice:价格范围
starName:星级
我们需要做两件事情:
修改请求参数的对象RequestParams,接收上述参数
修改业务逻辑,在搜索条件之外,添加一些过滤条件
4.2.2.修改实体类
修改在cn.itcast.hotel.pojo包下的实体类RequestParams:
4.2.3.修改搜索业务
在HotelService的search方法中,只有一个地方需要修改:requet.source().query( ... )其中的查询条件。
在之前的业务中,只有match查询,根据关键字搜索,现在要添加条件过滤,包括:
品牌过滤:是keyword类型,用term查询
星级过滤:是keyword类型,用term查询
价格过滤:是数值类型,用range查询
城市过滤:是keyword类型,用term查询
多个查询条件组合,肯定是boolean查询来组合:
关键字搜索放到must中,参与算分
其它过滤条件放到filter中,不参与算分
因为条件构建的逻辑比较复杂,这里先封装为一个函数:

@OVERRIDE

}

PAGERESULT SEARCH(REQUESTPARAMS PARAMS)

PUBLIC

TRY {

//1.准备REQUEST

SEARCHREQUEST REQUEST - NEW SEARCHREQUEST( .... . "HOTELU);

//2准备DSL

// 2.1.QUERY

整个查询条件的构建比较复杂,封装为一个函数

BUILDBASICQUERY(PARAMS, REQUEST);

1/2.2.2分页

INT PAGE -

PARAMS GETPAGE();

PARAMS.GETSIZE();

INT SIZE

REQUEST.SOURCE().FROM((PAGE - 1) * SIZE).SIZE(SIZE);

/3发送请求

CLIENT.SEARCH(REQUEST, REQUESTOPTIONS.DEFAULT);

SEARCHRESPONSE

RESPONSE

/4.解析响应

RETURN HANDLERESPONSE(RESPONSE);

CH (IOEXCEPTION E){

CATCH


buildBasicQuery的代码如下:


4.3.酒店竞价排名
需求:让指定的酒店在搜索结果中排名置顶
4.3.1.需求分析
要让指定酒店在搜索结果中排名置顶,效果如图:

image.svg

图片加载失败

上海希尔顿酒店

¥2688

五星级

立即预定

位于静安寺地区周边,静安华山路250号

3.7分/5分

1分钟前有人预订了该酒店

上海外高桥皇冠假日酒店

3299

五星级

立即预定

位于 浦东外高桥地区 周边,杨高北路1000号

4.6分/5分

1分钟前有人预订了该酒店

上海虹口三至喜来登酒店

1899

五星级

立即预定

位于四川北路商业区 周边,四平路59号

4.6分/5分

1分钟前有人预订了该酒店


页面会给指定的酒店添加广告标记。那怎样才能让指定的酒店排名置顶呢?
我们之前学习过的function_score查询可以影响算分,算分高了,自然排名也就高了。而function_score包含3个要素:
过滤条件:哪些文档要加分
算分函数:如何计算function score
加权方式:function score 与 query score如何运算
这里的需求是:让指定酒店排名靠前。因此我们需要给这些酒店添加一个标记,这样在过滤条件中就可以根据这个标记来判断,是否要提高算分。
比如,我们给酒店添加一个字段:isAD,Boolean类型:
true:是广告
false:不是广告
这样function_score包含3个要素就很好确定了:
过滤条件:判断isAD 是否为true
算分函数:我们可以用最简单暴力的weight,固定加权值
加权方式:可以用默认的相乘,大大提高算分
因此,业务的实现步骤包括:
1 给HotelDoc类添加isAD字段,Boolean类型
2 挑选几个你喜欢的酒店,给它的文档数据添加isAD字段,值为true
3 修改search方法,添加function score功能,给isAD值为true的酒店增加权重
4.3.2.修改HotelDoc实体
给cn.itcast.hotel.pojo包下的HotelDoc类添加isAD字段:

image.svg

图片加载失败

@DATA

NOARGSCONSTRUCTOR

PUBLIC CLASS HOTELDOC

PRIVATE

LONG ID;

STRING NAME;

PRIVATE

ADDRESS;

PRIVATE

STRING

PRIVATE INTEGER PRICE;

PRIVATE

INTEGER

SCORE;

PRIVATE

STRING BRAND;

STRING CITY;

PRIVATE

STRING

PRIVATE

STARNAME;

PRIVATE

STRING BUSINESS;

LOCATION;

PRIVATE

STRING

STRING PIC;

PRIVATE

PRIVATE OBJECT

DISTANCE;

广告标记

PRIVATE BOOLEAN ISAD;


4.3.3.添加广告标记
接下来,我们挑几个酒店,添加isAD字段,设置为true:(注意改成自己本地DB的数据ID)
修改完成可以通过命令查看是否有:isAD:true

SETTINGS

TORY

200-OK

HELP

668735377786一

88-

TYPE":

DOC'

39-

_ID":"38609".

"ADJUST_PURE_NEGATIVE": TRUE,

_SCORE" : 4.4115853,

'BOOST":1

SOURCE"::

:"广灵二路126号",

"ADDRESS"

"速8"

"BRAND":

"四川北路商业区"

"BUSINESS"

上海"

"CITY"

POST /HOTEL182/_SEARCH

"ID":38609,

"LOCATION" : "31.282444, 121.479385",

"NAME":"速8酒店(上海赤峰路店)",

48

"QUERY":

19

"PIC": "HTTPS://M.TUNIUCDN.COM/FB2/T1/G2/M00/DF/96/CII

"MATCH":

50

-TFKXEIMIQZEIAAITIL0LM7CAALCYWKXHQ4AAHOI377_W200_H200_C1_+

"ID":"38609"

-JPG"

SU3433333

249

"PRICE"

35

SCORE

POST /HOTEL182/_UPDATE/727679

"ISAD":TRUE

"DOC":{

58

"ISAD": TRUE


4.3.4.添加算分函数查询
接下来我们就要修改查询条件了。之前是用的boolean 查询,现在要改成function_socre查询。
function_score查询结构如下:

GET /HOTEL/_SEARCH

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

"QUERY":[

"FUNCTION_SCORE": {

过滤条件,符合条件的文档才会被重新算分

"MATCH":{"ALL":"外滩"了

"QUERY":I "MA

"FUNCTIONS": [

算分函数,算分函数的结果称为FUNCTION SCORE,将来会与QUERY

SCORE运算,得到新算分,常见的算分函数有:

WEIGHT:给一个常量值,作为函数结果(FUNCTION SCORE)

"FILTER": "TERM": {"ID": "1"J了,

FIELD VALUE FACTOR:用文档中的某个字段值作为函数结果

'WEIGHT":10

RANDOM_SCORE:随机生成一个值,作为函数结果

SCRIPT_SCORE:自定义计算公式结果作为函数结果

],

BOOST_MODE":'MULTIPLY

加权模式,定义FUNCTION SCORE与QUERY SCORE的运算方式,包括:

MULTIPLY:两者相乘.默认就是这个

REPLACE:用FUNCTION SCORE 替换 QUERY SCORE

其它:SUM,AVG,MAX,MIN


image.svg

图片加载失败

对应的JavaAPI如下:

GET /HOTEL/_SEARCH

"QUERY":[

"FUNCTION_SCORE": {

"QUERY':[

// 7.FUNCTION SCORE

"MATCH":

"NAME":"外滩"

FUNCTIONSCOREQUERYBUILDER FUNCTIONSCOREQUERYBUILDER -

原始查询条件

QUERYBUILDERS.FUNCTIONSCOREQUERY(

"FUNCTIONS": [

QUERYBUILDERS.MATCHQUERY("NAME","外滩"),

W FUNCTIONSCOREQUERYBUILDER.FILTERFUNCTIONBUILDER[]{

NEW FU

"FILTER":[

FUNCTIONSCOREQUERYBUILDER.FILTERFUNCTIONBUILDER(

NEW

TERM''I

"BRAND":"如家"

过滤条件

QUERYBUILDERS.TERMQUERY("BRAND","如家"),

SCOREFUNCTIONBUILDERS.WEIGHTFACTORFUNCTION(5)

算分函数

'WEIGHT":5

加权模式没指定,默认用MULTIPLY,相乘

);

SOURCEBUILDER.QUERY(FUNCTIONSCOREQUERYBUILDER);


我们可以将之前写的boolean查询作为原始查询条件放到query中,接下来就是添加过滤条件、算分函数、加权模式了。所以原来的代码依然可以沿用。
修改cn.itcast.hotel.service.impl包下的HotelService类中的buildBasicQuery方法,添加算分函数查询:
运行项目之后,可以发现上面ID被修改过的数据,优先排在前面

NAME

PAYLOAD

PREVIEW

RESPONSE

INITIATOR

TIMING

COOKIES

HEADERS

DFP8K782ETSOHQWSRDKD7ST9LA2_W200_H200_H200_C1_TO.JPG

修改过ISAD的数据

{TOTAL:83,--_]

E:"7天连镇酒店(上海宝山路地铁结店)",ADDRESS:"静安交通路40号",PRICE:336, SCORE:37,

HOTELS:[[ID:36934,NAME:"7天

ICII9EFKWVMKIP.MCAAI3FOHIS1WAALSDAMEO2MAAJEU309_W200.H200.C

1天连镇酒店(上海宝山路地铁站店)",ADDRESS:"静安交通路40号",PRICE:336, SCORE: 37,BRAR

0:{ID:36934,NAME:

LIST

道8酒店(上海赤峰路店)",ADDRESS:""灵二路126号",PRICE:249,SCORE:35,BRAND:"速8""速8""

1:[ID:38609,NAME:

FILTERS

(1:(1D:3865, NAME;"理8酒店上海中山北路三田路38;   ADARE; 35,   SCORESS;  SCORE; 35, SCORE; 226,  SCORE; 35, BRAN

户3:(1D: 38817, NAME; "7天连锁酒原(上海清溪路地铁站(上海清溪路;

MARK BS.PNG

产名:(ID:39196, NAME;"7天连锁利用(上海华庄地铁站用)",                                             348,    348,  

CII-TIK2MV2IMZ- AAEUCGG3DX4AALAAWEJIYCAAS6K083.W200.H200.C1.T0.

TOTAL:83

1 CII-TIKYLR2IEWNOAAHQYV7I5CKAALD-QP2IJWAADB6245.W200.H200.C1.TO.

CII-T1KU2ZGIGR7UAAF1NYY9CLWAAKXZAHO8HQAAXVN368 W200 H200 C1


4.3.5.添加排序
排序相对比较简单,注意它是跟query同级的,所以我们不放在封装的:buildBasicQuery 中,而是与其保持平级,代码如下:

TRY

1/ 1-准备请求

SEARCHREQUEST REQUEST - NEW SEARCHREQUEST( -INDICES: "HOTEL182");

1/2-准备DSL语句

1/2.1-基础查询

BUILDBASICQUERY(PARAMS, REQUEST);

1/2.2排序

("DEFAULT", PARAMS.QETSORTBY()))

IF (STRINGUTILS.ISNOTBLANK(PARAMS.GETSORTBY()) && !ST

STRINGUTILS.EQUALS

REQUEST.SOURCE().SORT(PARAMS.GETSORTBY(), SORTORDER.DESC);

1/2.3-分页

MS.GETSIZE();

REQUEST.SOURCEQ.FROM((PARAMS.QETPAQEO - 1) * PARAIS.GETSIZEQ).SIZE(DARAMS.QET

重启工程后,可以点击测试:

WWW.ITHEIMA.COM

如家

搜索

全部结果:

深圳

北京

上海

城市

杭州

二钻

四钻

四星

五钻

三钻

星级

五星

7天酒店

华美达

皇冠假日

喜来登

速8

和颐

万豪

如家

万怡

品牌

希尔顿

300-600元

价格

600-1500元

100元以下

100-300元

1500元以上

默认!

评价格

共30家酒店1/6

地图预览

如家酒店(北京西客站丽泽桥店)

459

如家酒店

东一环

二钻

立即预定

位于北京西站/丽泽商务区周边,西三环南路44

4.7分/5分

号-218

朝阳区

1分钟前有人预订了该酒店

西城区

北京西站

7号线

如家酒店(北京国展三元桥店)

458


4.3.6.添加高亮
高亮的技术实现与我们单测一模一样,分别是:
与query平级的高亮处理(因为高亮不影响数据结果集大小,所以放在哪里都可以)

TRY

1/1-准备请求

( ...INDICES:"HOTEL182");

SEARCHREQUEST REQUEST HEW SEARCHREQUEST(

1/2-准备DSL语句

1/2.1-基础查询

BUILDBASICQUERY(PARAMS, REQUEST);

1/2.2 排序

IF (STRINGUTILS,ISHOTBLANK(PARANS.GETSORTBYO) BS ISTRINGUTINGUTILS.EQUALS('DEFAULT", PARANS.GETSORTBY

REQUEST.SOURCE().SORT(PARAMS.GETSORTBY(), SORTORDER.DESC);

1/2.3-分页

- 1) * PARAMS.GETSIZE()).SIZE(PARAMS.GETSIZE());

REQUEST.SOURCE().FROM((PARAMS.GETPAGE() - 1)

2.4-高亮

REQUEST.SOURCE().HIQHLIGHTER(NEW HIGHLIQHTBUILDER(

.FIELD("NAME")

.REQUIREFIELDMATCH(FALSE));

3-3-发送请求

SEARCHRESPONSE SEARCH ; RESTHIGHLEVELCLIENT.SEARCH(REQUEST, REQUESTOPTIONS.DEFAULT);


返回结果处理,重新设置name属性

2 PAGERESULT HANDLERESPONSE(SEARCHRESPONSE SEARCHRESPONSE)

PRIVATE PA

1/0-实例化返回结果

PAGERESULT RESULT - NEW PAGERESULT();

/11拿到最外层HITS

SEARCHHITS HITS - SEARCHRESPONSE.GETHITS();

2-循环遍历内层HITS

LIST<HOTELDOC> HOTELDOCLIST - NEW ARRAYLIST<>();

FOR (SEARCHHIT HIT : HITS) {

1/3-获取每一个里面的SOUCE

STRING SOURCEJSON - HIT.GETSOURCEASSTRING();

1/4-输出SOURCE转换成HOTELDOC

HOTELDOC HOTELDOC - JSON.PARSEOBJECT(SOURCEJSON, HOTELDOC.CLASS);

//5-获取高亮数据

MAP<STRING, HIGHLIGHTFIELD> HIGHLIGHTFIELDS - HIT.GETHIGHTIGHTFIELDSO;

IF (NULL !: HIGHLIGHTFIELDS & HIGHLIGHTFIELDS.CONTAINSKEY("NAME")) (

HIGHLIGHTFIELDS.GET("NAME").GETFRAGMENTS()[O].STRING();

STRING NAME

HOTELDOC.SETNAME(NAME)


Java

运行代码复制代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

private PageResult handleResponse(SearchResponse searchResponse) {


// 0-实例化返回结果

PageResult result = new PageResult();


// 1- 拿到最外层hits

SearchHits hits = searchResponse.getHits();


// 2-循环遍历内层hits

List<HotelDoc> hotelDocList = new ArrayList<>();

for (SearchHit hit : hits) {

// 3-获取每一个里面的souce

String sourceJson = hit.getSourceAsString();

// 4-输出source转换成HotelDoc

HotelDoc hotelDoc = JSON.parseObject(sourceJson, HotelDoc.class);

// 5-获取高亮数据

Map<String, HighlightField> highlightFields = hit.getHighlightFields();

if (null != highlightFields && highlightFields.containsKey("name")) {

String name = highlightFields.get("name").getFragments()[0].string();

hotelDoc.setName(name);

}


hotelDocList.add(hotelDoc);

}


result.setTotal(hits.getTotalHits().value);

result.setHotels(hotelDocList);


return result;

}

相关文章
|
22小时前
|
SQL Java 数据库连接
持久层框架MyBatisPlus
本节介绍MyBatisPlus,一款基于MyBatis的增强型ORM框架,简化单表增删改查操作。通过引入`mybatis-plus-boot-starter`并继承`BaseMapper`,即可实现无SQL的CRUD功能,显著提升开发效率。同时支持`@TableName`、`@TableId`、`@TableField`等注解,灵活处理表名、字段映射问题,让数据库操作更简洁、高效。(238字)
|
21小时前
|
安全 数据安全/隐私保护
SpringSecurity核心功能
SpringSecurity 是功能强大的鉴权框架,支持表单、OAuth2.0、SAML2.0、CAS 等多种认证方式,可扩展自定义逻辑。提供基于 URL、方法、SPEL 的细粒度授权,支持 RBAC 模型与动态配置,并具备防御 CSRF 等攻击的安全机制,全面保障应用安全。
|
20小时前
Arrays.asList之后不要调用修改操作
`Arrays.asList()`返回的列表不可直接增删元素,因其底层为固定大小的内部类,调用`add`等方法会抛出`UnsupportedOperationException`;且列表与原数组共享数据,修改数组内容会同步反映到列表中。
|
20小时前
|
NoSQL 关系型数据库 MySQL
MySQL 并发控制核心原理与实践技巧
本文深入解析MySQL高并发场景下的数据一致性难题,涵盖事务隔离级别、锁机制与乐观锁原理,结合电商秒杀、订单重复等真实案例,提供隔离级别选择、悲观锁/乐观锁应用及Redis分流等实战策略,助力开发者平衡性能与一致性。
|
20小时前
|
安全
SimpleDateFormat不要定义为static
SimpleDateFormat非线程安全,避免定义为static;若需静态使用,应加锁或采用ThreadLocal封装。JDK8推荐使用Instant、LocalDateTime和DateTimeFormatter,具备线程安全、不可变等优势,更简单高效。
|
21小时前
|
机器学习/深度学习 人工智能 自然语言处理
AI群策群力术:让多个大模型一起干活不摸鱼
想让AI回答更准确?别指望一个模型包打天下!就像做菜找多个大厨试味,提示词集成(Prompting Ensembling)让多个提示词协同作战,通过民主投票选出最佳答案。从自一致性(Self-Consistency)到多样化推理(DiVeRSe),掌握这些技巧让你的AI应用准确率飙升!#人工智能 #提示词工程 #机器学习 #AI优化
|
20小时前
|
存储 NoSQL Linux
2.4 Linux系统中的安装启动和连接
本文介绍在Linux系统部署单机MongoDB用于生产环境的完整步骤,包括下载、解压、目录配置、日志与数据路径设置、配置文件编写及服务启停方法。操作类似Windows,通过配置`mongod.conf`实现后台运行,支持命令行与图形工具连接,并提供防火墙处理与安全关闭服务方案,确保稳定运行。
|
22小时前
|
存储 NoSQL 网络协议
Redis集群部署指南
本教程基于CentOS7讲解Redis集群搭建,涵盖单机安装、主从复制、哨兵模式及分片集群的配置与测试,详细演示了多实例部署、节点关联、故障转移与数据分片等核心操作。
 Redis集群部署指南
|
20小时前
|
消息中间件 Linux Shell
RabbitMQ部署指南
本文介绍了RabbitMQ在CentOS 7上基于Docker的单机与集群部署方案。内容涵盖镜像安装、DelayExchange插件配置,并详细说明了普通模式与镜像模式集群的搭建及测试方法,重点解析了镜像队列的高可用机制。此外,还引入了3.8版本后推荐的仲裁队列,展示其自动容灾与动态扩容能力,为构建稳定可靠的消息中间件系统提供完整实践指南。(239字符)
 RabbitMQ部署指南
|
21小时前
|
消息中间件 存储 数据挖掘
应用架构图
技术架构是将业务需求转化为技术实现的关键过程,涵盖分层设计、技术选型与系统集成。本文详解单体与分布式架构,包括展现层、业务层、数据层及基础层的设计原则,并通过调用关系图明确系统边界与内外依赖,支撑高效稳定的技术体系构建。
应用架构图