ES 深入理解搜索

1.基于Term的查询

Term是表达语义的最小单位,搜索和自然语言处理都用Term处理。

  • Term级别的查询:Term查询、范围查询、存在性查询、前缀查询、通配符查询。
  • 在ES里面对于Term查询,输入不做分词。将输入当成一个整体,在倒排索引里面查找准确的词。然后根据公式对文档进行算分。
  • 可以通过Constant Score,避免算分,并利用缓存提高性能。

案例

POST /products/_bulk
{"index":{"_id":1}}
{"product_id":"ABC-DEF","desc":"iPhone"}
{"index":{"_id":2}}
{"product_id":"ABC-bba","desc":"iPad"}

POST /products/_search
{
  "query": {
    "term": {
      "desc": {
        "value": "iPhone"//查不到,"iphone"可以,
      }
    }
  }
}
POST /products/_search
{
  "query": {
    "term": {
      "product_id.keyword": {
        "value": "ABC-bba"
      }
    }
  }
}
忽略计算评分
 {
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "product_id.keyword": "ABC-bba"
        }
      }
    }
  }
}

2.基于全文的查询

可以通过match、match phrase、query string这些类的查询实现。

特点:

  • 索引和搜索时都会进行分词,查询字符串先传递一个合适的分词器,然后生成可供查询的列表。
  • 查询,先对输入分词,对每个单词逐个底层查询,最终合并结果,并计算分数。

详细内容见:https://www.jianshu.com/p/8e70f80cca80

3.结构化搜索

3.1 结构化数据

  • 布尔类型和数字类型是结构化的
  • 文本也可以是结构化的
    • 彩色笔可以有离散的颜色结合。
    • 博客可能被标记了标签。
    • 电商网站上的商品的标识符,严格规定结构化的格式

3.2 ES的结构化搜索

  • 布尔、时间、日期或者数字这类结构化数据:有精确的格式,可以进行逻辑操作。包括比较数字或者范围比较,判断两个值大小。
  • 结构化的文本可以做精确匹配或者部分匹配:Term查询/Prefix前缀查询。
  • 结构化结果只有是或者否两个值,可以根据场景需要决定是否打分。

范围查询案例:

POST products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "price": {
            "gte": 10,
            "lte": 20
          }
        }
      },
      "boost": 1.2
    }
  }
}

4.相关性算分

搜索的相关性算法,描述了一个文档和查询语句的匹配程度,ES会对每个匹配的查询条件做score,ES5以前用TF-IDF算法,现在用BM25/

一些基本概念:

词频(IF):检索词在一片文档中出现的频率。检索词出现的次数/文档总字数。可以把各个词频(除去没有用的词)想加得到一个评分。

逆文档频率(IDF):log(全部文档数/检索词出现过的文档总数)

TF-IDF:本质就是将TF加权求和,TF(区块链)IDF(区块链)+TF(的)IDF(的)+TF(应用)*IDF(应用)

4.1通过Boosting控制相关度

  • 当boost > 1时,打分的相关度相对性提升
  • 当 0 < boost < 1,打分的权重相对性降低
  • 当boost <0,贡献负分
POST score_test/_search
{
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "content": "live"
        }
      },
      "negative":{
        "term":{
          "content":"he"
        }
      },
      "negative_boost":0.3
    }
  }
}

匹配带live的,如果content字段里面包含了he,减分0.3

5. Query & Filter 多字段查询

5.1 bool查询

应用于多个字段,多个条件的符合查询。

  • 一个bool查询是一个或者多个查询的组合。
    • 总共包括4中子句,其中两种会影响算分,两种不影响分数。
  • 多个子句被合并为复合语句的时候,比如bool查询,每个语句的评分都会合并到总评分。
  • 同一个层级的条件具有相同的权重,可以调整嵌套结构控制算分
字段值 说明
must 必须匹配,贡献算分
should 选择性匹配,贡献算分
must_not filter context查询子句,必须不能匹配,不算评分
filter filter context 必须匹配,但是不贡献算分。
  • 子查询可以任意顺序出现
  • 可以嵌套多个查询
  • 如果没有must条件,should必须至少满足一条查询

案例

GET movies/_search
{
  "size": 20, 
  "query": {
    "bool": {
      "must": [
        {"term": {
          "title": {
            "value": "bob"
          }
        }}
      ], 
      "filter": [
        {
          "range": {
            "year": {
              "gte": 1991,
              "lte": 1994
            }
          }
        }
      ],
      "should": [
        {"term": {
          "id": {
            "value": "3809"
          }
        }},
         {"term": {
          "id": {
            "value": "1994"
          }
        }}
      ],
       "minimum_should_match": 1
    }
  }
}

标题里面包含bob,发行时间在1991年到1994年,最好id是3809或者1994,should条件最少满足一个。

嵌套案例,boost会增加。

GET movies/_search
{
 "query": {
   "bool": {
     "should": [
       {"bool": {
         "must": [
           {"term": {
             "title": {
               "value": "bob",
               "boost": 1.1
             }
           }}
         ]
       }}
     ]
   }
 }
}

5.2 单字符串多字段查询

案例,查询title或者year出现2012的电影,should换成must表示且。

GET movies/_search
{
  "explain": true,
  "query": {
    "bool": {
      "should": [
        {"match": {"title": "2012" }},
        {"match": {"year": "2012"}}
      ]
    }
  }
}

评分规则:

  • 查询should语句的两个查询
  • 对两个查询的评分求和
  • 乘以匹配语句的总数
  • 除以所有的语句的总数

基于这个评分规则,可能评分可能不是自己想要的。可以使用Disjunction Max Query,这样就会选择分数最佳的查询的结果的值,返回,不再是相加。

5.3 Disjunction Max Query

GET search_test/_search
{
  "query": {
    "dis_max": {
      "should": [
        {"match": {"title": "php class" }},
        {"match": {"body": "php class"}}
      ],
      "tie_breaker": 0.7
    }
  }
}

找出最大值,然后把其他的评分乘以0.7,然后想加得到一个新的评分。

5.3 Mulit Match

5.3.1 最佳字段(best field)

当字段之间存在相互竞争,又相互关联。类似title和body,评分来自最匹配的字段。

案例

POST movies/_search
{
  "query": {
    "multi_match": {
      "type": "best_fields", 
      "query": "1993",
      "fields": [
        "title",
        "id"
      ],
      "minimum_should_match": 1
      , "tie_breaker": 0.2
    }
  }
}
5.3.2 多字段(most field)
  • 在主字段(english analyzer),抽取词干,加入同义词,以匹配更多的项。
  • 相同的文本,加入子字段(standard analyzer),以提供更加精准的匹配。其他字段作为匹配文档提高相关度的信号,匹配的字段越多越好。

案例

PUT /titles 
{
  "mappings": {
    "properties": {
      "title":{
        "type": "text",
        "analyzer": "english",
        "fields": {
          "std":{
            "type":"text",
            "analyzer":"standard"
          }
        }
      }
    }
  }
}

std字段不会进行分词,是对title的优化
查询

GET /titles/_search
{
  "query": {
    "multi_match": {
      "type": "most_fields", 
      "query": "1",
      "fields": ["title^10","title.std"]
    }
  }
}

mostfield方式,title设置权重百分10。

5.3.3 混合字段(cross field)

对于某些实体,人名、地址。需要在多个字段中确定信息,单个字段只能作为整体的一部分。希望在列出的字段中尽可能找到多的词。

案例

POST address/_search
{
"query": {
"multi_match": {
"query": "poland street w",
"fields": ["street","city","country","postcode"],
"type": "cross_fields",
"operator": "and"
}
}
}

相对于通过_copyto方式,减少了磁盘空间,同时可以在搜索的时候为单个字段提升权重

6 search template

将搜索定义成模板,定义和使用的案例如下

POST _scripts/tmdb
{
"script":{
"lang":"mustache",
"source":{
"_source":["title","overview"],
"size":20,
"query":{
"multi_match":{
"query":"{{q}}",
"field":["title","overview"]
}
}
}
}
}
POST tmdb/_search/template
{
"id":"tmdb",
"params": {
"q":"basketball with"
}
}

7 index alias

给索引创建一个别名,实现灵停机运维。通过别名,索引的改名和重建程序不用动。

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "movies",
        "alias": "moveis-2019",
        "filter": {
          "range": {
            "year": {
              "gte": 2012
            }
          }
        }
      }
    }
  ]
}

你可能感兴趣的:(ES 深入理解搜索)