Elasticsearch 之 DQL 查询语法 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 31 32 33 34 35 36 37 38 39 40 PUT /product/_doc/1 { "name" : "xiaomi phone" , "desc" : "shouji zhong de zhandouji" , "price" : 3999 , "tags" : [ "xingjiabi" , "fashao" , "buka" ] } PUT /product/_doc/2 { "name" : "xiaomi nfc phone" , "desc" : "zhichi quangongneng nfc,shouji zhong de jianjiji" , "price" : 4999 , "tags" : [ "xingjiabi" , "fashao" , "gongjiaoka" ] } PUT /product/_doc/3 { "name" : "nfc phone" , "desc" : "shouji zhong de hongzhaji" , "price" : 2999 , "tags" : [ "xingjiabi" , "fashao" , "menjinka" ] } PUT /product/_doc/4 { "name" : "xiaomi erji" , "desc" : "erji zhong de huangmenji" , "price" : 999 , "tags" : [ "low" , "bufangshui" , "yinzhicha" ] } PUT /product/_doc/5 { "name" : "hongmi erji" , "desc" : "erji zhong de kendeji" , "price" : 399 , "tags" : [ "lowbee" , "xuhangduan" , "zhiliangx" ] }
以后语句将忽略type:_doc
Query_String
1 2 3 4 5 6 7 8 9 timeout: (1) 设置:默认没有timeout,如果设置了timeout,那么会执行timeout机制。 (2) Timeout机制:假设用户查询结果有1W条数据,但是需要10s才能查询完毕 但是用户设置了1s的timeout,那么不管当前一共查询到了多少数据,都会在1s后ES将停止查询,并返回当前数据。 GET /_search?timeout=1s
1 2 GET /product/_search?q=xiaomi
1 2 GET /product/_search?q=name:xiaomi
上面两者的区别:
q=xiaomi :将所有字段拼接成一个长字符串进行匹配
q=name:xiaomi :直接按照name进行匹配
1 2 GET /product/_search?from=0&size=2
1 2 GET /product/_search?sort=price:asc
Query DQL 1 2 3 4 5 6 7 GET /product/_search { "query" :{ "match_all" : {} } }
match 重点掌握语句!!!
1 2 3 4 5 6 7 8 9 GET /product/_search { "query" : { "match" : { "name" : "nfc" } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 GET /product/_search { "query" : { "multi_match" : { "query" : "nfc" , "fields" : ["name" ,"desc" ] } }, "sort" : [ { "price" : "desc" } ] }
1 2 3 4 5 6 7 8 9 10 GET /product/_search { "query" : { "multi_match" : { "query" : "nfc" , "fields" : ["name" ,"desc" ] } } }
1 2 3 4 5 6 7 8 9 10 GET /product/_search { "query" :{ "match" : { "name" : "nfc" } }, "_source" : ["name" ,"price" ] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 GET /product/_search { "query" :{ "match_all" : {} }, "sort" : [ { "price" : "asc" } ], "from" : 0 , "size" : 2 }
Full-text queries 全文检索 1 2 3 4 5 6 7 8 9 10 GET /product/_search { "query" : { "term" : { "name" : "nfc phone" } } }
1 2 3 4 5 6 7 8 9 10 11 12 GET /product/_search { "query" : { "bool" : { "must" : [ {"term" :{"name" :"nfc" }}, {"term" :{"name" :"phone" }} ] } } }
1 2 3 4 5 6 7 8 9 GET /product/_search { "query" : { "terms" : { "name" :["nfc" ,"phone" ] } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 GET /product/_search { "query" : { "match" : { "name" : "xiaomi nfc zhineng phone" } } } GET /_analyze { "analyzer" : "standard" , "text" :"xiaomi nfc zhineng phone" }
Phrase search 1 2 3 4 5 6 7 8 9 10 GET /product/_search { "query" : { "match_phrase" : { "name" : "nfc phone" } } }
Query and filter
bool:可以组合多个查询条件,bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值。
must :必须满足
子句(查询)必须出现在匹配的文档中,并将有助于得分。
filter :过滤器 不计算相关度分数 ,cache☆
子句(查询)必须出现在匹配的文档中。但是不像 must查询的分数将被忽略。Filter子句在filter上下文中执行,这意味着计分被忽略,并且子句被考虑用于缓存。
should :可能满足 or
子句(查询)应出现在匹配的文档中。
must_not :必须不满足 不计算相关度分数 not
子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着计分被忽略,并且子句被视为用于缓存。由于忽略计分,0因此将返回所有文档的分数。
minimum_should_match :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 GET /product/_search { "query" : { "bool" :{ "must" : [ {"match" : { "name" : "xiaomi" }}, {"match" : {"desc" : "shouji" }} ], "filter" : [ {"match_phrase" :{"name" :"xiaomi phone" }}, {"range" : { "price" : { "gt" : 1999 } }} ] } } }
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 31 GET /product/_search { "query" : { "bool" :{ "must" : [ {"match" : { "name" : "xiaomi" }} ], "must_not" : [ {"match" : { "name" : "erji" }} ], "should" : [ {"match" : { "desc" : "nfc" }} ], "filter" : [ {"range" : { "price" : { "gt" : 4999 } }} ] } } }
minimum_should_match:参数指定should返回的文档必须匹配的子句的数量或百分比。 如果bool查询包含至少一个should子句,而没有must或 filter子句,则默认值为1。否则,默认值为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 GET /product/_search { "query" : { "bool" :{ "must" : [ {"match" : { "name" : "nfc" }} ], "should" : [ {"range" : { "price" : {"gt" :1999 } }}, {"range" : { "price" : {"gt" :3999 } }} ], "minimum_should_match" : 1 } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 GET /product/_search { "query" : { "bool" : { "filter" : { "bool" : { "should" : [ { "range" : {"price" : {"gt" : 1999 }}}, { "range" : {"price" : {"gt" : 3999 }}} ], "must" : [ { "match" : {"name" : "nfc" }} ] } } } } }
Compound queries 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 GET /product/_search { "query" : { "constant_score" :{ "filter" : { "bool" : { "should" :[ {"term" :{"name" :"xiaomi" }}, {"term" :{"name" :"nfc" }} ], "must_not" :[ {"term" :{"name" :"erji" }} ] } }, "boost" : 1.2 } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 GET /product/_search { "query" : { "constant_score" : { "filter" : { "bool" :{ "should" :[ {"match_phrase" :{"name" :"xiaomi nfc phone" }}, { "bool" :{ "must" :[ {"term" :{"name" :"phone" }}, {"range" :{"price" :{"lte" :"2999" }}} ] } } ] } } } } }
Highlight search 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 GET /product/_search { "query" : { "match_phrase" : { "name" : "nfc phone" } }, "highlight" :{ "fields" :{ "name" :{} } } } "highlight" : { "name" : [ "<em>nfc</em> <em>phone</em>" ] }
Deep paging问题
假设我要分页获取第5001~5050条数据时,由于数据是无序散落在各个shard
分片中的,所以进行分页排序的时候,需要将各个shard
分片进行排序,获取每个分片的【0 - 5050】条数据,然后进行合并,最后取出合适的50条数据,然后丢弃其他数据。
这种操作是十分损耗性能的,尽量避免深度分页查询,当你的数据超过1W,不要使用,返回结果不要超过1000个,500以下为宜。
通过使用Scroll search
来避免部分分页查询,在查询中添加?scroll
参数
1 2 3 4 5 6 7 8 9 GET /product/_search?scroll=1m { "query" :{ "match_all" :{} }, "sort" :[{"price" :"asc" }], "size" :2 }
通过这样查询,返回值会带上一个_scroll_id
结果
当进行下一页时,直接通过上一次返回的scroll_id
进行查询即可
1 2 3 4 5 6 GET /_search/scroll { "scroll" :"1m" , scroll_id:"xxxxxxxxxxxx" }
他的缺点是只能下一页,没办法上一页,不适合实时查询
Filter缓存原理
当使用 term词项去倒排索引表进行搜索时,返回的一条条数据,filter会通过一个Bit数组存储,每个词项term对应一个bit数组,1表示匹配成功,0表示匹配失败。
计算多个filter条件的组合时,直接进行bit数组的与运算就能得出相应的结果,在一定条件下,filter会将查询的bit数组进行缓存。
Mapping 概念:mapping就是ES数据字段field的type元数据,ES在创建索引的时候,dynamic mapping会自动为不同的数据指定相应mapping,mapping中包含了字段的类型、搜索方式(exact value或者full text)、分词器等。
1 2 查看mapping GET /product/_mappings
1 2 3 4 5 6 7 Dynamic mapping “Elasticsearch”:text/keyword 123456 => long ?为什么不是integer 123.123 => double true false => boolean 2020-05-20 => date
为啥price是long类型而不是integer?因为es的mapping_type是由JSON分析器检测数据类型,而Json没有隐式类型转换(integer=>long or float=> double),所以dynamic mapping会选择一个比较宽的数据类型。
1 2 3 搜索方式: exact value 精确匹配:在倒排索引过程中,分词器会将field作为一个整体创建到索引中, full text全文检索:分词、近义词同义词、混淆词、大小写、词性、过滤、时态转换等(normaliztion)
数据类型 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 【核心类型】: 数字类型: long, integer, short, byte, double, float, half_float, scaled_float 在满足需求的情况下,尽可能选择范围小的数据类型。 2.字符串:string: 2.1 keyword:适用于索引结构化的字段,可以用于过滤、排序、聚合。 keyword类型的字段只能通过精确值(exact value)搜索到。 Id应该用keyword。 2.2 text: 当一个字段是要被全文搜索的,比如Email内容、产品描述,这些字段应该使用text类型。 设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。 text类型的字段不用于排序,很少用于聚合。 (解释一下为啥不会为text创建索引:字段数据会占用大量堆空间,尤其是在加载高基数text字段时。 字段数据一旦加载到堆中,就在该段的生命周期内保持在那里。 同样,加载字段数据是一个昂贵的过程,可能导致用户遇到延迟问题。这就是默认情况下禁用字段数据的原因) 2.3 有时,在同一字段中同时具有全文本(text)和关键字(keyword)版本会很有用:一个用于全文本搜索,另一个用于聚合和排序。 3.date(时间类型):exact value(精确匹配) 4.布尔类型:boolean 5.binary(二进制):binary 6.range(区间类型):integer_range、float_range、long_range、double_range、date_range 【复杂类型】: 1.Object:用于单个JSON对象 2.Nested:用于JSON对象数组 【地理位置】: 1.Geo-point:纬度/经度积分 2.Geo-shape:用于多边形等复杂形状 【特有类型】: 1.IP地址:ip 用于IPv4和IPv6地址 2.Completion:提供自动完成建议 3.Tocken_count:计算字符串中令牌的数量 4.Murmur3:在索引时计算值的哈希并将其存储在索引中 5.Annotated-text:索引包含特殊标记的文本(通常用于标识命名实体) 6.Percolator:接受来自query-dsl的查询 7.Join:为同一索引内的文档定义父/子关系 8.Rank features:记录数字功能以提高查询时的点击率。 9.Dense vector:记录浮点值的密集向量。 10.Sparse vector:记录浮点值的稀疏向量。 11.Search-as-you-type:针对查询优化的文本字段,以实现按需输入的完成 12.Alias:为现有字段定义别名。 13.Flattened:允许将整个JSON对象索引为单个字段。 14.Shape:shape 对于任意笛卡尔几何。 15.Histogram:histogram 用于百分位数聚合的预聚合数值。 16.Constant keyword:keyword当所有文档都具有相同值时的情况的 专业化。 【Array(数组)】:在Elasticsearch中,数组不需要专用的字段数据类型。 默认情况下,任何字段都可以包含零个或多个值,但是,数组中的所有值都必须具有相同的数据类型。 【ES 7新增】: 1.Date_nanos:date plus 纳秒 2.Features: 3.Vector:as
1 2 3 4 5 6 7 8 9 10 11 PUT /product { "mappings" : { "properties" : { "field" : { "mapping_parameter" : "parameter_value" } } } }
Mapping parameters index :是否对创建对当前字段创建索引,默认true,如果不创建索引,该字段不会通过索引被搜索到,但是仍然会在source元数据中展示
analyzer :指定分析器(character filter、tokenizer、Token filters)。
boost :对当前字段相关度的评分权重,默认1
coerce :是否允许强制类型转换 true “1”=> 1 false “1”=< 1
copy_to :拷贝字段值
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 31 32 33 34 PUT /product3 { "mappings" : { "properties" : { "date" : { "type" : "text" }, "desc" : { "type" : "text" , "analyzer" : "english" }, "name" : { "type" : "text" , "index" : "false" , "boost" : 1 }, "price" : { "type" : "Integer" , "coerce" : false }, "tags" : { "type" : "text" , "index" : "true" }, "parts" : { "type" : "object" }, "partlist" : { "type" : "nested" } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PUT copy_to { "mappings" : { "properties" : { "field1" : { "type" : "text" , "copy_to" : "field_all" }, "field2" : { "type" : "text" , "copy_to" : "field_all" }, "field_all" : { "type" : "text" } } } }
doc_values :为了提升排序和聚合效率,默认true,如果确定不需要对字段进行排序或聚合,也不需要通过脚本访问字段值,则可以禁用doc值以节省磁盘空间(不支持text和annotated_text)
dynamic :控制是否可以动态添加新字段
1 2 3 4 5 6 true 新检测到的字段将添加到映射中。(默认) false 新检测到的字段将被忽略。这些字段将不会被索引,因此将无法搜索,但仍会出现在_source返回的匹配项中。 这些字段不会添加到映射中,必须显式添加新字段。 strict 如果检测到新字段,则会引发异常并拒绝文档。必须将新字段显式添加到映射中
eager_global_ordinals:用于聚合的字段上,优化聚合性能。
Frozen indices(冻结索引):有些索引使用率很高,会被保存在内存中,有些使用率特别低,宁愿在使用的时候重新创建,在使用完毕后丢弃数据,Frozen indices的数据命中频率小,不适用于高搜索负载,数据不会被保存在内存中,堆空间占用比普通索引少得多,Frozen indices是只读的,请求可能是秒级或者分钟级。eager_global_ordinals不适用于Frozen indices
enable :只用于mapping中的object字段类型 。当设置为false时,其作用是使es不去解析该字段,并且该字段不能被查询和store ,只有在_source中才能看到(即查询结果中会显示的_source数据)。设置enabled为false,可以不设置字段类型,默认为object。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 PUT my_index{ "mappings": { "enabled": false }} PUT my_index{ "mappings": { "properties": { "session_data": { "type": "object", "enabled": false } } }}
fielddata :查询时内存 数据结构,在首次用当前字段聚合、排序或者在脚本中使用时,需要字段为fielddata数据结构,并且创建正排索引保存到堆中。
fields: 给field创建多字段,用于不同目的(全文检索或者聚合分析排序)
format :格式化
1 2 3 4 "date": { "type": "date", "format": "yyyy-MM-dd" }
ignore_above :text中的keyword长度,超过长度将被截断
ignore_malformed :忽略类型错误
1 2 3 4 5 6 7 8 9 10 11 12 13 PUT my_index{ "mappings": { "properties": { "number_one": { "type": "integer", "ignore_malformed": true }, "number_two": { "type": "integer" } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 PUT my_index/_doc/1 { "text" : "Some text value" , "number_one" :"foo" } PUT my_index/_doc/2 { "text" : "Some text value" , "number_two" : "foo" }
index_options :控制将哪些信息添加到反向索引中以进行搜索和突出显示。仅用于text字段
Index_phrases :提升exact_value查询速度,但是要消耗更多磁盘空间
Index_prefixes :前缀搜索
1 2 min_chars:前缀最小长度,>0,默认2(包含) max_chars:前缀最大长度,<20,默认5(包含)
1 2 3 4 "index_prefixes" : { "min_chars" : 1 , "max_chars" : 10 }
meta :附加元数据
norms :是否禁用评分(在filter和聚合字段上应该禁用)。
null_value :为null值设置默认值
proterties :除了mapping还可用于object的属性设置
search_analyzer :设置单独的查询时分析
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 31 32 33 34 PUT my_index{ "settings": { "analysis": { "filter": { "autocomplete_filter": { "type": "edge_ngram", "min_gram": 1, "max_gram": 20 } }, "analyzer": { "autocomplete": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "autocomplete_filter" ] } } } }, "mappings": { "properties": { "text": { "type": "text", "analyzer": "autocomplete", "search_analyzer": "standard" } } } }
1 2 3 4 PUT my_index/_doc/1 { "text" : "Quick Brown Fox" }
1 2 3 4 5 6 7 8 9 10 GET my_index/_search{ "query": { "match": { "text": { "query": "Quick Br", "operator": "and" } } } }
similarity :为字段设置相关度算法,支持BM25、claassic(默认TF-IDF)、boolean
store :设置字段是否仅查询
聚合查询 语法:"aggs":{}
1 2 3 4 5 6 7 8 9 10 11 12 13 GET /product/_search { "aggs" : { "your_group_name" : { "terms" : { "field" : "tags.keyword" } } }, "size" :0 }
1 2 3 4 5 6 7 8 9 10 11 PUT /product/_mapping { "properties" : { "tags" : { "type" : "text" , "fielddata" : true } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 GET /product/_search { "query" : { "bool" : { "filter" : [ { "range" : {"price" : {"gt" : 1999 }} } ] } }, "aggs" : { "tag_agg_group" : { "terms" : { "field" : "tags.keyword" } } }, "size" : 0 }
1 2 3 4 "avg": { "field": "your_avg_key" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 GET /product/_search { "aggs" : { "tag_agg_avg" : { "terms" : { "field" : "tags.keyword" , "order" : { "avg_price" : "desc" } }, "aggs" : { "avg_price" : { "avg" : { "field" : "price" } } } } }, "size" :0 }
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 31 32 GET /product/_search { "aggs" : { "tag_agg_group" : { "range" : { "field" : "price" , "ranges" : [ { "from" : 100 , "to" : 1000 }, { "from" : 1000 , "to" : 3000 }, { "from" : 3000 } ] }, "aggs" : { "price_agg" : { "avg" : { "field" : "price" } } } } }, "size" : 0 }