搜索机制
搜索的流程图如下:
1,文档写入ES的时候,ES中会存储两份数据。一份是文档的原始数据,即_source中存储的信息。另一份是通过分词、过滤等一系列动作之后生成的倒排索引文件,该文件记录了词项和文档的对应关系。
2,用户对文档进行搜索,ES接受到用户的关键词之后到倒排索引中取查找,通过倒排记录表查找到关键词对应的文档,然后在通过评分、高亮、排序等处理,返回给用户。
3,搜索机制解决的是相关度的问题。用户输入一个查询,ES通过计算关键词和文档之间的相关度,按照评分排序后返回相关度最高的文档给用户。
搜索过程
索引的创建、字段映射、文档的导入等工作参考前文,此处略过。
ES中有了数据就可以进行下一步的搜索了。
ES中的restful的查询语句要封装成json对象的形式,称为查询DSL语句。
例如查看books索引的全部数据的DSL语句如下:
get books/_search
{
"query":{
"match_all":{}
}
}
match_all query 会返回所有的文档,文档的评分都是1。可以简写为
get books/_search
下面以term query(精确匹配) 为例子进行讲解。
term 查询用来查询包含指定词项的文档。term 查询不被解析,只有查询项和文档中的词精确匹配到该文档才会被索引到。应用场景为查询需要精确匹配的需求。
例如查询title中含有“思想”的文档,DSL语句如下:
GET books/_search
{
"from":"0" //从哪条开始
"size":"100" //返回的条数”
"_source":["title","price"], //指定返回的字段,如果不指定,默认返回所有的字段
"min_score":"0.6" //最下评分过滤机制,只有评分大于0.6的文档才会返回。
"query":{
"term":{"title":"思想"}
}
//高亮查询关键字
"highlight":{
"fields":{
"title":{}
}
}
}
全文索引
match query
match query 会对查询语句进行分词,分词后查询语句中的任何一个词被匹配到,那么文档就会被索引到。如果想匹配所有关键词的文档,可以用and关键字连接。例如:
GET books/_search
{
"query":{
"match":{
"title":{
"query":"java"
"operator":"and"
}
}
}
}
match一phrase query
match-phrase query 查询首先会把查询语句分词,分词器可以自定义。同时文档需要满足一下两个条件文档才会被索引到
1,分词后的字段要全部出现在目标字段中
2,字段中的词项顺序需要一致
match_phrase_prefix query
multi一match query
multi一match query 是match的升级版,用于搜索多个字段。例如查询语句为:“Java编程”,
查询域为title和description。查询DSL语句如下:
GET books/_search
"query":{
multi-match:{
query:"Java编程"
//指定查询的字段,*_name 为通配符的形式
fields:["title","description","*_name"]
}
}
common_terms query
common terms query 是一种在不牺牲性能的情况下替代停用词提高搜索准确率和召回率
的方案。
query_string query
query string query 是与Lucene 查询语句的语法结合非常紧密的一种查询,允许在一个查
询语句中使用多个特殊条件关键字( 如:AND|OR|NOT ) 对多个字段进行查询,建议熟悉
Lucene 查询语法的用户去使用。
词项查询
term query
上文讲过了,不在赘述
terms 查询
如下,查询北京和西安的数据
{
"query": {
"terms": {
"city": [
"北京",
"西安"
]
}
}
}
range query
range 用于检索某一个范围内的文档。比如说价格50到100的书籍信息等。range有四个参数
gt:大于(>),查询范围的最小值,不包含临界值
gte: 大于等于(>=)。查询范围最小值,包含临界值。
lt: 小于(<)。查询范围的上界,不包含临界值。
lte:小于等于(<=)
查询价格50到100的书籍
GET books/_search
{
query:{
"range":{
"price":{
"gt":50,
"lte":100
}
}
}
}
exists query
返回字段中至少有一个非空值的文档。如:
{
"query": {
"exists": {
"field": "ownerName"
}
}
}
返回结果ownerName不为空的文档。
prefix query
prefix 查询用于查询某个字段中以给定前缀开始的文档,比如查询title 中含有以java 为前
缀的关键词的文档,那么含有java javascript javaee 等所有以java 开头关键词的文档都会被
匹配。如
{
"query": {
"prefix": {
"title": "Java"
}
}
}
wildcard query
通配符查询。支持单通配符和多通配符的形式。?用来一个字符,*用来匹配一个或者是多个字符。如:
{
"query": {
"wildcard": {
"title": "Java*"
}
}
}
regexp query
正则匹配。例如需要匹配以W 开头紧跟着数字的邮政编码:
{
"query": {
"regexp": {
"postcode": ""W[0-9].*"
}
}
}
fuzzy query
编辑距离又称Levenshtein 距离,是指两个字串之间,由一个转成另一个所需的最少编辑
操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
fuzzy 查询就是通过计算词项与文档的编辑距离来得到结果的,但是使用fozzy 查询需要消耗
的资源比较大,查询效率不高,适用于需要模糊查询的场景。举例如下,用户在输入查询关键
词时不小心把“javascript ” 拼成“javascritp ” ,在存在拼写错误的情况下使用模糊查询仍然可
以搜索到含有“javascript ” 的文档,查询语句如下:
{
"query": {
"fuzzy ": {
"title": "javascript "
}
}
}
type query
type query 用于查询具有指定类型的文档。例如查询Elasticsearch 中type 为IT 的文档,查
询语句如下:
{
"query": {
"type": {
"value": "IT"
}
}
}
ids query
ids query 用于查询具有指定id 的文档。。例如,查询类型为IT, id 为1 、3 、5
的文档,查询语句如下:
{
"query": {
"ids": {
"type": "IT",
"values":["1","3","5"]
}
}
}
复合查询
复合查询就是把一些简单查询组合在一起实现更复杂的查询需求,除此之外复合查询还
可以控制另外一个查询的行为。
constan_score
constant score query 可以包装一个其他类型的查询,并返回匹配过滤器中的查询条件且具
有相同评分的文档。下面的查询语句会返回title 字段中含有关键词“java ” 的文档,所有文档
的评分都是1.2:
{
"query": {
"constantscore": {
"filter":{
"term":{"title":"java"}
}
"boost":"1.2"
}
}
}
bool
bool查询可以把任意多个简单查询组合在一起。使用must、should、must_not、filter选项来表达各个简单查询之间的逻辑组合关系。每个选项都可以出现0次或者是多次。
must:文档必须匹配must选项下的查询条件。相当于逻辑运算的and
should:文档可以匹配should选项下的查询条件也可以不匹配。相当于逻辑运算的or。
must_not:于must相反。匹配到must_not查询条件的文档不会被返回。相当于逻辑运算的not
filter:功能和must类似。区别在于filter只起到过滤的作用,不对文档进行评分。
假设要查询title 中包含关键词java, 并且price 不能高于70, description 可以包含也可以
不包含虚拟机的书籍, 构造bool 查询语句如下:
GET books/ search
"query": {
"bool": {
"minimum—should一match": 1,}
"must": {
"match": { "title": "java"}}
"should": [
{"match": { "description": " 虚拟机" }}]
must_not": {
"range": {"price": {"gte": 70}}
}
}
dis_max
dis max与bool query 有一定联系也有一定区别,dis max query 支持多并发查询。
可返回与任意查询条件子句匹配的任何文档类型。与bool 查询可以将所有匹配查询的分数相
结合使的方式不同,dis 查询只使用最佳匹配查询条件的分数
fonction_score
fonction_score query 可以修改查询的文档得分,这个查询在有些情况下非常有用,比如通
过评分函数计算文档得分代价较高,可以改用过滤器加自定义评分函数的方式来取代传统的评
分方式。
使用fiinction score query , 用户需要定义一个查询和一至多个评分函数,评分函数会对查
询到的每个文档分别计算得分。
boosting
boosting 查询用于需要对两个查询的评分进行调整的场景,boosting 查询会把两个查询封
装在一起并降低其中一个查询的评分
indices
indices query 适用于需要在多个索引之间进行查询的场景,它允许指定一个索引名字列表
和内部查询。indices query 中有query 和no match query 两部分,query 中用于搜索指定索引
列表中的文档,no_match_query 中的查询条件用于搜索指定索引列表之外的文档。
嵌套查询
在Elasticsearch 这样的分布式系统中执行全SQL 风格的连接查询代价昂贵,是不可行的。
相应地,为了实现水平规模地扩展,Elasticsearch 提供了以下两种形式的join:
nested query (嵌套查询)
文档中可能包含嵌套类型的字段,这些字段用来索引一些数组对象,每个对象都可以作为
一条独立的文档被查询出来。
has child query (有子查询)和has_parent query (有父查询)
父子关系可以存在单个的索引的两个类型的文档之间。has_child 查询将返回其子文档能满
足特定查询的父文档,而has_parent 则返回其父文档能满足特定查询的子文档。
has_child
现在有两个索引:员工employee和城市(branch)。类似于数据的两张表。在ES中,员工是child type ,城市是parent type 。需要在映射中声明如下:
PUT /company
"mappings": {
"branch": {},
"employee” {"_parent": { "type": "branch"}}
}
通过子文档查询父文档需要使用has_child查询。例如,搜索1980 年以后出生的员工所在的
城市。
GET company/branch/_search
"query": {
"has_child": {
"type": "employee",
"query": {
"range": {"birthday": {"gte": "1980-01-01’ }}
has_parent
通过父文档查询子文档使用has_parent 查询。比如,搜索哪些employee 工作在UK, 查询
命令如下:
GET /company/employee/_search
"query": {
"has_parent": {
"paren_type": "branch",
"query": {"match": {"country": "UK"}}
}
位置查询
Elasticsearch 可以对地理位置点geo_point 类型和地理位置形状geo_shape 类型的数据进行
搜索。
geo_distance、geo_boundin_box、geo_polygon、geo_shape等。需要的时候可以具体的查询API。
特殊查询
more_like_this
more_like_this query 可以查询和提供文本类似的文档,通常用于近似文本的推荐等场景。
script
Elasticsearch 支持使用脚本进行查询。
搜索高亮(略)
搜索排序
默认排序
Elasticsearch 是按照查询和文档的相关度进行排序的,默认按评分降序排序。
对于match_all query 而言,由于只返回所有文档,不需要评分,文档的顺序为添加文档的
顺序。如果需要改变match_all query 的文档返回顺序,可以对_doc 进行排序。例如,返回最
后添加的那条文档, 可以对_doc 降序排序,设置返回文档条数为1 。
多字段排序
如按照价格降序排序,在根据年份升序排序。
GET books/ search
"sort": [
{"price": { "order": "desc"} },
{"year": {"order": "asc"}}]
分片影响评分
ES是在每一个分片上进行单独评分的,分片的数量会影响到评分的结果。。同时,分词器也会
影响评分,原因是使用不同的分词器会使倒排索引中的词项数发生改变,最终影响评分。
示例是一个SQL和SDL对应的关系
SELECT
COUNT(1)
FROM
table
WHERE order_type = 'GUARANTEE'
AND STATUS IN (
'FAILED',
'SUCCEEDED',
'REFUNDED',
'UNTESTED',
'UNTRANSFERED',
'TRANSFERED',
'SETTLED',
'APPLYREFUND'
)
AND id = 306472658
AND time >= "2019-01-01 00:00:00"
AND time <= "2019-03-31 23:59:59"
SDL:
{
"bool" : {
"must" : [
{
"term" : {
"id" : {
"value" : "306472658",
"boost" : 1.0
}
}
},
{
"term" : {
"order_type" : {
"value" : "GUARANTEE",
"boost" : 1.0
}
}
},
{
"range" : {
"time" : {
"from" : "20190101000000",
"to" : null,
"include_lower" : true,
"include_upper" : true,
"boost" : 1.0
}
}
},
{
"range" : {
"time" : {
"from" : null,
"to" : "20190331235959",
"include_lower" : true,
"include_upper" : true,
"boost" : 1.0
}
}
},
{
"terms" : {
"status" : [
"FAILED",
"SUCCEEDED",
"REFUNDED",
"UNTESTED",
"UNTRANSFERED",
"TRANSFERED",
"SETTLED",
"APPLYREFUND"
],
"boost" : 1.0
}
}
],
"disable_coord" : false,
"adjust_pure_negative" : true,
"boost" : 1.0
}
}