DSL查询概述Elasticsearch提供了基于JSON的DSLDomain Specific Language语句来定义查询条件其JavaAPI就是在组织DSL条件。Elasticsearch的查询可以分为两大类叶子查询Leafqueryclauses一般是在特定的字段里查询特定值属于简单查询很少单独使用。复合查询Compoundqueryclauses以逻辑方式组合多个叶子查询或者更改叶子查询的行为方式。语法结构GET /{索引库名}/_search { query: { 查询类型: { // .. 查询条件 } } }如查询所有GET /items/_search { query: { match_all: { } } }会返回took本次查询耗时单位 mstimed_out查询是否超时_shards分片执行情况total总分片数successful成功分片数skipped跳过分片数failed失败分片数hits查询命中结果total命中文档总数value命中数量relationeq精确值gte大于等于max_score最高相关性得分hits具体命中的文档列表例{took:17,timed_out:false,_shards:{total:1,successful:1,skipped:0,failed:0},hits:{total:{value:3,relation:eq},max_score:1.0,hits:[{_index:item,_type:_doc,_id:hBQUXp4Bz6ExaZwBC6H-,_score:1.0,_source:{id:100002644680,name:【千玺代言】华为新品 HUAWEI nova 4 极点全面屏手机 2000万超广角三摄 8GB128GB 蜜语红 全网通双卡双待,price:45700,image:https://m.360buyimg.com/mobilecms/s720x720_jfs/t1/20085/14/1076/149604/5c0f8dd2Ebafd3bfd/0cb34a7826cbe1c3.jpg!q70.jpg.webp,category:手机,brand:华为,sold:0,commentCount:0,isAD:false,updateTime:1556640000000}}}]}}基于安全考虑match_all只会查询出10条数据叶子查询概述文档[Query DSL | Elasticsearch Guide 7.12] | Elastic常见查询全文检索查询Full Text Queries利用分词器对用户输入搜索条件先分词得到词条然后再利用倒排索引搜索词条。例如matchmulti_match**精确查询**Term-level queries不对用户输入搜索条件分词根据字段内容精确值匹配。但只能查找keyword、数值、日期、boolean类型的字段。例如idstermrange地理坐标查询用于搜索地理位置搜索方式很多例如geo_bounding_box按矩形搜索geo_distance按点和半径搜索全文检索查询matchGET /{索引库名}/_search { query: { match: { 字段名: 搜索条件 } } }例GET/item/_search{query:{match:{name:手机}}}multi_matchGET /{索引库名}/_search { query: { multi_match: { query: 搜索条件, fields: [字段1, 字段2] } } }例GET/item/_search{query:{multi_match:{query:华为,fields:[name,brand]}}}精确查询精确查询英文是Term-level query顾名思义词条级别的查询。也就是说不会对用户输入的搜索条件再分词而是作为一个词条与搜索的字段内容精确值匹配。因此推荐查找keyword、数值、日期、boolean类型的字段。例如idprice城市地名人名termGET /{索引库名}/_search { query: { term: { 字段名: { value: 搜索条件 } } } }例GET/item/_search{query:{term:{brand.keyword:{value:华为}}}}虽然brand不是纯keyword但es生成了子字段keywordsrangeGET /{索引库名}/_search { query: { range: { 字段名: { gte: {最小值}, lte: {最大值} } } } }range是范围查询对于范围筛选的关键字有gte大于等于gt大于lte小于等于lt小于例GET/item/_search{query:{range:{price:{gte:45700,lte:46000}}}}复合查询复合查询大致可以分为两类第一类基于逻辑运算组合叶子查询实现组合条件例如bool第二类基于某种算法修改查询时的文档相关性算分从而改变文档排名。例如function_scoredis_max算分函数查询当我们利用match查询时文档结果会根据与搜索词条的关联度打分_score返回结果时按照分值降序排列。从elasticsearch5.1开始采用的相关性打分算法是BM25算法公式如下基本语法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的运行流程如下根据原始条件查询搜索文档并且计算相关性算分称为原始算分query score根据过滤条件过滤文档符合过滤条件的文档基于算分函数运算得到函数算分function score将原始算分query score和函数算分function score基于运算模式做运算得到最终结果作为相关性算分。因此其中的关键点是过滤条件决定哪些文档的算分被修改算分函数决定函数算分的算法运算模式决定最终算分结果示例给IPhone这个品牌的手机算分提高十倍分析如下过滤条件品牌必须为IPhone算分函数常量weight值为10算分模式相乘multiply对应代码如下GET /hotel/_search { query: { function_score: { query: { .... }, // 原始查询可以是任意条件 functions: [ // 算分函数 { filter: { // 满足的条件品牌必须是Iphone term: { brand: Iphone } }, weight: 10 // 算分权重为2 } ], boost_mode: multiply // 加权模式求乘积 } } }bool查询bool查询即布尔查询。就是利用逻辑运算来组合一个或多个查询子句的组合。bool查询支持的逻辑运算有must必须匹配每个子查询类似“与”should选择性匹配子查询类似“或”must_not必须不匹配不参与算分类似“非”filter必须匹配不参与算分bool查询的语法如下GET /items/_search { query: { bool: { must: [ {match: {name: 手机}} ], should: [ {term: {brand: { value: vivo }}}, {term: {brand: { value: 小米 }}} ], must_not: [ {range: {price: {gte: 2500}}} ], filter: [ {range: {price: {lte: 1000}}} ] } } }出于性能考虑与搜索关键字无关的查询尽量采用must_not或filter逻辑运算避免参与相关性算分。例比如我们要搜索手机但品牌必须是华为价格必须是900~1599那么可以这样写GET /items/_search { query: { bool: { must: [ {match: {name: 手机}} ], filter: [ {term: {brand: { value: 华为 }}}, {range: {price: {gte: 90000, lt: 159900}}} ] } } }排序elasticsearch默认是根据相关度算分_score来排序但是也支持自定义方式对搜索结果排序。不过分词字段无法排序能参与排序字段类型有keyword类型、数值类型、地理坐标类型、日期类型等。语法GET /indexName/_search { query: { match_all: {} }, sort: [ { 排序字段: { order: 排序方式asc和desc } } ] }例按照商品价格排序升序销量降序GET/item/_search{query:{match_all:{}},sort:[{price:{order:desc},sold:{order:asc}}]}分页elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果from从第几个文档开始size总共查询几个文档类似于mysql中的limit ?, ?简单分页语法GET /item/_search { query: { match_all: {} }, from: 0, // 分页开始的位置默认为0 size: 10, // 每页文档数量默认10 sort: [ { price: { order: desc } } ] }例GET/item/_search{query:{match_all:{}},from:0,size:10,sort:[{price:desc,sold:asc}]}深度分页elasticsearch的数据一般会采用分片存储也就是把一个索引中的数据分成N份存储到不同节点上。这种存储方式比较有利于数据扩展但给分页带来了一些麻烦。比如一个索引库中有100000条数据分别存储到4个分片每个分片25000条数据。现在每页查询10条查询第99页。那么分页查询的条件如下GET /items/_search { from: 990, // 从第990条开始查询 size: 10, // 每页查询10条 sort: [ { price: asc } ] }找出前1000名只能在每一个分片上都找出排名前1000的数据然后汇总到一起重新排序才能找出整个索引库中真正的前1000名此时截取990~1000的数据即可。但查询的分页深度更深时汇总数据过多对内存和CPU会产生非常大的压力因此elasticsearch会禁止from size超过10000的请求。针对深度分页elasticsearch提供了两种解决方案search after分页时需要排序原理是从上一次的排序值开始查询下一页数据。官方推荐使用的方式。scroll原理将排序后的文档id形成快照保存下来基于快照做分页。官方已经不推荐使用。一般我们采用限制分页深度的方式即可无需实现深度分页。高亮通过查询筛选出应强调的关键字经前端处理变得醒目这叫高亮显示前端中一般高亮词条都被加了em标签em标签都添加了红色样式因此实现高亮的思路就是用户输入搜索关键字搜索数据服务端根据搜索关键字到elasticsearch搜索并给搜索结果中的关键字词条添加html标签前端提前给约定好的html标签添加CSS样式基本实现GET/{索引库名}/_search{query:{match:{搜索字段:搜索关键字}},highlight:{fields:{高亮字段名称:{pre_tags:em,post_tags:/em}}}}注意搜索必须有查询条件而且是全文检索类型的查询条件例如match参与高亮的字段必须是text类型的字段默认情况下参与高亮的字段要与搜索字段一致除非添加required_field_matchfalse例GET/item/_search{query:{match:{name:华为新品}},highlight:{fields:{name:{pre_tags:em,post_tags:em}}}}会输出类似highlight:{name:[【千玺代言】em华emem为emem新emem品em HUAWEI nova 4 极点全面屏手机 2000万超广角三摄 8GB128GB 蜜语红 全网通双卡双待]}其中要高亮的都被em标签包围可以自己规定任意标志包围默认为总结查询的DSL是一个大的JSON对象包含下列属性query查询条件from和size分页条件sort排序条件highlight高亮条件