elasticsearch你懂的,为了搜索

elasticsearch你懂的,为了搜索


参考文章
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html

elasticsearch搜索中两种操作

评分

  1. filter 过滤操作,不进行评分和相关度计算
  2. search 搜索,需要评分和相关度计算

查询

  1. 精确查询
  2. 全文搜索

精确查询

term 基于词项的查询

由于倒排索引表自身的特性,整个字段是否相等会难以计算,如果确定某个特定文档是否 只(only) 包含我们想要查找的词呢?首先我们需要在倒排索引中找到相关的记录并获取文档 ID,然后再扫描 倒排索引中的每行记录 ,查看它们是否包含其他的 terms 。

可以想象,这样不仅低效,而且代价高昂。正因如此, term 和 terms 是 必须包含(must contain) 操作,而不是 必须精确相等(must equal exactly) 。

精确相等

如果一个字段是数组的话,通过控制字段数组的长度来判断

{ "tags" : ["search"], "tag_count" : 1 }
{ "tags" : ["search", "open_source"], "tag_count" : 2 }
GET /my_index/my_type/_search
{
    "query": {
        "constant_score" : {
            "filter" : {
                 "bool" : {
                    "must" : [
                        { "term" : { "tags" : "search" } }, 
                        { "term" : { "tag_count" : 1 } } 
                    ]
                }
            }
        }
    }
}

返回查询

  • gt: > 大于(greater than)
  • lt: < 小于(less than)
  • gte: >= 大于或等于(greater than or equal to)
  • lte: <= 小于或等于(less than or equal to)
GET /my_store/products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
 
 
 gt: > 大于(greater than)
lt: < 小于(less than)
gte: >= 大于或等于(greater than or equal to)
lte: <= 小于或等于(less than or equal to)                       "lt"  : 40
                    }
                }
            }
        }
    }
}

日期范围

"range" : {
    "timestamp" : {
        "gt" : "2014-01-01 00:00:00",
        "lt" : "2014-01-07 00:00:00"
    }
}

当使用它处理日期字段时, range 查询支持对 日期计算(date math 进行操作,比方说,如果我们想查找时间戳在过去一小时内的所有文档:

"range" : {
    "timestamp" : {
        "gt" : "now-1h"
    }
}

is null and is not null

GET /my_index/posts/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "exists" : { "field" : "tags" }
            }
        }
    }
}
GET /my_index/posts/_search
{
    "query" : {
        "constant_score" : {
            "filter": {
                "missing" : { "field" : "tags" }
            }
        }
    }
}

对于对象的判断

"people":{
    "name":"z",
    "age":20
}

实际存储的是:

{
    "people.nmae":"z",
    "people.age":20
}

所以判断people是否存在

{
    "exists" : { "field" : "people" }
}

===>转化为

{
    "bool": {
        "should": [
            { "exists": { "field": "people.name" }},
            { "exists": { "field": "people.age" }}
        ]
    }
}

缓存

自动缓存行为编辑
在 Elasticsearch 的较早版本中,默认的行为是缓存一切可以缓存的对象。这也通常意味着系统缓存 bitsets 太富侵略性,从而因为清理缓存带来性能压力。不仅如此,尽管很多过滤器都很容易被评价,但本质上是慢于缓存的(以及从缓存中复用)。缓存这些过滤器的意义不大,因为可以简单地再次执行过滤器。

检查一个倒排是非常快的,然后绝大多数查询组件却很少使用它。例如 term 过滤字段 “user_id” :如果有上百万的用户,每个具体的用户 ID 出现的概率都很小。那么为这个过滤器缓存 bitsets 就不是很合算,因为缓存的结果很可能在重用之前就被剔除了。

这种缓存的扰动对性能有着严重的影响。更严重的是,它让开发者难以区分有良好表现的缓存以及无用缓存。

为了解决问题,Elasticsearch 会基于使用频次自动缓存查询。如果一个非评分查询在最近的 256 次查询中被使用过(次数取决于查询类型),那么这个查询就会作为缓存的候选。但是,并不是所有的片段都能保证缓存 bitset 。只有那些文档数量超过 10,000 (或超过总文档数量的 3% )才会缓存 bitset 。因为小的片段可以很快的进行搜索和合并,这里缓存的意义不大。

一旦缓存了,非评分计算的 bitset 会一直驻留在缓存中直到它被剔除。剔除规则是基于 LRU 的:一旦缓存满了,最近最少使用的过滤器会被剔除。

全文搜索

  • 如果查询 日期(date) 或 整数(integer) 字段,它们会将查询字符串分别作为日期或整数对待。
  • 如果查询一个( not_analyzed )未分析的精确值字符串字段, 它们会将整个查询字符串作为单个词项对待。
  • 但如果要查询一个( analyzed )已分析的全文字段, 它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表。

对于keyword的字段,使用非评分filter更好。

GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": "QUICK!"
        }
    }
}
            --
            ||
            ||
            \/
    
GET /my_index/my_type/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {
          "title": "quick"
        }}
      ]
    }
  }
}

提高精度

  1. 多词匹配的时候,查询字符串通过分析器,得到多个token,再转化成底层term查询,这多个term匹配是or关系,通过operator来确定是or或and
GET /my_index/my_type/_search
{
    "query": {
        "match": {
            "title": {      
                "query":    "BROWN DOG!",
                "operator": "and"
            }
        }
    }
}
  1. 通过minimum_should_match来控制term的最小匹配率
GET /my_index/my_type/_search
{
  "query": {
    "match": {
      "title": {
        "query":                "quick brown dog",
        "minimum_should_match": "75%"
      }
    }
  }
}
参数示例解释
整数3无论可选子句的数量如何,都表示固定值。
负整数-2表示可选子句的总数减去此数字应该是必需的。
百分比75%表示可选子句总数的百分比是必需的。从百分比计算的数字向下舍入并用作最小值。
负百分比-25%表示可以丢失可选子句总数的百分比。从百分比计算的数字向下舍入,然后从总数中减去以确定最小值。
组合3<90%正整数,后跟小于号,后跟任何前面提到的说明符是条件规范。它表示如果可选子句的数量等于(或小于)整数,则它们都是必需的,但如果它大于整数,则适用规范。在这个例子中:如果有1到3个条款,则它们都是必需的,但是对于4个或更多条款,只需要90%。
多种组合2<-25% 9<-3多个条件规范可以用空格分隔,每个空格仅对大于之前的数字有效。在这个例子中:如果需要1或2个子句,如果有3-9个子句,则除了25%之外都需要,如果有9个以上的子句,则除了3个子句外都需要。

注意:

处理百分比时,负值可用于在边缘情况下获得不同的行为。在处理4个条款时,75%和-25%的含义相同,但在处理5个条款时,75%表示需要3个,但-25%表示需要4个。

如果基于规范的计算确定不需要可选子句,则关于BooleanQueries的常规规则仍然适用于搜索时间(包含没有必需子句的BooleanQuery必须仍然匹配至少一个可选子句)

无论计算到达的是什么数字,都将永远不会使用大于可选子句数的值或小于1的值。(即:无论计算结果的结果有多低或多高,所需匹配的最小数量永远不会低于1或大于子句数。

查询组合

表示查询该类型下的所有title含有quick不含有lazy的文档,should只是增加了相关性,should不会影响文档的匹配

GET /my_index/my_type/_search
{
  "query": {
    "bool": {
      "must":     { "match": { "title": "quick" }},
      "must_not": { "match": { "title": "lazy"  }},
      "should": [
                  { "match": { "title": "brown" }},
                  { "match": { "title": "dog"   }}
      ]
    }
  }
}

控制精度

GET /my_index/my_type/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title": "brown" }},
        { "match": { "title": "fox"   }},
        { "match": { "title": "dog"   }}
      ],
      //表示最少要匹配两条match
      "minimum_should_match": 2 
    }
  }
}

bool和match

{
    "match": {
        "title": {
            "query":    "brown fox",
            "operator": "and"
        }
    }
}
            --
            ||
            ||
            \/
{
  "bool": {
    "must": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }}
    ]
  }
}
{
    "match": {
        "title": {
            "query":                "quick brown fox",
            "minimum_should_match": "75%"
        }
    }
}
            --
            ||
            ||
            \/
{
  "bool": {
    "should": [
      { "term": { "title": "brown" }},
      { "term": { "title": "fox"   }},
      { "term": { "title": "quick" }}
    ],
    "minimum_should_match": 2 
  }
}

提高权重

GET /_search
{
    "query": {
        "bool": {
            "must": {
                "match": {  
                    "content": {
                        "query":    "full text search",
                        "operator": "and"
                    }
                }
            },
            "should": [
                { "match": {
                    "content": {
                        "query": "Elasticsearch",
                        "boost": 3 
                    }
                }},
                { "match": {
                    "content": {
                        "query": "Lucene",
                        "boost": 2 
                    }
                }}
            ]
        }
    }
}

boost是相关性权重,这种提升和降低并不是线性的。

搜索文本的分析器

  • 查询自己定义的 analyzer ,否则
  • 字段映射里定义的 search_analyzer ,否则
  • 字段映射里定义的 analyzer ,否则
  • 索引设置中名为 default_search 的分析器,默认为
  • 索引设置中名为 default 的分析器,默认为

standard 标准分析器

索引模板

PUT _template/template_1
{
//index的名称以te开头将使用此模板
  "template": "te*",
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "type1": {
      "_source": {
        "enabled": false
      },
      "properties": {
        "host_name": {
          "type": "keyword"
        },
        "created_at": {
          "type": "date",
          "format": "EEE MMM dd HH:mm:ss Z YYYY"
        }
      }
    }
  }
}

删除模板

DELETE /_template/template_1

查询模板

GET /_template/template_1
GET /_template/temp*
GET /_template/template_1,template_2

相关度

相关度是 词频/逆向文档频率(TF/IDF)/字段长度

  • 词频:是计算某个词在当前被查询文档里某个字段中出现的频率
  • 逆向文档频率:将某个词在索引内所有文档出现的百分数 考虑在内,出现的频率越高,它的权重就越低。
  • 字段长度:长度越短越相关

es在处理分片的时候,并不会去收集全部分片计算IDF,而是分片内计算一个本地IDF,这就会到这同一个词在不同node的IDF不一致,可能会出现偏差。
但是,由于分片的数据是均匀分布的,当数据量慢慢增长的时候,这种相关度不一致的程度就会越少。

多字段文本搜索

boost是查询权重,提高权重就要>1

GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { 
            "title":  {
              "query": "War and Peace",
              "boost": 2
        }}},
        { "match": { 
            "author":  {
              "query": "Leo Tolstoy",
              "boost": 2
        }}},
        { "bool":  { 
            "should": [
              { "match": { "translator": "Constance Garnett" }},
              { "match": { "translator": "Louise Maude"      }}
            ]
        }}
      ]
    }
  }
}

最佳字段

索引数据
PUT /my_index/my_type/1
{
    "title": "Quick brown rabbits",
    "body":  "Brown rabbits are commonly seen."
}

PUT /my_index/my_type/2
{
    "title": "Keeping pets healthy",
    "body":  "My quick brown fox eats rabbits on a regular basis."
}
  1. 它会执行 should 语句中的两个查询。
  2. 加和两个查询的评分。as s
  3. 乘以匹配语句的总数。as count
  4. 除以所有语句总数(这里为:2)。as num
    (s1 + s2)*count/num

文档 1 的两个字段都包含 brown 这个词,所以两个 match 语句都能成功匹配并且有一个评分。文档 2 的 body 字段同时包含 brown 和 fox 这两个词,但 title 字段没有包含任何词。这样, body 查询结果中的高分,加上 title 查询中的 0 分,然后乘以二分之一,就得到比文档 1 更低的整体评分。

{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

那么如何解决这个问题

不使用 bool 查询,可以使用 dis_max 即分离 最大化查询(Disjunction Max Query) 。分离(Disjunction)的意思是 或(or) ,这与可以把结合(conjunction)理解成 与(and) 相对应。分离最大化查询(Disjunction Max Query)指的是: 将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回 :

{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

dis_max调优

可以通过指定 tie_breaker 这个参数将其他匹配语句的评分也考虑其中

{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ],
            "tie_breaker": 0.3
        }
    }
}

tie_breaker
参数提供了一种 dis_max 和 bool 之间的折中选择,它的评分方式如下:

  1. 获得最佳匹配语句的评分 _score 。
  2. 将其他匹配语句的评分结果与 tie_breaker 相乘。
  3. 对以上评分求和并规范化。

tie_breaker 可以是 0 到 1 之间的浮点数,其中 0 代表使用 dis_max 最佳匹配语句的普通逻辑,** 1** 表示所有匹配语句同等重要。最佳的精确值需要根据数据与查询调试得出,但是合理值应该与零接近(处于** 0.1 - 0.4** 之间),这样就不会颠覆 dis_max 最佳匹配性质的根本。

多数字段跨字段

{
    "multi_match": {
        "query":                "Quick brown fox",
        "type":                 "best_fields", 
        "fields":               [ "title", "body" ],
        "tie_breaker":          0.3,
        "minimum_should_match": "30%" 
    }
}

模糊匹配

{
    "multi_match": {
        "query":  "Quick brown fox",
        "fields": "*_title"
    }
}

提高字段的权重

{
    "multi_match": {
        "query":  "Quick brown fox",
        "fields": [ "*_title", "chapter_title^2" ] 
    }
}

跨字段

{
  "query": {
    "bool": {
      "should": [
        { "match": { "street":    "Poland Street W1V" }},
        { "match": { "city":      "Poland Street W1V" }},
        { "match": { "country":   "Poland Street W1V" }},
        { "match": { "postcode":  "Poland Street W1V" }}
      ]
    }
  }
}
GET my_index/my_type/_search
{
  "query": {
    "multi_match": {
      "query": "brown fox",
      "type": "most_fields",
       "operator":    "or",
       "minimum_should_match":"75%",
      "fields": ["title","body"]
    }
  }
}

most_fields 方式的问题编辑

用 most_fields 这种方式搜索也存在某些问题,这些问题并不会马上显现:

  • 它是为多数字段匹配 任意 词设计的,而不是在 所有字段 中找到最匹配的。
  • 它不能使用 operator 或 minimum_should_match 参数来降低次相关结果造成的长尾效应。
  • 词频对于每个字段是不一样的,而且它们之间的相互影响会导致不好的排序结果。
解决方案编辑

存在这些问题仅仅是因为我们在处理着多个字段,如果将所有这些字段组合成单个字段,问题就会消失。可以为 person 文档添加 full_name 字段来解决这个问题:

{
    "first_name":  "Peter",
    "last_name":   "Smith",
    "full_name":   "Peter Smith"
}
自定义_all字段
PUT /my_index
{
    "mappings": {
        "person": {
            "properties": {
                "first_name": {
                    "type":     "string",
                    "copy_to":  "full_name" 
                },
                "last_name": {
                    "type":     "string",
                    "copy_to":  "full_name" 
                },
                "full_name": {
                    "type":     "string"
                }
            }
        }
    }
}

copy_to 对多字段(一个字段有不同的索引方式)无效,只能针对主字段,主字段的多字段是没有自己的数据源的,依存于主字段

PUT /my_index
{
    "mappings": {
        "person": {
            "properties": {
                "first_name": {
                    "type":     "string",
                    "copy_to":  "full_name", 
                    "fields": {
                        "raw": {
                            "type": "string",
                            "index": "not_analyzed"
                        }
                    }
                },
                "full_name": {
                    "type":     "string"
                }
            }
        }
    }
}

跨字段

GET /books/_search
{
    "query": {
        "multi_match": {
            "query":       "peter smith",
            "type":        "cross_fields",
            "fields":      [ "title^2", "description" ] 
        }
    }
}
对于匹配的文档, peter 和 smith 都必须同时出现在相同字段中,要么是 first_name 字段,要么 last_name 字段:

字段中心 会导致几个字段同事匹配一个词要比几个字段匹配不同的要高
(+first_name:peter +first_name:smith)
(+last_name:peter  +last_name:smith)

词中心式 会使用以下逻辑:
+(first_name:peter last_name:peter)
+(first_name:smith last_name:smith)

换句话说,词 peter 和 smith 都必须出现,但是可以出现在任意字段中。
  • 自定义 _all 的方式是一个好的解决方案,只需在索引文档前为其设置好映射。 不过, Elasticsearch 还在搜索时提供了相应的解决方案:使用 cross_fields 类型进行 multi_match 查询。 cross_fields 使用词中心式(term-centric)的查询方式,这与 best_fields 和 most_fields 使用字段中心式(field-centric)的查询方式非常不同,它将所有字段当成一个大字段,并在 每个字段 中查找 每个词 。
  • cross_fields 使用词中心式(term-centric)的查询方式,这与 best_fields 和 most_fields 使用字段中心式(field-centric)的查询方式非常不同,它将所有字段当成一个大字段,并在 每个字段 中查找 每个词 。
为了让 cross_fields 查询以最优方式工作,所有的字段都须使用相同的分析器, 具有相同分析器的字段会被分组在一起作为混合字段使用。

如果包括了不同分析链的字段,它们会以 best_fields 的相同方式被加入到查询结果中。例如:我们将 title 字段加到之前的查询中(假设它们使用的是不同的分析器), explanation 的解释结果如下:

(+title:peter +title:smith)
(
  +blended("peter", fields: [first_name, last_name])
  +blended("smith", fields: [first_name, last_name])
)
当在使用 minimum_should_match 和 operator 参数时,这点尤为重要

权重

GET /books/_search
{
    "query": {
        "multi_match": {
            "query":       "peter smith",
            "type":        "cross_fields",
            "fields":      [ "title^2", "description" ] 
        }
    }
}   

精确值 not_analyzed 未分析字段,将 not_analyzed 字段与 multi_match 中 analyzed 字段混在一起没有多大用处。

参考文章
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在当今的软件开发领域,自动化构建与发布是提升开发效率和项目质量的关键环节。Jenkins Pipeline作为一种强大的自动化工具,能够有效助力Java项目的快速构建、测试及部署。本文将详细介绍如何利用Jenkins Pipeline实现Java项目的自动化构建与发布。 Jenkins Pipeline简介 Jenkins Pipeline是运行在Jenkins上的一套工作流框架,它将原本分散在单个或多个节点上独立运行的任务串联起来,实现复杂流程的编排与可视化。它是Jenkins 2.X的核心特性之一,推动了Jenkins从持续集成(CI)向持续交付(CD)及DevOps的转变。 创建Pipeline项目 要使用Jenkins Pipeline自动化构建发布Java项目,首先需要创建Pipeline项目。具体步骤如下: 登录Jenkins,点击“新建项”,选择“Pipeline”。 输入项目名称和描述,点击“确定”。 在Pipeline脚本中定义项目字典、发版脚本和预发布脚本。 编写Pipeline脚本 Pipeline脚本是Jenkins Pipeline的核心,用于定义自动化构建和发布的流程。以下是一个简单的Pipeline脚本示例: 在上述脚本中,定义了四个阶段:Checkout、Build、Push package和Deploy/Rollback。每个阶段都可以根据实际需求进行配置和调整。 通过Jenkins Pipeline自动化构建发布Java项目,可以显著提升开发效率和项目质量。借助Pipeline,我们能够轻松实现自动化构建、测试和部署,从而提高项目的整体质量和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值