ElasticSearch高级查询语法

1. ES倒排索引

当数据写入ES时,数据将会通过“分词”被切分为不同的term,ES将term与其对应的文档列表建立一种映射关系,这种结构就是倒排索引。如下图所示:

为了进一步提升索引的效率,ES在term的基础上利用term的前缀或者后缀构建了term index,用于对term本身进行索引,ES实际的索引结构如下图所示:

这样当我们去搜索某个关键词时,ES 首先根据它的前缀或者后缀迅速缩小关键词的在term dictionary中的范围,大大减少了磁盘IO的次数。

  • 单词词典(Term Dictionary) :记录所有文档的单词,记录单词到倒排列表的关联关系;
  • 倒排列表(Posting List)-记录了单词对应的文档,由倒排索引项组成;
  • 倒排索引项(Posting):
    • 文档ID
    • 词频TF–该单词在文档中出现的次数,用于相关性评分;
    • 位置(Position)-单词在文档中分词的位置。用于短语搜索(match phrase query)
    • 偏移(Offset)-记录单词的开始结束位置,实现高亮显示。

Elasticsearch的JSON文档中的每个字段,都有自己的倒排索引。

可以指定对某些字段不做索引:

  • 优点︰节省存储空间
  • 缺点: 字段无法被搜索

2. 文档映射Mapping

Mapping类似数据库中的Schema的定义,作用如下:

  • 定义索引中的字段的名称
  • 定义字段的数据类型,例如字符串,数字,布尔等;
  • 字段,倒排索引的相关配置(Analyzer)。

ES中Mapping映射可以分为动态映射和静态映射:

  • 动态映射:在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。
  • 静态映射:静态映射是在Elasticsearch中也可以事先定义好映射,包含文档的各字段类型、分词器等,这种方式称之为静态映射。

动态映射(Dynamic Mapping)的机制,使得我们无需手动定义Mappings,Elasticsearch会自动根据文档信息,推算出字段的类型。但是有时候会推算的不对,例如地理位置信息。当类型如果设置不对时,会导致一些功能无法正常运行,例如Range查询。

  • Dynamic Mapping类型自动识别:

✒️示例

#删除原索引
DELETE /user
#创建文档(ES根据数据类型, 会自动创建映射)
PUT /user/_doc/1
{
  "name":"magic",
  "age":32,
  "address":"哈尔滨中央大街"
}
#获取文档映射
GET /user/_mapping

思考:能否后期更改Mapping的字段类型?

两方面考虑:

  • 新增加字段
    • dynamic设为true时,一旦有新增字段的文档写入,Mapping也同时被更新;
    • dynamic设为false,Mapping不会被更新,新增字段的数据无法被索引,但是信息会出现在_source中;
    • dynamic设置成strict(严格控制策略),文档写入失败,抛出异常。

true

false

strict

文档可索引

yes

yes

no

字段可索引

yes

no

no

Mapping被更新

yes

no

no

  • 对已有字段,一旦已经有数据写入,就不再支持修改字段定义。
    • Lucene实现的倒排索引,一旦生成后,就不允许修改;
    • 如果希望改变字段类型,可以利用 reindex API,重建索引。

原因:

  • 如果修改了字段的数据类型,会导致已被索引的数据无法被搜索;
  • 但是如果是增加新的字段,就不会有这样的影响。

📝测试

PUT /user
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "name": {
        "type": "text"
      },
      "address": {
        "type": "object",
        "dynamic": "true"
      }
    }
  }
}
# 插入文档报错,原因为age为新增字段,会抛出异常
PUT /user/_doc/1
{
  "name":"fox",
  "age":32,
  "address":{
    "province":"湖南",
    "city":"长沙"
  }
}

dynamic设置成strict,新增age字段导致文档插入失败。

修改dynamic后再次插入文档成功

#修改daynamic
PUT /user/_mapping
{
  "dynamic":true
}

对已有字段的mapping修改

  1. 如果要推倒现有的映射,你得重新建立一个静态索引;
  2. 然后把之前索引里的数据导入到新的索引里
  3. 删除原创建的索引
  4. 为新索引起个别名,为原索引名。

2.1 常用Mapping参数配置

2.1.1 index

index,控制当前字段是否被索引,默认为true;如果设置为false,该字段不可被搜索。

DELETE /user
PUT /user
{
  "mappings" : {
      "properties" : {
        "address" : {
          "type" : "text",
          "index": false
        },
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text"
        }
      }
    }
}

PUT /user/_doc/1
{
  "name":"magic",
  "address":"哈尔滨中央大街",
  "age":30
}

GET /user

GET /user/_search
{
  "query": {
    "match": {
      "address": "广州"
    }
  }
}

2.1.2 index options

index options有四种不同的基本配置,控制倒排索引记录的内容:

  • docs : 记录doc id
  • freqs:记录doc id 和term frequencies(词频)
  • positions: 记录doc id / term frequencies / term position
  • offsets: doc id / term frequencies / term posistion / character offsets

text类型默认记录postions,其他默认为 docs;记录内容越多,占用存储空间越大。

DELETE /user
PUT /user
{
  "mappings" : {
      "properties" : {
        "address" : {
          "type" : "text",
          "index_options": "offsets"
        },
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text"
        }
      }
    }
}

2.1.3 null_value

需要对Null值进行搜索,只有keyword类型支持设计Null_Value。

DELETE /user
PUT /user
{
  "mappings" : {
      "properties" : {
        "address" : {
          "type" : "keyword",
          "null_value": "NULL"
        },
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text"
        }
      }
    }
}

PUT /user/_doc/1
{
  "name":"fox",
  "age":32,
  "address":null
}

GET /user/_search
{
  "query": {
    "match": {
      "address": "NULL"
    }
  }
}

2.1.4 copy_to

        将字段的数值拷贝到目标字段,满足一些特定的搜索需求;copy_to的目标字段不出现在_source中。

# 设置copy_to
DELETE /address
PUT /address
{
  "mappings" : {
      "properties" : {
        "province" : {
          "type" : "keyword",
          "copy_to": "full_address"
        },
        "city" : {
          "type" : "text",
          "copy_to": "full_address"
        }
      }
    },
    "settings" : {
        "index" : {
            "analysis.analyzer.default.type": "ik_max_word"
        }
    }
}

PUT /address/_bulk
{ "index": { "_id": "1"} }
{"province": "湖南","city": "长沙"}
{ "index": { "_id": "2"} }
{"province": "湖南","city": "常德"}
{ "index": { "_id": "3"} }
{"province": "广东","city": "广州"}
{ "index": { "_id": "4"} }
{"province": "湖南","city": "邵阳"}

GET /address/_search
{
  "query": {
    "match": {
      "full_address": {
        "query": "湖南常德",
        "operator": "and"
      }
    }
  }
}

2.2 Index Template

Index Templates可以帮助你设定Mappings和Settings,并按照一定的规则,自动匹配到新创建的索引之上。

  • 模版仅在一个索引被新创建时,才会产生作用。修改模版不会影响已创建的索引;
  • 你可以设定多个索引模版,这些设置会被“merge”在一起;
  • 你可以指定“order”的数值,控制“merging”的过程。

2.2.1 模板的创建

PUT /_template/template_default
{
  "index_patterns": ["*"],  
  "order": 0,
  "version": 1,
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

PUT /_template/template_test
{
  "index_patterns": ["test*"], #匹配index的
  "order": 1,
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 1
  },
  "mappings": {
    "date_detection": false,
    "numeric_detection": true
  }
}

2.2.2 模板的工作方式

当一个索引被新创建时,配置信息的默认规则:

  • 应用Elasticsearch默认的settings和mappings
  • 应用order数值低的lndex Template中的设定
  • 应用order高的Index Template中的设定,之前的设定会被覆盖;
  • 应用创建索引时,用户所指定的Settings和Mappings,并覆盖之前模版中的设定。

2.3 Dynamic Template

Dynamic Tempate定义在某个索引的Mapping中。粒度更细,可以根据字段类型设置模板。

#Dynaminc Mapping 根据类型和字段名
DELETE my_index
PUT my_index/_doc/1
{
  "firstName":"Ruan",
  "isVIP":"true"
}

GET my_index/_mapping
DELETE my_index
PUT my_index
{
  "mappings": {
    "dynamic_templates": [
      { 
        "strings_as_boolean": {
          "match_mapping_type":   "string",
          "match":"is*",
          "mapping": {
            "type": "boolean"
          }
        }
      },
      {
        "strings_as_keywords": {
          "match_mapping_type":   "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

#结合路径
PUT /my_test_index
{
  "mappings": {
    "dynamic_templates": [
      {
        "full_name":{
          "path_match": "name.*",
          "path_unmatch": "*.middle",
          "mapping":{
            "type": "text",
            "copy_to": "full_name"
          }
        }
      }
    ]
  }
}

PUT /my_test_index/_doc/1
{
  "name":{
    "first": "John",
    "middle": "Winston",
    "last": "Lennon"
  }
}

GET /my_test_index/_search
{
  "query": {
    "match": {
      "full_name": "John"
    }
  }
}

3. ES高级查询Query DSL

        ES中提供了一种强大的检索数据方式,这种检索方式称之为Query DSL(Domain Specified Language),Query DSL是利用Rest API传递JSON格式的请求体(RequestBody)数据与ES进行交互,这种方式的丰富查询语法让ES检索变得更强大,更简洁。

Query DSL | Elasticsearch Guide [7.17] | Elastic

📰语法

GET /es_db/_doc/_search {json请求体数据}
#可以简化为下面写法
GET /es_db/_search {json请求体数据}

✒️示例

#无条件查询,默认返回10条数据
GET /es_db/_search
{
    "query":{
        "match_all":{}
    }
}

3.1 查询所有match_all

        使用match_all,默认只会返回10条数据(_search查询默认采用的是分页查询,每页记录数size的默认值为10);如果想显示更多数据,指定size。

GET /es_db/_search
等同于
GET /es_db/_search
{
    "query":{
        "match_all":{}
    }
}
#在查询中过滤
#不查看源数据,仅查看元字段
{
  "_source": false,
  "query": {
    ...
  } 
}
#只看以obj.开头的字段
{
  "_source": "obj.*",
  "query": {
    ...
  } 
}

3.1.1 返回指定条数size

        使用size,指定查询结果中返回指定条数(默认返回值10条)。

GET /es_db/_search
{
  "query": {
    "match_all": {}
  },
  "size": 100
}

思考: size可以无限增加吗?

  • 测试
GET /es_db/_search
{
  "query": {
    "match_all": {}
  },
  "size": 20000
}
  • 出现异常

异常原因:

  1. 查询结果的窗口太大,from + size的结果必须小于或等于10000,而当前查询结果的窗口为20000。
  2. 可以采用scroll api更高效的请求大量数据集。
  3. 查询结果的窗口的限制可以通过参数index.max_result_window进行设置。
PUT /es_db/_settings
{ 
  "index.max_result_window" :"20000"
}
#修改现有所有的索引,但新增的索引,还是默认的10000。
PUT /_all/_settings
{ 
  "index.max_result_window" :"20000"
}

#查看所有索引中的index.max_result_window值
GET /_all/_settings/index.max_result_window

注意

        参数index.max_result_window主要用来限制单次查询满足查询条件的结果窗口的大小,窗口大小由from + size共同决定。不能简单理解成查询返回给调用方的数据量。这样做主要是为了限制内存的消耗。

        比如:from为1000000,size为10,逻辑意义是从满足条件的数据中取1000000到(1000000 + 10)的记录。这时ES一定要先将(1000000 + 10)的记录(即result_window)加载到内存中,再进行分页取值的操作。尽管最后我们只取了10条数据返回给客户端,但ES进程执行查询操作的过程中确需要将(1000000 + 10)的记录都加载到内存中,可想而知对内存的消耗有多大。这也是ES中不推荐采用(from + size)方式进行深度分页的原因。

        同理,from为0,size为1000000时,ES进程执行查询操作的过程中确需要将1000000 条记录都加载到内存中再返回给调用方,也会对ES内存造成很大压力。

3.1.2 分页查询form

        from 关键字,用来指定起始返回位置,和size关键字连用可实现分页效果。

ET /es_db/_search
{
  "query": {
    "match_all": {}
  },
  "size": 5,
  "from": 0
}

3.1.3 指定字段排序sort

        注意:会让得分失效

GET /es_db/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": "desc"
    }
  ]
}

#排序,分页
GET /es_db/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": "desc"
    }
  ],
  "from": 10,
  "size": 5
}

3.1.4 返回指定字段_source

        _source 关键字,是一个数组,在这个数组中指定展示哪些字段。

GET /es_db/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["name","address"]
}

3.2 全文检索

        全文检索查询(Full Text Queries)和术语级别查询(Term-Level Queries)是 Elasticsearch 中搜索和检索数据的两种不同方法。全文检索查询指在基于相关性搜索和匹配文本数据。这些查询会对输入的文本进行分析,将其拆分为词项(单个单词),并执行诸如分词、词干处理和标准化等操作。Elasticsearch 中的一些全文检索查询示例包括 match、match_phrase 和 multi_match 查询。全文检索的关键特点:

  • 对输入的文本进行分析,并根据分析后的词项进行搜索和匹配。全文检索查询会对输入的文本进行分析,将其拆分为词项,并基于这些词项进行搜索和匹配操作。
  • 以相关性为基础进行搜索和匹配。全文检索查询使用相关性算法来确定文档与查询的匹配程度,并按照相关性进行排序。相关性可以基于词项的频率、权重和其他因素来计算。
  • 全文检索查询适用于包含自由文本数据的字段,例如文档的内容、文章的正文或产品描述等。

3.2.1 match query

        match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找,match支持以下参数:

  • query : 指定匹配的值
    • operator : 匹配条件类型
      • and:条件分词后都要匹配
      • or:条件分词后有一个匹配即可(默认)
  • minmum_should_match:最低匹配度,即条件在倒排索引中最低的匹配度。
# 模糊匹配 分词后"or"的效果
GET /es_db/_search
{
  "query": {
    "match": {
      "address": "广州白云山公园"
    }
  }
}

# 分词后"and"的效果
GET /es_db/_search
{
  "query": {
    "match": {
      "address":{
        "query": "广州白云山公园",
        "operator": "and"
      }
    }
  }
}

        在match中的应用: 当operator参数设置为or时,minnum_should_match参数用来控制匹配的分词的最少数量。

# 最少匹配广州,公园两个词
GET /es_db/_search
{
  "query": {
    "match": {
      "address": {
        "query": "广州公园",
        "minimum_should_match": 2
      }
    }
  }
}

3.2.2 match_phrase

        短语查询,match_phrase查询分析文本并根据分析的文本创建一个短语查询。match_phrase会将检索关键词分词。match_phrase的分词结果必须在被检索字段的分词中都包含,而且顺序必须相同,而且默认必须都是连续的。

GET /es_db/_search
{
  "query": {
    "match_phrase": {
      "address": "广州白云山"
    }
  }
}
GET /es_db/_search
{
  "query": {
    "match_phrase": {
      "address": "广州白云"
    }
  }
}
  • 思考:为什么查询广州白云山有数据,广州白云没有数据?

  • 分析原因:

        先查看广州白云山公园分词结果,可以知道广州和白云不是相邻的词条,中间会隔一个白云山,而match_phrase匹配的是相邻的词条,所以查询广州白云山有结果,但查询广州白云没有结果。

POST _analyze
{
    "analyzer":"ik_max_word",
    "text":"广州白云山"
}
#结果
{
  "tokens" : [
    {
      "token" : "广州",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "白云山",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "白云",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "云山",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 3
    }
  ]
}

        如何解决词条间隔的问题?可以借助slop参数,slop参数告诉match_phrase查询词条能够相隔多远时仍然将文档视为匹配。

#广州云山分词后相隔为2,可以匹配到结果
GET /es_db/_search
{
  "query": {
    "match_phrase": {
      "address": {
        "query": "广州云山",
        "slop": 2
      } 
    }
  }
}

3.2.3 multi_match

        多字段查询,可以根据字段类型,决定是否使用分词查询,得分最高的在前面。

GET /es_db/_search
{
  "query": {
    "multi_match": { #地址和名称中如果匹配到都能查询到
      "query": "长沙张龙",
      "fields": [
        "address",
        "name"
      ]
    }
  }
}

        注意:字段类型分词,将查询条件分词之后进行查询,如果该字段不分词就会将查询条件作为整体进行查询。

3.2.4 query_string

        允许我们在单个查询字符串中指定AND | OR | NOT条件,同时也和 multi_match query 一样,支持多字段搜索。和match类似,但是match需要指定字段名,query_string是在所有字段中搜索,范围更广泛。

        注意:查询字段分词就将查询条件分词查询,查询字段不分词将查询条件不分词查询。

3.2.4.1 未指定字段查询
GET /es_db/_search
{
  "query": {
    "query_string": {
      "query": "张三 OR 橘子洲"
    }
  }
}
3.2.4.2 指定单个字段查询
#Query String
GET /es_db/_search
{
  "query": {
    "query_string": {
      "default_field": "address",
      "query": "白云山 OR 橘子洲"
    }
  }
}
3.2.4.3 指定多个字段查询
GET /es_db/_search
{
  "query": {
    "query_string": {
      "fields": ["name","address"],
      "query": "张三 OR (广州 AND 王五)"
    }
  }
}

3.2.5 simple_query_string

        类似query_string,但是会忽略错误的语法,同时只支持部分查询语法,不支持AND OR NOT,会当作字符串处理。支持部分逻辑:

  • + 替代AND
  • | 替代OR
  • - 替代NOT
#simple_query_string 默认的operator是OR
GET /es_db/_search
{
  "query": {
    "simple_query_string": {
      "fields": ["name","address"],
      "query": "广州公园",
      "default_operator": "AND"
    }
  }
}

GET /es_db/_search
{
  "query": {
    "simple_query_string": {
      "fields": ["name","address"],
      "query": "广州 + 公园"
    }
  }
}

3.3 术语级别查询

        术语级别查询(Term-Level Queries)指的是搜索内容不经过文本分析直接用于文本匹配,这个过程类似于数据库的SQL查询,搜索的对象大多是索引的非text类型字段。Elasticsearch 中的一些术语级别查询示例包括 term、terms 和 range 查询。

3.3.1 关键词查询Term

        Term用来使用关键词查询(精确匹配),还可以用来查询没有被进行分词的数据类型。Term是表达语意的最小单位,搜索和利用统计语言模型进行自然语言处理都需要处理Term。match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找,而term会直接对关键词进行查找。一般模糊查找的时候,多用match,而精确查找时可以使用term。

  • ES中默认使用分词器为标准分词器(StandardAnalyzer),标准分词器对于英文单词分词,对于中文单字分词。
  • 在ES的Mapping Type 中 keyword、date、integer、long、double、boolean这些类型不分词,只有text类型分词。

        注意:最好不要在term查询的字段中使用text字段,因为text字段会被分词,这样做既没有意义,还很有可能什么也查不到。

# 对bool,日期,数字,结构化的文本可以利用term做精确匹配
# term 精确匹配
GET /es_db/_search
{
  "query": {
    "term": {
      "age": {
        "value": 28
      }
    }
  }
}

# 思考: 查询广州白云是否有数据,为什么?
GET /es_db/_search
{
  "query":{
    "term": {
      "address": {
        "value": "广州白云"
      }
    }
  }
}

# 采用term精确查询, 查询字段映射类型为keyword
GET /es_db/_search
{
  "query":{
    "term": {
      "address.keyword": {
        "value": "广州白云山公园"
      }
    }
  }
}

        在ES中,Term查询时对输入不做分词。会将输入作为一个整体,在倒排索引中查找准确的词项,并且使用相关度算分公式为每个包含该词项的文档进行相关度算分。

PUT /product/_bulk
{"index":{"_id":1}}
{"productId":"xxx123","productName":"iPhone"}
{"index":{"_id":2}}
{"productId":"xxx111","productName":"iPad"}

# 思考: 查询iPhone可以查到数据吗? 答:查不到,因为"iPhone"会被小写保存。
GET /product/_search
{
  "query":{
    "term": {
      "productName": {
        "value": "iPhone"
      }
    }
  }
}

GET /product/_analyze
{
  "analyzer":"standard",
  "text":"iPhone"
}

# 对于英文,可以考虑建立索引时忽略大小写
PUT /product
{
  "settings": {
    "analysis": {
      "normalizer": {
        "es_normalizer": {
          "filter": [
            "lowercase",
            "asciifolding"
          ],
          "type": "custom"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "productId": {
        "type": "text"
      },
      "productName": {
        "type": "keyword",
        "normalizer": "es_normalizer",
        "index": "true"
      }
    }
  }
}

        使用Term查询时,可以通过Constant Score将查询转换成一个Filtering,避免算分,并利用缓存,提高性能。

  • 将Query转成Filter,忽略TF-IDF计算,避免相关性算分的开销;
  • Filter可以有效利用缓存
GET /es_db/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "address.keyword": "广州白云山公园"
        }
      }
    }
  }
}

        term处理多值字段时,term查询是包含,不是等于。

POST /employee/_bulk
{"index":{"_id":1}}
{"name":"小明","interest":["跑步","篮球"]}
{"index":{"_id":2}}
{"name":"小红","interest":["跳舞","画画"]}
{"index":{"_id":3}}
{"name":"小丽","interest":["跳舞","唱歌","跑步"]}

POST /employee/_search
{
  "query": {
    "term": {
      "interest.keyword": {
        "value": "跑步"
      }
    }
  }
}

3.3.2 多术语查询Terms

        Terms用于在指定字段上匹配多个词项(terms)。它会精确匹配指定字段中包含的任何一个词项。

POST /es_db/_search
{
  "query": {
    "terms": {
      "remark.keyword": ["java assistant", "java architect"]
    }
  }
}

3.3.3 exists query

        在Elasticsearch中可以使用exists进行查询,以判断文档中是否存在对应的字段。

#查询索引库中存在remarks字段的文档数据
GET /es_db/_search
{
  "query": {
    "exists": 
    {
      "field": "remark"
    }
  }
}

3.3.4 多id查询ids

        ids 关键字 : 值为数组类型,用来根据一组id获取多个对应的文档。

GET /es_db/_search
{
  "query": {
    "ids": {
      "values": [1,2]
    }
  }
}

3.3.5 范围查询range

  • range:范围关键字
  • gte 大于等于
  • lte  小于等于
  • gt 大于
  • lt 小于
  • now 当前时间
POST /es_db/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 25,
        "lte": 28
      }
    }
  }
}

3.3.6 日期range

DELETE /product
POST /product/_bulk
{"index":{"_id":1}}
{"price":100,"date":"2021-01-01","productId":"BJ-1293"}
{"index":{"_id":2}}
{"price":200,"date":"2022-01-01","productId":"HRB-5421"}

GET /product/_mapping

GET /product/_search
{
  "query": {
    "range": {
      "date": {
        "gte": "now-2y"
      }
    }
  }
}

3.3.7 前缀查询prefix

        它会对分词后的term进行前缀搜索。

  • 它不会分析要搜索字符串,传入的前缀就是想要查找的前缀;
  • 默认状态下,前缀查询不做相关度分数计算,它只是将所有匹配的文档返回,然后赋予所有相关分数值为1。它的行为更像是一个过滤器而不是查询。两者实际的区别就是过滤器是可以被缓存的,而前缀查询不行。

prefix的原理:需要遍历所有倒排索引,并比较每个term是否已所指定的前缀开头。

GET /es_db/_search
{
  "query": {
    "prefix": {
      "address": {
        "value": "广州"
      }
    }
  }
}

3.3.8 通配符查询wildcard

        通配符查询:工作原理和prefix相同,只不过它不是只比较开头,它能支持更为复杂的匹配模式。

GET /es_db/_search
{
  "query": {
    "wildcard": {
      "address": {
        "value": "*白*"
      }
    }
  }
}

3.3.9 模糊查询fuzzy

        在实际的搜索中,我们有时候会打错字,从而导致搜索不到。在Elasticsearch中,我们可以使用fuzziness属性来进行模糊查询,从而达到搜索有错别字的情形。fuzzy查询会用到两个很重要的参数:fuzziness、prefix_length

  • fuzziness:表示输入的关键字通过几次操作可以转变成为ES库里面的对应field的字段
    • 操作是指:新增一个字符,删除一个字符,修改一个字符,每次操作可以记做编辑距离为1;
    • 如中文集团到中威集团编辑距离就是1,只需要修改一个字符;
    • 该参数默认值为0,即不开启模糊查询;
    • 如果fuzziness值在这里设置成2,会把编辑距离为2的东东集团也查出来。
  • prefix_length:表示限制输入关键字和ES对应查询field的内容开头的第n个字符必须完全匹配,不允许错别字匹配;
    • 如这里等于1,则表示开头的字必须匹配,不匹配则不返回
    • 默认值也是0
    • 加大prefix_length的值可以提高效率和准确率。
GET /es_db/_search
{
  "query": {
    "fuzzy": {
      "address": {
        "value": "白运山",
        "fuzziness": 1    
      }
    }
  }
}

GET /es_db/_search
{
  "query": {
    "match": {
      "address": {
        "query": "广洲",
        "fuzziness": 1
      }
    }
  }
}

注意:fuzzy模糊查询最大模糊错误必须在0-2之间

  • 搜索关键词长度为 2,不允许存在模糊;
  • 搜索关键词长度为3-5,允许1次模糊;
  • 搜索关键词长度大于5,允许最大2次模糊。

3.4 高亮highlight

        highlight 关键字,可以让符合条件的文档中的关键词高亮。highlight相关属性:

  • pre_tags 前缀标签
  • post_tags 后缀标签
  • tags_schema 设置为styled可以使用内置高亮样式
  • require_field_match 多字段高亮需要设置为false

3.4.1 自定义高亮html标签

        可以在highlight中使用pre_tags和post_tags

GET /products/_search
{
  "query": {
    "term": {
      "name": {
        "value": "牛仔"
      }
    }
  },
  "highlight": {
    "post_tags": ["</span>"], 
    "pre_tags": ["<span style='color:red'>"],
    "fields": {
      "*":{}
    }
  }
}

3.4.2 多字段高亮

GET /products/_search
{
  "query": {
    "term": {
      "name": {
        "value": "牛仔"
      }
    }
  },
  "highlight": {
    "pre_tags": ["<font color='red'>"],
    "post_tags": ["<font/>"],
    "require_field_match": "false",
    "fields": {
      "name": {},
      "desc": {}
    }
  }
}

3.5 bool query布尔查询

        布尔查询可以按照布尔逻辑条件组织多条查询语句,只有符合整个布尔条件的文档才会被搜索出来。在布尔条件中,可以包含两种不同的上下文。

  • 搜索上下文(query context):使用搜索上下文时,Elasticsearch需要计算每个文档与搜索条件的相关度得分,这个得分的计算需使用一套复杂的计算公式,有一定的性能开销,带文本分析的全文检索的查询语句很适合放在搜索上下文中。
  • 过滤上下文(filter context):使用过滤上下文时,Elasticsearch只需要判断搜索条件跟文档数据是否匹配,例如使用Term query判断一个值是否跟搜索内容一致,使用Range query判断某数据是否位于某个区间等。过滤上下文的查询不需要进行相关度得分计算,还可以使用缓存加快响应速度,很多术语级查询语句都适合放在过滤上下文中。

布尔查询一共支持4种组合类型:

类型

说明

must

可包含多个查询条件,每个条件均满足的文档才能被搜索到,每次查询需要计算相关度得分,属于搜索上下文。

should

可包含多个查询条件,不存在must和fiter条件时,至少要满足多个查询条件中的一个,文档才能被搜索到,否则需满足的条件数量不受限制,匹配到的查询越多相关度越高,也属于搜索上下文。

filter

可包含多个过滤条件,每个条件均满足的文档才能被搜索到,每个过滤条件不计算相关度得分,结果在一定条件下会被缓存, 属于过滤上下文。

must_not

可包含多个过滤条件,每个条件均不满足的文档才能被搜索到,每个过滤条件不计算相关度得分,结果在一定条件下会被缓存, 属于过滤上下文。

  • 示例
PUT /books
{
  "settings": {
    "number_of_replicas": 1,
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      },
      "title": {
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "language": {
        "type": "keyword"
      },
      "author": {
        "type": "keyword"
      },
      "price": {
        "type": "double"
      },
      "publish_time": {
        "type": "date",
        "format": "yyy-MM-dd"
      },
      "description": {
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

POST /_bulk
{"index":{"_index":"books","_id":"1"}}
{"id":"1", "title":"Java编程思想", "language":"java", "author":"Bruce Eckel", "price":70.20, "publish_time":"2007-10-01", "description":"Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉。"}
{"index":{"_index":"books","_id":"2"}}
{"id":"2","title":"Java程序性能优化","language":"java","author":"葛一鸣","price":46.5,"publish_time":"2012-08-01","description":"让你的Java程序更快、更稳定。深入剖析软件设计层面、代码层面、JVM虚拟机层面的优化方法"}
{"index":{"_index":"books","_id":"3"}}
{"id":"3","title":"Python科学计算","language":"python","author":"张若愚","price":81.4,"publish_time":"2016-05-01","description":"零基础学python,光盘中作者独家整合开发winPython运行环境,涵盖了Python各个扩展库"}
{"index":{"_index":"books","_id":"4"}}
{"id":"4", "title":"Python基础教程", "language":"python", "author":"Helant", "price":54.50, "publish_time":"2014-03-01", "description":"经典的Python入门教程,层次鲜明,结构严谨,内容翔实"}
{"index":{"_index":"books","_id":"5"}}
{"id":"5","title":"JavaScript高级程序设计","language":"javascript","author":"Nicholas C. Zakas","price":66.4,"publish_time":"2012-10-01","description":"JavaScript技术经典名著"}


GET /books/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "java编程"
          }
        },{
          "match": {
            "description": "性能优化"
          }
        }
      ]
    }
  }
}

GET /books/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "java编程"
          }
        },{
          "match": {
            "description": "性能优化"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}
GET /books/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "language": "java"
          }
        },
        {
          "range": {
            "publish_time": {
              "gte": "2010-08-01"
            }
          }
        }
      ]
    }
  }
}

  • 38
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值