小言_互联网的博客

elasticsearch

296人阅读  评论(0)

1.倒排索引

1》mysql等数据库使用正向索引

如果根据id(索引列查询),速度会非常快,但是如果根据非索引列,并且模糊查询时,速度会非常慢,流程如下(比如id是索引列,title是要模糊查询的非索引列)

1)用户搜索数据,条件是title符合"%手机%"

2)逐行获取数据,比如id为1的数据

3)判断数据中的title是否符合用户搜索条件

4)如果符合则放入结果集,不符合则丢弃。回到步骤1

2》所以要用到倒排索引,倒排索引的搜索流程如下:

1)用户输入条件"华为手机"进行搜索。

2)对用户输入内容分词,得到词条:华为手机

3)拿着词条在倒排索引中查找,可以发现id为1 2 3的记录,或者有手机,或者有华为,或者两个都有,

4)拿着文档id 1 2 3到正向索引中查找具体文档。

3》那么为什么一个叫做正向索引,一个叫做倒排索引呢?

  • 正向索引是最传统的,根据id索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程

  • 倒排索引则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的id,然后根据id获取文档。是根据词条找文档的过程

  • 倒排索引虽然要先查询倒排索引,再查询正向索引,但是无论是词条、还是文档id都建立了索引,查询速度非常快!无需全表扫描。

2.  ElasticSearch 和mysql 的对应关系

 3.索引库dsl操作

3.1mapping映射属性

mapping是对索引库中文档的约束,常见的mapping属性包括:

  • type:字段数据类型,常见的简单类型有:

    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)

    • 数值:long、integer、short、byte、double、float、

    • 布尔:boolean

    • 日期:date

    • 对象:object

  • index:是否创建索引,默认为true

  • analyzer:使用哪种分词器

  • properties:该字段的子字段

例如下面的json文档:

{
    "age": 21,
    "weight": 52.1,
    "isMarried": false,
    "info": "黑马程序员Java讲师",
    "email": "zy@itcast.cn",
    "score": [99.1, 99.5, 98.9],
    "name": {
        "firstName": "云",
        "lastName": "赵"
    }
}

对应的每个字段映射(mapping):

  • age:类型为 integer;参与搜索,因此需要index为true;无需分词器

  • weight:类型为float;参与搜索,因此需要index为true;无需分词器

  • isMarried:类型为boolean;参与搜索,因此需要index为true;无需分词器

  • info:类型为字符串,需要分词,因此是text;参与搜索,因此需要index为true;分词器可以用ik_smart

  • email:类型为字符串,但是不需要分词,因此是keyword;不参与搜索,因此需要index为false;无需分词器

  • score:虽然是数组,但是我们只看元素的类型,类型为float;参与搜索,因此需要index为true;无需分词器

  • name:类型为object,需要定义多个子属性

    • name.firstName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器

    • name.lastName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器

3.2dsl操作索引库

json格式,用于ElasticSearch的语句,进行增删改查操作

1》创建索引库(索引库相当于mysql中的表)


  
  1. PUT /heima 索引库名
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "info":{ 文档名
  6.         "type":  "text", 类型
  7.          "analyzer""ik_smart" 使用的分词器
  8.       },
  9.       "email":{
  10.         "type":  "keyword",
  11.          "index""false" 是否使用倒排索引排序 ,电子邮箱,不会根据电子邮箱查询,所以是false,其他属性默认是true
  12.       },
  13.       "name":{
  14.         "properties": { 因为name中有姓和名两个属性,所以要写个properties
  15.           "firstName": {
  16.             "type":  "keyword"
  17.           }
  18.         }
  19.       },
  20.     }
  21.   }
  22. }

2》查询索引库结构

GET /heima

3》修改索引库结构

倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改mapping

只能增加新的属性


  
  1. PUT /heima/_mapping
  2. {
  3. "properties":{
  4. "age":{
  5. "type": "integer"
  6. }
  7. }
  8. }

4》删除索引库

DELETE /索引库名

4.文档dsl操作

    1.添加一条文档(记录) 如果该文档已存在,则更新该文档


  
  1. POST /heima/_doc/ 1
  2. {
  3. "info": "黑马程序员java讲师",
  4. "email": "1960703672@qq.com",
  5. "name":{
  6. "firstname": "云",
  7. "lastname": "ZHAO"
  8. }
  9. }

2. 查询一条文档(记录)

GET /heima/_doc/1

3.删除一条文档(记录)

DELETE /heima/_doc/1

4.修改一条文档,如果不存在,就创建新的该条文档


  
  1. POST /heima/_doc/ 1
  2. {
  3. "info": "黑马程序员java讲师",
  4. "email": "1960703672@qq.com",
  5. "name":{
  6. "firstname": "云",
  7. "lastname": "ZHAO"
  8. }
  9. }

5.局部修改文档字段


  
  1. POST /heima /_update / 1
  2. {
  3. "doc" : {
  4. "email" : "1195"
  5. }
  6. }

5.dsl创建索引练习及文档查询操作

  • location:地理坐标,里面包含精度、纬度

  • all:一个组合字段,其目的是将多字段的值 利用copy_to合并,提供给用户搜索,
    会创建一个新的all字段,包含使用了all的其他字段(下面代码中的name,brand,city),并创建反向索引


  
  1. PUT /hotel
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "id" : {
  6. "type" : "keyword"
  7. } ,
  8. "name" : {
  9. "type" : "text" ,
  10. "analyzer" : "ik_max_word" ,
  11. "copy_to" : "all"
  12. } ,
  13. "address" : {
  14. "type" : "keyword" ,
  15. "index" : false
  16. } ,
  17. "price" : {
  18. "type" : "integer"
  19. } ,
  20. "score" : {
  21. "type" : "integer"
  22. } ,
  23. "brand" : {
  24. "type" : "keyword" ,
  25. "copy_to" : "all"
  26. } ,
  27. "city" : {
  28. "type" : "keyword" ,
  29. "copy_to" : "all"
  30. } ,
  31. "starName" : {
  32. "type" : "keyword"
  33. } ,
  34. "business" : {
  35. "type" : "keyword"
  36. } ,
  37. "location" : {
  38. "type" : "geo_point"
  39. } ,
  40. "pic" : {
  41. "type" : "keyword" ,
  42. "index" : false
  43. } ,
  44. "all" : {
  45. "type" : "text" ,
  46. "analyzer" : "ik_max_word"
  47. }
  48. }
  49. }
  50. }

文档查询操作

1.查询所有文档


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. }
  6. }

2. 全文检索查询

模糊查询info中有java工程师的,模糊查询不太准确,因为是根据分词来倒排索引查询的,比如分成了java 和工程师,会去找eleaticsearch分词后的字典中工程师和java对应的id,所以比如查询参数的info是 java 哈哈哈哈 工程师,是可以查询出来的,但如果是java 工程大师,工程大师在eleaticsearch分词后的字典中没有(就是eleaticsearch的info中没有工程大师info的记录),就查询不出来。


  
  1. GET /heima2/_search
  2. {
  3. "query": {
  4. "match": { "info": "java工程师"}
  5. }
  6. }

   在定义索引(表结构)时,定义了all字段,关联了 酒店名称 品牌 城市 三个字段
    所以可以直接根据all字段查询,会查询酒店名称  或者品牌 或者城市中有上海的


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "match": {
  5. "all": "上海"
  6. }
  7. }
  8. }

3.全文检索查询2

如果没有定义all字段,想查询酒店名称  或者品牌 或者城市中有上海的 ,就必须使用如下的查询方式,速度很慢,推荐在定义表时使用all字段,查询all字段。


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "multi_match": {
  5. "query": "杭州",
  6. "fields": [ "brand", "name", "bussiness"]
  7. }
  8. }
  9. }

4.term精确查询

查询城市上海的 (比如有个上海市,则不能查询到)


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "term": {
  5. "city": {
  6. "value": "上海"
  7. }
  8. }
  9. }
  10. }

5.范围查询  要查询的字段必须是数字类型,可以比较的。


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "range": {
  5. "price": {
  6. "gte": 1000,
  7. "lte": 2000
  8. }
  9. }
  10. }
  11. }

6.地理位置查询1(不常用)画一个矩形,查询在该矩形范围内的所有地址


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "geo_bounding_box": {
  5. "location": {
  6. "top_left": {
  7. "lat": 31.1,
  8. "lon": 121.5
  9. },
  10. "bottom_right": {
  11. "lat": 30.9,
  12. "lon": 0.0
  13. }
  14. }
  15. }
  16. }
  17. }

7.地理位置查询2 (很常用)  附近的人,查询距离某个点什么范围的记录


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "geo_distance": {
  5. "distance": "15km",
  6. "location": "31.21,121.5"
  7. }
  8. }
  9. }

8.设置查询的优先级

在使用match查询时,文档结果会根据与搜索词条的关联度打分(_score),返回结果时按照分值降序排列。比如在搜索虹桥如家时:


  
  1. [
  2.   {
  3.      "_score" :  17.850193,
  4.      "_source" : {
  5.        "name" :  "虹桥如家酒店真不错",
  6.     }
  7.   },
  8.   {
  9.      "_score" :  12.259849,
  10.      "_source" : {
  11.        "name" :  "外滩如家酒店真不错",
  12.     }
  13.   },
  14.   {
  15.      "_score" :  11.91091,
  16.      "_source" : {
  17.        "name" :  "迪士尼如家酒店真不错",
  18.     }
  19.   }
  20. ]

 算法主要有两种:

 而这个算分结果,是可以被干预的,比如,收了如家的广告费,想要如家的搜索结果提前

实现:

     

 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)

  • 过滤条件:filter部分,符合该条件的文档才会重新算分

  • 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数

    • weight:函数结果是常量

    • field_value_factor:以文档中的某个字段值作为函数结果

    • random_score:以随机数作为函数结果

    • script_score:自定义算分函数算法

  • 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:

    • multiply:相乘

    • replace:用function score替换query score

    • 其它,例如:sum、avg、max、min


  
  1. GET /hotel/_search
  2. {
  3. "query": {
  4. "function_score": {
  5. "query": {
  6. "match": {
  7. "all": "外滩"
  8. }
  9. },
  10. "functions": [
  11. {
  12. "filter": {
  13. "term": {
  14. "brand": "如家"
  15. }
  16. },
  17. "weight": 10
  18. }
  19. ]
  20. }
  21. }
  22. }

查询结果:

9. 布尔查询

    一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”

  • should:选择性匹配子查询,类似“或”

  • must_not:必须不匹配,不参与算分,类似“非”

  • filter:必须匹配,不参与算分

  • 比如在搜索酒店时,除了关键字搜索外,我们还可能根据品牌、价格、城市等字段做过滤:

    每一个不同的字段,其查询的条件、方式都不一样,必须是多个不同的查询,而要组合这些查询,就必须用bool查询了。

  • 查询城市是上海的,品牌是皇冠假日或者华美达的,价格大于500的,评分大于45的酒店

  • 
        
    1. #布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询
    2. GET /hotel/_search
    3. {
    4. "query": {
    5. "bool": {
    6. "must": [
    7. {
    8. "term": {
    9. "city": "上海"
    10. }
    11. }
    12. ],
    13. "should": [
    14. { "term": { "brand": "皇冠假日"}},
    15. { "term":{ "brand": "华美达"}}
    16. ],
    17. "must_not": [
    18. { "range": { "price": {
    19. "lte": 500
    20. }}}
    21. ],
    22. "filter": [
    23. {
    24. "range": {
    25. "score": {
    26. "gte": 45
    27. }
    28. }
    29. }
    30. ]
    31. }
    32. }
    33. }
  • 10.根据地理位置排序(查询距离某个点最近的)

  • 
        
    1. GET /hotel/_search
    2. {
    3. "query": {
    4. "match_all": {}
    5. },
    6. "sort": [
    7. {
    8. "_geo_distance": {
    9. "location": {
    10. "lat": 40.476483,
    11. "lon": 115.97481
    12. },
    13. "order": "asc"
    14. }
    15. }
    16. ]
    17. }

  • 6.搜索结果处理

  • 6.1手动排序  

  • elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

    手动排序后,不会再打分,也就没有_score属性了。

  • 
        
    1. GET /indexName/_search
    2. {
    3.    "query": {
    4.      "match_all": {}
    5.   },
    6.    "sort": [
    7.     {
    8.        "FIELD""desc"   // 排序字段、排序方式ASC、DESC
    9.     }
    10.   ]
    11. }

    6.2分页查询

  • elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch中通过修改from、size参数来控制要返回的分页结果:

  • from:从第几个文档开始

  • size:总共查询几个文档

  • 
        
    1. GET /hotel/_search
    2. {
    3.    "query": {
    4.      "match_all": {}
    5.   },
    6.    "from"0// 分页开始的位置,默认为0
    7.    "size"10// 期望获取的文档总数
    8. }

    6.3高亮

  • 高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。

  • 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮

  • 如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false

    
        
    1. #fields指定高亮的标签,如果搜索字段和高亮字段(即city和name)不是一个,需要require_field_match设为false
    2. GET /hotel/_search
    3. {
    4. "query": {
    5. "match": {
    6. "city": "上海"
    7. }
    8. },
    9. "highlight": {
    10. "fields": {
    11. "name": {
    12. "require_field_match": "false"
    13. }
    14. }
    15. }
    16. }

    6.4聚合查询

  • 统计所有酒店品牌拥有的酒店数,并升序排列

  • 
        
    1. GET /hotel/_search
    2. {
    3.    "size"0,   // 设置size为0,结果中不包含文档,只包含聚合结果
    4.    "aggs": {  // 定义聚合
    5.      "brandAgg": {  //给聚合起个名字
    6.        "terms": {  // 聚合的类型,按照品牌值聚合,所以选择term
    7.          "field""brand"// 参与聚合的字段
    8.          "size"20// 希望获取的聚合结果数量,比如获取出现次数前二十的,
    9. "order": { //根据出现次数递增排序
    10. "_count": "asc"
    11. }
    12.       }
    13.     }
    14.   }
    15. }
  • 6.5嵌套聚合

  • 统计每个品牌的,最高评分,最低评分,平均值
  • 
        
    1. GET /hotel/_search
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "brandAgg": {
    6. "terms": {
    7. "field": "brand",
    8. "size": 20
    9. },
    10. "aggs": {
    11. "scoreAgg": {
    12. "stats": {
    13. "field": "score"
    14. }
    15. }
    16. }
    17. }
    18. }
    19. }


转载:https://blog.csdn.net/sharesb/article/details/128302665
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场