Elasticsearch实战——搜索详解
Elasticsearch实战——搜索详解
文章目录
Elasticesearch的核心功能是搜索,现在介绍ES的搜索API及其用法。
为了有助于讲解,这里准备一些测试数据,把数据保存到文件website.json
中:
{"index":{"_index":"website","_id":"1"}}
{"address":"北京市昌平区南丰路","province":"北京市","city":"北京市","district":"昌平区","location":"40.150775,116.2841456","title":"凤凰自行车","category":["购物","自行车专卖"]}
{"index":{"_index":"website","_id":"2"}}
{"address":"北京市昌平区 ","province":"北京市","city":"北京市","district":"昌平区","location":"40.109854,116.274349","title":"速派奇","category":["购物","其它购物"]}
{"index":{"_index":"website","_id":"3"}}
{"address":"北京市昌平区 ","province":"北京市","city":"北京市","district":"昌平区","location":"40.165716,116.270237","title":"共享单车停放处","category":["生活服务","其它生活服务"]}
{"index":{"_index":"website","_id":"4"}}
{"address":"北京市昌平区X030","province":"北京市","city":"北京市","district":"昌平区","location":"40.16806,116.32344","title":"昌平公共自行车存车处","category":["生活服务","其它生活服务"]}
{"index":{"_index":"website","_id":"5"}}
{"address":"北京市昌平区百沙路","province":"北京市","city":"北京市","district":"昌平区","location":"40.149193,116.28929","title":"共享单车停放处","category":["生活服务","其它生活服务"]}
{"index":{"_index":"website","_id":"6"}}
{"address":"北京市昌平区北七家镇白庙村200号","province":"北京市","city":"北京市","district":"昌平区","location":"40.10261,116.38784","title":"绿源奥顺通物流销售门店","category":["购物","自行车专卖"]}
{"index":{"_index":"website","_id":"7"}}
{"address":"北京市昌平区北七家镇白庙中街200号","province":"北京市","city":"北京市","district":"昌平区","location":"40.1026,116.38751","title":"新日电动车(北清路店)","category":["购物","自行车专卖"]}
{"index":{"_index":"website","_id":"8"}}
{"address":"北京市昌平区北七家镇白庙村中街往西50米202号","province":"北京市","city":"北京市","district":"昌平区","location":"40.102558,116.387483","title":"都市风电动车(北清路店)","category":["购物","自行车专卖"]}
{"index":{"_index":"website","_id":"9"}}
{"address":"北京市昌平区小汤山镇小汤山马坊村小汤山中心马坊小学附近","province":"北京市","city":"北京市","district":"昌平区","location":"40.1547354,116.40153086","title":"雅迪电动车(马坊店)","category":["购物","自行车专卖"]}
{"index":{"_index":"website","_id":"10"}}
{"address":"北京市昌平区东北路附近","province":"北京市","city":"北京市","district":"昌平区","location":"40.134651,116.433778","title":"雅迪电动车","category":["购物","自行车专卖"]}
创建索引并设置settings
和mapping
,指定索引名为website
,副本数为1
,分片数为3
,命令如下:
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"address": {
"type": "text"
},
"category": {
"type": "keyword"
},
"city": {
"type": "keyword"
},
"district": {
"type": "keyword"
},
"location": {
"type": "geo_point"
},
"province": {
"type": "keyword"
},
"title": {
"type": "text"
}
}
}
}
最后执行bulk批量导入命令把文档导入ES
:
$curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' --data-binary @website.json
1. 搜索机制
1.1 查询所有数据
GET website/_search
{
"query":{
"match_all":{}
}
}
也可以写成:
GET website/_search
1.2 指定返回字段
默认情况下返回结果包含文档的所有字段信息。也可以指定返回某些字段。
GET website/_search
{
"_source":["title","city"],
"query":{
"term":{
"city":"北京市"
}
}
}
java示例如下:
SearchSourceBuilder builder = new SearchSourceBuilder();
//指定返回的字段
String[] includes = {"title","city"};
//排除返回的字段
String[] excludes = {};
builder.fetchSource(includes,excludes);
1.3 指定返回版本号
GET website/_search
{
"version":true,
"query":{
"term":{
"city":"北京市"
}
}
}
java示例如下:
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.version(true);
1.4 过滤低频分数据
ES提供了最小评分的过滤机制,可以使用这个过滤掉评分比较低的数据。
GET website/_search
{
"min_score":2,
"query":{
"match":{
"title":"单车"
}
}
}
java示例如下:
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.minScore(2f);
1.5 高亮查询
{
"query":{
"match":{
"title":"单车"
}
},
"highlight":{
"fields":{
"title":{}
}
}
}
java示例如下:
SearchSourceBuilder builder = new SearchSourceBuilder();
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "单车");
HighlightBuilder highlight = new HighlightBuilder();
highlight.field("title");
builder.query(query);
builder.highlighter(highlight);
2. 全文查询
高级别的全文搜索通常用于全文字段上进行全文搜索,通过全文查询理解被查询字段是如何索引和分析的,在执行之前将每个字段的分词器(或搜索分词器)应用于查询字符串。
2.1 match query
match
查询会解析查询语句。会把查询字符串经过分词器分词后去查询。查询匹配到的文档。
{
"query":{
"match":{
"title":"单车"
}
}
}
java示例如下:
MatchQueryBuilder query = QueryBuilders.matchQuery("title", "单车");
2.2 match_phrase query
match_phrase
query首先会把query内容分词,同时文档还要满足下面两个条件才能被都是到:
- 分此后所有词项都要出现在该字符串中。
- 字段中的词项顺序要一致。
{
"query":{
"match_phrase":{
"title":"共享单车"
}
}
}
java示例如下:
MatchPhraseQueryBuilder query = QueryBuilders.matchPhraseQuery("title", "共享单车");
2.3 match_phrase_prefix query
match_phrase_prefix
和match_phrase
类似,只不过match_phrase_prefix
支持最后一个term
前缀匹配:
{
"query":{
"match_phrase_prefix":{
"title":"共享单"
}
}
}
java示例如下:
QueryBuilders.matchPhrasePrefixQuery("title", "共享单");
2.4 multi_match query
multi_match
是match
的升级,用于搜索多个字段。
{
"query":{
"multi_match":{
"query":"北京",
"fields":["title","address"]
}
}
}
multi_match
支持对要搜索的字段的名称的通配符:示例如下:
{
"query":{
"multi_match":{
"query":"北京",
"fields":["title","*_address"]
}
}
}
也支持指数符指定搜索字段的权重。指定关键词出现在title
中的权重是出现在address
字段中的3倍
,命令如下:
{
"query":{
"multi_match":{
"query":"北京",
"fields":["title^3","address"]
}
}
}
java示例如下:
QueryBuilders.multiMatchQuery("北京","title","address");
2.5 common_terms query
common_terms
query是一种在不牺牲性能的情况下替代停用词提高搜索准确率和召回率的方案。
2.5.1 问题
查询中的每个术语都有成本。搜索"The brown fox"
需要三个术语查询,每个查询一个"the"
,"brown"
并且 "fox"
所有查询都针对索引中的所有文档执行。查询"the"可能与许多文档匹配,因此对相关性的影响比其他两个术语小得多。
以前,这个问题的解决方案是忽略高频率的术语。通过将其"the"视为停用词,我们减少了索引大小并减少了需要执行的术语查询的数量。
这种方法的问题在于,虽然停用词对相关性的影响很小,但它们仍然很重要。如果我们删除了停用词,我们就会失去精确度,我们无法区分"happy"
和"not happy"
,"The The"
或者 "To be or not to be"
就不会再索引中存在,搜索的的准确率和召回率就会降低。
2.5.2 解决方案
common_terms
query 提高了一种解决方案,它把query分词后的词项分成重要词项(低频词项)和不重要词项(高频词,也就是之前的停用词)。在搜索的时候,首先搜索和重要词项匹配的文档,这些文档是词项出现较少并且词项对其评分影响较大的文档。然后执行第二次查询,搜索对评分影响较小的高频词项,但是不计算所有文档的评分,而是只计算第一次查询已经匹配到的文档得分。如果第一个查询中只包含高频词,那么会通过and连接符执行一个单独的查询,换言之,会搜索所有的词项。
词项是高频词还是低频词是通过cutoff_frequency
来设置阈值的。
也许这个查询最有趣的属性是它自动适应域特定的停用词。例如,在视频托管网站上,常见的术语如clip
或video
将自动表现为停用词而无需维护手动列表。
2.5.3 示例
例如,文档频率高于0.1%的词项会被当做高频词,词频直接可以用low_freq_operator
、high_freq_operator
参数连接。设置低频词操作符为“and”
使所有的低频词都是必须搜索到的。
{
"query":{
"common":{
"body":{
"query":"nelly the elephant as a cartoon",
"cutoff_frequency":0.001,
"low_freq_opterator":"and"
}
}
}
}
上述操作会等价于:
{
"query":{
"bool":{
"must":[
{"term":{"body":"helly"}},
{"term":{"body":"elephant"}},
{"term":{"body":"cartoon"}}
],
"should":[
{"term":{"body":"the"}},
{"term":{"body":"as"}},
{"term":{"body":"a"}}
]
}
}
}
java示例如下:
QueryBuilders.commonTermsQuery("body","nelly the elephant as a cartoon").cutoffFrequency(0.001f).lowFreqOperator(Operator.AND);
2.6 query_string query
query_string query是与Lucene查询语句的语法结合非常紧密的一种查询,允许在一个查询语句中使用多个特殊条件查询(如:AND|OR|NOT)对多个字段进行查询,建议熟悉Lucene查询语法的用户去使用。
java示例如下:
QueryBuilders.queryStringQuery("大数据") .field("title").defaultOperator(Operator.AND);
3. 词项查询
全文搜索在执行查询之前会分析查询字符串,词项搜索时对倒排索引中存储的词项进行精确操作。词项级别的查询通常用于结构化数据,例如数字、日期和枚举类型。
3.1 term query
term
query用于精确匹配一个词。
{
"query":{
"term":{
"city":"北京市"
}
}
}
java示例如下:
QueryBuilders.termQuery("city","北京市");
3.2 terms query
terms
查询是trem
查询的升级,可以用来查询文档中包含多个词的文档。比如想查询city
字段中包含关键词“北京市”
或“天津市”
的文档
{
"query":{
"terms":{
"city":["北京市","天津市"]
}
}
}
java示例如下:
QueryBuilders.termsQuery("city","北京市","天津市");
3.3 range query
range
查询用于匹配在某一个范围内的数值型、日期型或字符串型字段的文档。使用range
查询只能查询一个字段,不能作用在多个字段上。range
查询支持的参数有以下几种:
-
gt
:大于 -
gte
:大于等于 -
lt
:小于 -
lte
:小于等于
例如查询价格 20 < price <= 80的数据:
{
"query":{
"range":{
"price":{
"gt":20,
"lte":80
}
}
}
}
查询日期在2020-01-01~2020-01-08
的数据:
{
"query":{
"range":{
"indexAt":{
"gte":"2020-01-01",
"lte":"2020-01-08",
"format":"yyyy-MM-dd"
}
}
}
}
java示例如下:
QueryBuilders.rangeQuery("price").gt(20).lte(80);
3.4 exists query
exists
查询会返回字段中至少有一个非空值的文档。
{
"query":{
"exists":{
"field":"city"
}
}
}
java示例如下:
QueryBuilders.existsQuery("city");
3.5 prefix query
prefix查询用于查询某个字段中给定前缀开始的文档。
{
"query":{
"prefix":{
"city":"北京"
}
}
}
java示例如下:
QueryBuilders.prefixQuery("city","北京");
3.6 wildcard query
wildcard query中文译为通配符查询,支持单字符通配符(?
,用来匹配任意一个字符)和多字符通配符(*
,用来匹配0个或多个字符)。
{
"query":{
"wildcard":{
"city":"北?市"
}
}
}
java示例如下:
QueryBuilders.wildcardQuery("city","北?市");
3.7 regexp query
ES也支持正则表达式查询,通过regexp query可以查询指定字段包含与指定正则表达式匹配的文档。可以代表任意字符,“a.c.e"和"ab…“都可以匹配"abcde”,a{3}b{3}、a{2,3}b{2,4}、a{2,}{2,}都可以匹配字符串"aaabbb”。
例如需要匹配以 W开头紧跟着数字的邮政编码,使用正则表达式查询构造查询语句如下:
{
"query":{
"regexp":{
"postcode":"W[0-9].+"
}
}
}
java示例如下:
QueryBuilders.regexpQuery("postcode","W[0-9].+");
"wildcard":{
"city":"北?市"
}
}
}
java示例如下:
QueryBuilders.wildcardQuery("city","北?市");
3.7 regexp query
ES也支持正则表达式查询,通过regexp query可以查询指定字段包含与指定正则表达式匹配的文档。可以代表任意字符,“a.c.e"和"ab…“都可以匹配"abcde”,a{3}b{3}、a{2,3}b{2,4}、a{2,}{2,}都可以匹配字符串"aaabbb”。
例如需要匹配以 W开头紧跟着数字的邮政编码,使用正则表达式查询构造查询语句如下:
{
"query":{
"regexp":{
"postcode":"W[0-9].+"
}
}
}
java示例如下:
QueryBuilders.regexpQuery("postcode","W[0-9].+");
上一篇: lintcode----解码方法
推荐阅读