ElasticSearch关于DSL检索
前言
在学习ES的过程中,数据检索的方式可以是Sql、一些语言客户端(http)、EQL、DSL,其中DSL是ES进行搜索不可或缺的东西,DSL究竟是什么,怎么用,有哪些需要注意的呢,接下来一一进行探索。
DSL定义
DSL全拼 Domain Special Language(领域特定语言),是ES提供的一种以Json体作为请求体的查询语言。
可以把它作为一种语法树,包括两种类型:独立/子查询和组合查询,组合查询可以理解为由其它组合查询和独立查询的组合。
独立查询包括查询类型如 term、match、range等;
组合查询包括如bool、dix_max等。
Query和Filter Context
默认情况下,ES搜索的结果是按相关性算分进行排序的,算分越高越靠前。
如下,不同的查询类型对算分有不同的影响。
Query context
Query context 决定了文档的匹配度,通过相关性算分进行排序,算分结果会出现在结果的_score字段中。
Filter context
Filter context 决定了文档是否匹配,不会进行相关性算分,答案只有是和否。
它比较适合匹配例如时间、数字、Boolean等结构化类型数据。
而且Filter搜索的结果会被ES进行缓存,以加快再次搜索速度。
我们通过定义一下索引文档,索引名 products,以便下面演示:
POST http://127.0.0.1:9200/products/_bulk
{ "index": { "_id": 1 }}
{ "productID" : "XHDK-A-1293-#fJ3","desc":"My iPhone", "createTime":"2023-08-16", "price": 10, "status":true}
{ "index": { "_id": 2 }}
{ "productID" : "KDKE-B-9947-#kL5","desc":"My iPad", "createTime":"2023-08-18", "price": 20, "status":true}
{ "index": { "_id": 3 }}
{ "productID" : "JODL-X-1937-#pV7","desc":"My MBP", "createTime":"2023-08-20", "price": 30.5, "status":false}
注意body体最后记得换行。
进行例如以下搜索:
GET http://127.0.0.1:9200/products/_search
{
"query": {
"bool": {
"must": {
"match": {"desc": "iphone"}
},
"filter": [
{"term": {"status": true}},
{"range": {"createTime": {"gte": "2023-08-16"}}}
]
}
}
}
其中:
1、最外层query表示它是一个query context;
2、must下也是一个query context;
3、filter下是一个filter context,包括了term和range。
分类
组合查询
1、bool查询, 比较常用的一个
它组合了多个查询,包括must、should、filter、must not。其中,
must:必须匹配,会算分
should:选择性匹配,会算分
filter:必须匹配,不算分,使用缓存
must mot:必须不匹配,不算分,使用缓存
可以理解成,bool词查询时,json下一层会接上面的词,如must体,如下:
{
"query": {
"bool": {
"must": {
"match": {"desc": "iphone"}
}
}
}
2、Boosting查询,改变相关性算分
包括positive和negative,positive正常匹配,negative会降低相关性分数,如下所示:
GET http://127.0.0.1:9200/products/_search
{
"query": {
"boosting": {
"positive": {
"term": {"status": true}
},
"negative": {
"term": {"desc": "iphone"}
},
// 原来的0.5倍
"negative_boost": 0.5
}
}
}
搜索结果,部分如下:
"hits":
[
{
"_index": "products",
"_id": "2",
"_score": 0.4700036,
"_source": {
"productID": "KDKE-B-9947-#kL5",
"desc": "My iPad",
"createTime": "2023-08-18",
"price": 20,
"status": true
}
},
{
"_index": "products",
"_id": "1",
"_score": 0.2350018,
"_source": {
"productID": "XHDK-A-1293-#fJ3",
"desc": "My iPhone",
"createTime": "2023-08-16",
"price": 10,
"status": true
}
}
]
可以看到 “_id”: “1”,_score分数由于命中negative条件,分值更低了。当然结果都需要满足positive。
3、Constant score query搜索
顾名思义,分数是固定常量,默认是1,也可以根据 "boost"变量进行指定,简化算分的逻辑,有助于搜索性能,如下所示:
{
"query": {
"constant_score": {
"filter": {
"term": {"status": true}
},
"boost":100
}
}
}
4、其它,还有查询分数为最大匹配项分数;函数算分 支持包括计算权重等。
可参考地址>
全文检索
Full-text search,针对文本进行分词,分词默认使用文档索引时使用的分词器。
常见的全文检索方式有:文本匹配match、词组匹配match phrase、逻辑与或非多串查询等等
1、match
对于文本会进行分词,再进行搜索;同时也支持数字、boolean、日期等。
查询时需要匹配好类型,如boolean类型字段不能传非boolean字符串。
同时可以指定分词器或者最小匹配数或者逻辑关系等等条件,如下:
{
"query": {
"match": {
"status": {
"query": true,
"operator": "and"
}
}
}
}
2、match_phrase,词组搜索
GET API http://127.0.0.1:9200/products/_search
{
"query": {
"match_phrase": {
"desc": {
"query": "my iphone"
}
}
}
}
my tel iphone 搜索不到,match_phrase改match可搜索到。
3、Query String VS Simple Query String
Query String:会使用严格的语法来解析查询串,支持例如 AND OR NOT
{
"query": {
"query_string": {
"query": "My AND iphone",
"default_field": "desc"
}
}
}
只会查出id=1的文档,desc为“My iPhone”,AND被解析成逻辑与。
Simple Query String:类似于Query String,容忍语法错误,不会解析AND OR NOT,只支持部分语法如(+ - |等),Term之间默认关系是OR,可以指定default_operator为AND NOT。
{
"query": {
"simple_query_string": {
"query": "My AND iphone",
"fields": ["desc"],
"default_operator": "OR"
}
}
}
3条商品文档都会被查询出来。 query 中的AND并不会被处理成逻辑关系
精确匹配
这种被称为 Term-level queries,用来查询结构化数据,例如价格、商品id、时间等等
精确查询不会进行分词。
其包括了有 exists、fuzzy、ids、range、prefix、regexp、term、terms等。
1、exists
用来判断某个字段是否有值,返回包含该字段值的文档。
不包含的原因如下所示:
1)、字段值为null或[]
2)、mapping设置了字段 index为false
3)、字段超过了ignore_above的长度
如下:可以查询到数据
{
"query": {
"exists": {
"field": "desc"
}
}
}
2、term
精确匹配词项,注意避免使用term进行text文本的搜索。
如下所示,匹配价格字段:
{
"query": {
"term": {
"price": {
"value": "10"
}
}
}
}
如果进行文本text类型的字段搜索,不会进行分词,所以会导致很可能搜索不到的情况。
如下情况搜索不到,应该使用match,才会分词搜索。
{
"query": {
"term": {
"desc": {
"value": "My"
}
}
}
}
{
"query": {
"term": {
"desc": {
"value": "my iphone"
}
}
}
}
如下会搜索到,匹配到my词的倒排索引:
{
"query": {
"term": {
"desc": {
"value": "my"
}
}
}
}
脚本搜索
ES中脚本查询默认使用的是painless脚本,脚本查询是Filter Context查询的一种。脚本查询可能导致检索速度降低。
painless是ES专门用来在ES中定义脚本的一种特殊语言。
可以通过脚本来创造一个新的字段,它可以用于字段搜索、排序、聚合分析等等场景。
如下,查询价格小于等于10的:
{
"query": {
"bool": {
"filter": {
"script":{
"script": "return doc['price'].value <= 10;"
}
}
}
}
}
总结
ES提供的DSL搜索不止文章中提到的这些,文章列出了大部分常用的,一种是match用于text的文本搜索,会进行分词;一种是term用于结构化字段的精确搜索,不会进行分词。
另外,如果按查询上下文进行搜索的话,一种是按Query Context、另一种是Filter Context,前者会进行算分,按算分高低进行结果的排序,后者不会进行算分,同时对结果进行缓存,提升再次检索的速度。