1. 深入搜索

1.1 基于词项与全文的搜索

1.1.1 基于term的搜索

  • term的重要性:term是表达语义的最小单位
  • 特点:
  • 包括term query\range query\exists query\prefix query\wildcard query
  • term查询,对输入不做分词
  • 可以通过constant score将查询转换成一个filtering,避免算分,并利用缓存,提高性能
GET kibana_sample_data_logs/_search
{
  "explain": true, 
  "query": {
    "term": {
      "extension.keyword": {
        "value": "css"
      }
    }
  }
}
GET kibana_sample_data_logs/_search
{
  "explain": true, 
  "query": {
    "constant_score": { // 避免算分,并利用缓存
      "filter": {
        "term": {
          "extension.keyword": "css"
        }
      }
    }
  }
}

1.1.2 基于全文的搜索

  • 基于全文本的查找 match query\match phrase query\query string query
  • 特点:
  • 索引和搜索时都会进行分词,查询字符串先传递到一个合适的分词器,然后生成一个供查询的词项列表
  • 查询时候,先会对输入的查询进行分词,然后每个词项逐个进行底层的查询,最终将结果进行合并,并为每个文档生成一个算分。例如查"新 泾 三 村",会查到包含新或泾或三或村的所有结果
GET amap_poi_detail/_search
{
  "query": {
    "match": {
      "name": {
        "query": "新 泾 三 村",
        "operator": "and" // 查询这四个字均包含的文档
      }
    }
  }
}

1.2 基于结构化的搜索

  • 布尔、日期和数字这类结构化数据:有精确的格式,我们可以对这些格式进行逻辑操作,包括范围,比较大小
  • 结构化文本可以做term查询或prefix查询
GET kibana_sample_data_flights/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "AvgTicketPrice": {
            "gte": 600,
            "lte": 800
          }
        }
      }
    }
  }
GET kibana_sample_data_flights/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "timestamp": {
            "gte": "now-1y"
          }
        }
      }
    }
  }
}
# 其中 y年 M月 w周 d天 H/h小时 m分钟 s秒

1.3 基于bool的查询

  • 一个bool查询,是一个或者多个查询子句的组合
  • 总共包括4中子句。其中2种会影响评分,2种不影响评分
  • must子句,必须匹配,贡献算分
  • should选择性匹配,贡献算分
  • must_not Filter Context查询子句,必须不能匹配,不贡献算分
  • filter Filter Context必须匹配,但是不贡献算分
POST products/_search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "price" : "30" }
      },
      "filter": {
        "term" : { "avaliable" : "true" }
      },
      "must_not" : {
        "range" : {
          "price" : { "lte" : 10 }
        }
      },
      "should" : [
        { "term" : { "productID.keyword" : "JODL-X-1937-#pV7" } },
        { "term" : { "productID.keyword" : "XHDK-A-1293-#fJ3" } }
      ],
      "minimum_should_match" :1
    }
  }
}

// 同一层级下的竞争字段,具有相同的权重
// 通过嵌套bool查询,可以改变对算分的影响
POST animals/_search
{
  "query": {
    "bool": {
      "should": [
        { "term": { "text": "quick" }}, // A
        { "term": { "text": "dog"   }}, // B 与A具有相同的权重
        {
          "bool":{
            "should":[
               { "term": { "text": "brown" }}, // C
               { "term": { "text": "brown" }} // D C+D权重=A权重=B权重
            ]
          }
        }
      ]
    }
  }
}

1.4 单字符串多字段查询

Disjunction Max Query: 将任何与任一查询匹配的文档做为返回结果。采用字段上最匹配的评分作为最终评分返回

PUT /blogs/_doc/1
{
    "title": "Quick brown rabbits",
    "body":  "Brown rabbits are commonly seen."
}
PUT /blogs/_doc/2
{
    "title": "Keeping pets healthy",
    "body":  "My quick brown fox eats rabbits on a regular basis."
}
POST /blogs/_search
{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}
// 可以发现should match出来的排序结果并不是想要的,因此需要Disjunction Max Query
POST blogs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}
POST blogs/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ],
	    // 可以尝试去掉该行,会发现评分会一致
            // 0<=tie_breaker<=1: 0最佳评分 1所有语句同样重要
            // 工作原理1.获得最佳匹配语句的评分_score 2.将其他匹配语句的评分与tie_breaker相乘
            // 3. 对以上评分求和并规范化
            "tie_breaker": 0.2
        }
    }
}

1.5 索引新增别名查询

POST blog-2021/_doc
{
  "name":"domi",
  "rating":5
}

POST blog-2022/_doc
{
  "name":"shenjian",
  "rating":3
}

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "blog-2021",
        "alias": "blog-latest"
      }
    },
    {
      "add": {
        "index": "blog-2022",
        "alias": "blog-latest"
      }
    }
  ]
}

GET blog-latest/_search
{
  "query": {
    "match_all": {}
  }
}

2. 搜索相关性算分机制

2.1 相关性和相关性算分

2.1.1 相关性

  • 搜索的相关性算分,描述了一个文档和查询语句匹配的程度。ES会对每个匹配查询条件的结果进行算分_score
  • 打分的本质是排序,需要把最符合用户需求的文档排在最前面。ES5之前,默认的相关性算分是TF-IDF,现在采用BM 25

2.2 词频TF

  • Term Frequency: 检索词在一篇文档中出现的频率
  • 检索词出现的次数除以文档的总字数
  • 度量一条查询和结果文档相关性的简单方法:将搜索中每个词的TF进行相加
  • TF(区块链) + TF(的) + TF(应用)
  • Stop Word
  • ‘的’在文档中出现了很多次,但对贡献相关度几乎没有作用,不应考虑它们的TF

2.3 逆文档频率

  • DF: 检索词在所有文档中出现的频率
  • “区块链”在相对比较少的文档中出现
  • “应用”在相对较多的文档中出现
  • “的”在大量的文档中出现
  • Inverse Document Frequency: 简单说=log(全部文档数/检索词出现过的文档总数)
  • TF-IDF的本质就是将TF求和变成了加权求和

TF(区块链)*IDF(区块链) + TF(的)*IDF(的) + TF(应用)*IDF(应用)

2.4 Lucene中的TF-IDF评分公式

es 怎么做热搜词统计_es 怎么做热搜词统计

2.5 BM 25

  • 从ES5开始,默认算法改为BM 25
  • 和TF-IDF相比,当TF无限增加时,BM 25算分会趋于一个数值

2.6 查询权限提升与降低

// boost > 1 打分提升 0 < boost < 1 打分降低  boost < 0 或 negative_boost 贡献负分
GET kibana_sample_data_flights/_search
{
  "explain": true, 
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "DestCountry": {
            "value": "IT",
            "boost": 2
          }
        }
      }, 
      "negative": {
        "range": {
          "AvgTicketPrice": {
            "gte": 800
          }
        }
      }, 
      "negative_boost": 0.2
    }
  }
}