本文介绍如何在Elasticsearch中对数据进行搜索。

1、简述

在Elasticsearch中的搜索中,有两类搜索:

  • queries
  • aggregations

区别在于:query可以进行全文搜索,而aggregation可以进行统计及分析。当然可以结合query及aggregation一起使用,比如先对文档进行搜索,然后进行aggregation。

  1. GET blogs/_search
  2. {
  3. "query": {
  4. "match": {
  5. "title": "community"
  6. }
  7. },
  8. "aggregations": {
  9. "top_authors": {
  10. "terms": {
  11. "field": "author"
  12. }
  13. }
  14. }
  15. }

在上面搜索中,先搜索title含有community的文档,然后再对数据进行aggregation。

2、搜索所有文档

使用如下命令搜索所有文档:

  1. GET /_search

此时没指定任何index,因此搜索该cluster下的所有的index。默认返回个数是10个,除非设定size:

  1. GET /_search?size=20

上面命令也等同于:

  1. GET /_all/_search

可以对多个index进行搜索:

  1. POST /index1,index2,index3/_search

也可以这么写,表明针对所有以index开头的索引进行搜索,但排除index3索引。

  1. POST /index*,-index3/_search

如果想对特定的index进行搜索,可以这样:

  1. GET twitter/_search

上图中,可以看到twiter索引里有7个文档。再hits数组里可以看到所有的结果。同时也可以看到_score 的项,表示我们搜索结果的相关度。这个分数值越高表明搜索匹配的相关度越高。再默认没有sort的情况下,所有搜索的结果读书安装分数由大到小来进行排列的。

在默认情况下,可以得到10个结果,当然可以通过设置size参数来得到想要的个数。同时也可以配合from来进行分页。

  1. GET twitter/_search?size=2&from=2

并且只显示两个文档。通过这种方法可以对文档进行分页显示。

上面查询类似DSL查询的如下语句:

  1. GET twitter/_search
  2. {
  3. "size": 2,
  4. "from": 2,
  5. "query": {
  6. "match_all": {}
  7. }
  8. }

可以通过filter_path来控制输出的较少的字段,比如:

  1. GET twitter/_search?filter_path=hits.total

3、_source filtering

通过_source来定义想要返回的字段:

  1. GET twitter/_search
  2. {
  3. "_source": ["user", "city"],
  4. "query": {
  5. "match_all": {
  6. }
  7. }
  8. }

也可以使用这种方式

  1. GET twitter/_search
  2. {
  3. "_source": {
  4. "includes": ["user", "city"]
  5. },
  6. "query": {
  7. "match_all": {
  8. }
  9. }
  10. }

可以设置_source为false,这样不返回任何_source信息:

  1. GET twitter/_search
  2. {
  3. "_source": false,
  4. "query": {
  5. "match": {
  6. "user": "张三"
  7. }
  8. }
  9. }

也可以接收通配符形式的控制:

  1. GET twitter/_search
  2. {
  3. "_source": {
  4. "includes": [
  5. "user*",
  6. "location*"
  7. ],
  8. "excludes": [
  9. "*.lat"
  10. ]
  11. },
  12. "query": {
  13. "match_all": {}
  14. }
  15. }

如果我们把 _source 设置为[],则显示所有的字段:

  1. GET twitter/_search
  2. {
  3. "_source": [],
  4. "query": {
  5. "match_all": {
  6. }
  7. }
  8. }

4、script fields

有事,我们想要的field可能在_source里根本没有,则可以使用script field来生成这些field。允许为每个匹配返回script evaluation(基于不用的字段):

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "script_fields": {
  7. "years_to_100": {
  8. "script": {
  9. "lang": "painless",
  10. "source": "100-doc['age'].value"
  11. }
  12. },
  13. "year_of_birth":{
  14. "script": "2019 - doc['age'].value"
  15. }
  16. }
  17. }

注意,使用script的方法来生成查询结果对于大量的文档来说,可能会占用大量资源。doc在这里指定是doc value。否则的话,我们需要使用ctx._source来做一些搜索动作。所以可以把上面命令修改为:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "script_fields": {
  7. "years_to_100": {
  8. "script": {
  9. "lang": "painless",
  10. "source": "100-params._source['age']"
  11. }
  12. },
  13. "year_of_birth":{
  14. "script": "2019 - params._source['age']"
  15. }
  16. }
  17. }

因为age是long数据类型。它是由doc value的,所以我们通过doc['age']来访问,而且这些访问是比较快的。

5、count api

使用_count来查询索引里有多少文档:

  1. GET twitter/_count

可以根据满足条件来查询文档数量(比如,可以查询到city为北京的所有文档的数量):

  1. GET twitter/_count
  2. {
  3. "query": {
  4. "match": {
  5. "city": "北京"
  6. }
  7. }
  8. }

6、settings

通过接口获取一个index的settings:

  1. GET twitter/_settings

从图中可以看到twitter索引中有多少个shards及多少个reblicas。我们也可以通过如下的接口来设置:

  1. PUT twitter
  2. {
  3. "settings": {
  4. "number_of_shards": 1,
  5. "number_of_replicas": 1
  6. }
  7. }

一旦把number_of_shards定下来了,就不可以修改了。除非把index删除,并重新index它。因为每个文档存储到哪一个shard是和number_of_shards这个数值有关的。一旦这个数值发生改变,那么之后寻找哪个文档所在的shard就会不准确。

7、mapping

查询当前index的mapping

  1. GET twitter/_mapping

从上图的显示中可以看出来location里的经纬度是一个multi-field类型。这显然不是我们所需的。正确的类型应该是:geo_point。因此需要修正我们的mapping。

注意:

我们不能为已经建立好的index动态修改mapping。这是因为一旦修改,那么之前建立的索引就变成不能搜索了。一种办法是reindex从而重新建立我们的索引。如果之前的mapping加入新的字段,那么欧盟可以不用重新建立索引。

为了能能够正确地创建我们的mapping,我们必须先把之前的twitter索引删除掉,并同时使用settings来创建这个index。具体的步骤如下:

  1. DELETE twitter
  1. PUT twitter
  2. {
  3. "settings": {
  4. "number_of_shards": 1,
  5. "number_of_replicas": 1
  6. }
  7. }
  1. PUT twitter/_mapping
  2. {
  3. "properties": {
  4. "address": {
  5. "type": "text",
  6. "fields": {
  7. "keyword": {
  8. "type": "keyword",
  9. "ignore_above": 256
  10. }
  11. }
  12. },
  13. "age": {
  14. "type": "long"
  15. },
  16. "city": {
  17. "type": "text",
  18. "fields": {
  19. "keyword": {
  20. "type": "keyword",
  21. "ignore_above": 256
  22. }
  23. }
  24. },
  25. "country": {
  26. "type": "text",
  27. "fields": {
  28. "keyword": {
  29. "type": "keyword",
  30. "ignore_above": 256
  31. }
  32. }
  33. },
  34. "location": {
  35. "type": "geo_point"
  36. },
  37. "message": {
  38. "type": "text",
  39. "fields": {
  40. "keyword": {
  41. "type": "keyword",
  42. "ignore_above": 256
  43. }
  44. }
  45. },
  46. "province": {
  47. "type": "text",
  48. "fields": {
  49. "keyword": {
  50. "type": "keyword",
  51. "ignore_above": 256
  52. }
  53. }
  54. },
  55. "uid": {
  56. "type": "long"
  57. },
  58. "user": {
  59. "type": "text",
  60. "fields": {
  61. "keyword": {
  62. "type": "keyword",
  63. "ignore_above": 256
  64. }
  65. }
  66. }
  67. }
  68. }

重新查看mapping,此时可以看到已经创建好了新的mapping,并更正了location类型:

  1. GET twitter/_mapping

再次运行之前的bulk接口,并把所需要的数据导入到twitter索引中。

  1. POST _bulk
  2. { "index" : { "_index" : "twitter", "_id": 1} }
  3. {"user":"双榆树-张三","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"北京","province":"北京","country":"中国","address":"中国北京市海淀区","location":{"lat":"39.970718","lon":"116.325747"}}
  4. { "index" : { "_index" : "twitter", "_id": 2 }}
  5. {"user":"东城区-老刘","message":"出发,下一站云南!","uid":3,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区台基厂三条3号","location":{"lat":"39.904313","lon":"116.412754"}}
  6. { "index" : { "_index" : "twitter", "_id": 3} }
  7. {"user":"东城区-李四","message":"happy birthday!","uid":4,"age":30,"city":"北京","province":"北京","country":"中国","address":"中国北京市东城区","location":{"lat":"39.893801","lon":"116.408986"}}
  8. { "index" : { "_index" : "twitter", "_id": 4} }
  9. {"user":"朝阳区-老贾","message":"123,gogogo","uid":5,"age":35,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区建国门","location":{"lat":"39.718256","lon":"116.367910"}}
  10. { "index" : { "_index" : "twitter", "_id": 5} }
  11. {"user":"朝阳区-老王","message":"Happy BirthDay My Friend!","uid":6,"age":50,"city":"北京","province":"北京","country":"中国","address":"中国北京市朝阳区国贸","location":{"lat":"39.918256","lon":"116.467910"}}
  12. { "index" : { "_index" : "twitter", "_id": 6} }
  13. {"user":"虹桥-老吴","message":"好友来了都今天我生日,好友来了,什么 birthday happy 就成!","uid":7,"age":90,"city":"上海","province":"上海","country":"中国","address":"中国上海市闵行区","location":{"lat":"31.175927","lon":"121.383328"}}

至此,我们已经完整地建立了我们所需要的索引。在下面,我们开始使用DSL(Domain Specific language)来进行查询。

8、查询数据

1)match query

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "city": "北京"
  6. }
  7. }
  8. }

从上述查询结果来看,可以看到有5个用户来自北京。而且查询出来的结果是按照关联(relavance)来进行排序的。

也可以使用script query来完成:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "script": {
  5. "script": {
  6. "source": "doc['city.keyword'].contains(params.name)",
  7. "lang": "painless",
  8. "params": {
  9. "name": "北京"
  10. }
  11. }
  12. }
  13. }
  14. }

script query 和match query查询结果是一样的。但是不建议使用script query方法(比较低效)。假如文档是几百万或者PB级的数据量,那么上面的运算可能被执行无数次,那么可能需要巨大的计算量。在这种情况下,我们需要考虑ingest的时候做计算。

上述的搜索也可以这么实现:

  1. GET twitter/_search?q=city:"北京"

显示如下:

_score项说明:表示与搜索结果相关度。分值越高,表明搜索匹配的相关度越高。在默认没有sort的情况下,所有的搜索的结果都是按照分数由大到小来进行排列的。

如果不要score,我们可以选择filter来完成。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "filter": {
  6. "term": {
  7. "city.keyword": "北京"
  8. }
  9. }
  10. }
  11. }
  12. }

从返回的结果来看,_score项为0.对于这种搜索,只要yes或no。我们并不关心她们的相关性。city.keyword表示在mapping中是一个multi-field项。它既是text也是keyword类型。对于一个keyword类型的项来说,这个项里面保存所有字符都被当作一个字符串。它们在建立文档时,不需要进行index。keyword字段用于精确搜索,aggregation和排序(sorting)。所以在filter中,使用term来完成查询。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "constant_score": {
  5. "filter": {
  6. "term": {
  7. "city.keyword": {
  8. "value": "北京"
  9. }
  10. }
  11. }
  12. }
  13. }
  14. }

使用match query时,默认的操作是or。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "user": {
  6. "query": "朝阳区-老贾",
  7. "operator": "or"
  8. }
  9. }
  10. }
  11. }

等同于

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "user": "朝阳区-老贾"
  6. }
  7. }
  8. }

因为默认操作是or,上面的查询的结果是任何匹配:"朝"、"阳"、"区"、"老"和"贾"这5个字中的任何一个将被显示。具体数据显示如下(注意,score相关性的大小)。

  1. {
  2. "took" : 1,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 5,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 4.4209847,
  16. "hits" : [
  17. {
  18. "_index" : "twitter",
  19. "_type" : "_doc",
  20. "_id" : "4",
  21. "_score" : 4.4209847,
  22. "_source" : {
  23. "user" : "朝阳区-老贾",
  24. "message" : "123,gogogo",
  25. "uid" : 5,
  26. "age" : 35,
  27. "city" : "北京",
  28. "province" : "北京",
  29. "country" : "中国",
  30. "address" : "中国北京市朝阳区建国门",
  31. "location" : {
  32. "lat" : "39.718256",
  33. "lon" : "116.367910"
  34. }
  35. }
  36. },
  37. {
  38. "_index" : "twitter",
  39. "_type" : "_doc",
  40. "_id" : "5",
  41. "_score" : 2.9019678,
  42. "_source" : {
  43. "user" : "朝阳区-老王",
  44. "message" : "Happy BirthDay My Friend!",
  45. "uid" : 6,
  46. "age" : 50,
  47. "city" : "北京",
  48. "province" : "北京",
  49. "country" : "中国",
  50. "address" : "中国北京市朝阳区国贸",
  51. "location" : {
  52. "lat" : "39.918256",
  53. "lon" : "116.467910"
  54. }
  55. }
  56. },
  57. {
  58. "_index" : "twitter",
  59. "_type" : "_doc",
  60. "_id" : "2",
  61. "_score" : 0.8713734,
  62. "_source" : {
  63. "user" : "东城区-老刘",
  64. "message" : "出发,下一站云南!",
  65. "uid" : 3,
  66. "age" : 30,
  67. "city" : "北京",
  68. "province" : "北京",
  69. "country" : "中国",
  70. "address" : "中国北京市东城区台基厂三条3号",
  71. "location" : {
  72. "lat" : "39.904313",
  73. "lon" : "116.412754"
  74. }
  75. }
  76. },
  77. {
  78. "_index" : "twitter",
  79. "_type" : "_doc",
  80. "_id" : "6",
  81. "_score" : 0.4753614,
  82. "_source" : {
  83. "user" : "虹桥-老吴",
  84. "message" : "好友来了都今天我生日,好友来了,什么 birthday happy 就成!",
  85. "uid" : 7,
  86. "age" : 90,
  87. "city" : "上海",
  88. "province" : "上海",
  89. "country" : "中国",
  90. "address" : "中国上海市闵行区",
  91. "location" : {
  92. "lat" : "31.175927",
  93. "lon" : "121.383328"
  94. }
  95. }
  96. },
  97. {
  98. "_index" : "twitter",
  99. "_type" : "_doc",
  100. "_id" : "3",
  101. "_score" : 0.4356867,
  102. "_source" : {
  103. "user" : "东城区-李四",
  104. "message" : "happy birthday!",
  105. "uid" : 4,
  106. "age" : 30,
  107. "city" : "北京",
  108. "province" : "北京",
  109. "country" : "中国",
  110. "address" : "中国北京市东城区",
  111. "location" : {
  112. "lat" : "39.893801",
  113. "lon" : "116.408986"
  114. }
  115. }
  116. }
  117. ]
  118. }
  119. }

可以设置minimum_should_match来设置至少匹配的term。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "user": {
  6. "query": "朝阳区-老贾",
  7. "operator": "or",
  8. "minimum_should_match": 3
  9. }
  10. }
  11. }
  12. }

上面显示,我们至少要匹配"朝"、"阳"、"区"、"老"和"贾"这5个字中的3个字才可以。显示结果如下:

  1. {
  2. "took" : 0,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 4.4209847,
  16. "hits" : [
  17. {
  18. "_index" : "twitter",
  19. "_type" : "_doc",
  20. "_id" : "4",
  21. "_score" : 4.4209847,
  22. "_source" : {
  23. "user" : "朝阳区-老贾",
  24. "message" : "123,gogogo",
  25. "uid" : 5,
  26. "age" : 35,
  27. "city" : "北京",
  28. "province" : "北京",
  29. "country" : "中国",
  30. "address" : "中国北京市朝阳区建国门",
  31. "location" : {
  32. "lat" : "39.718256",
  33. "lon" : "116.367910"
  34. }
  35. }
  36. },
  37. {
  38. "_index" : "twitter",
  39. "_type" : "_doc",
  40. "_id" : "5",
  41. "_score" : 2.9019678,
  42. "_source" : {
  43. "user" : "朝阳区-老王",
  44. "message" : "Happy BirthDay My Friend!",
  45. "uid" : 6,
  46. "age" : 50,
  47. "city" : "北京",
  48. "province" : "北京",
  49. "country" : "中国",
  50. "address" : "中国北京市朝阳区国贸",
  51. "location" : {
  52. "lat" : "39.918256",
  53. "lon" : "116.467910"
  54. }
  55. }
  56. }
  57. ]
  58. }
  59. }

也可以修改为and操作。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "user": {
  6. "query": "朝阳区-老贾",
  7. "operator": "and"
  8. }
  9. }
  10. }
  11. }

显示结果:

  1. {
  2. "took" : 15,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 1,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 4.4209847,
  16. "hits" : [
  17. {
  18. "_index" : "twitter",
  19. "_type" : "_doc",
  20. "_id" : "4",
  21. "_score" : 4.4209847,
  22. "_source" : {
  23. "user" : "朝阳区-老贾",
  24. "message" : "123,gogogo",
  25. "uid" : 5,
  26. "age" : 35,
  27. "city" : "北京",
  28. "province" : "北京",
  29. "country" : "中国",
  30. "address" : "中国北京市朝阳区建国门",
  31. "location" : {
  32. "lat" : "39.718256",
  33. "lon" : "116.367910"
  34. }
  35. }
  36. }
  37. ]
  38. }
  39. }

在这种情况下,需要索引匹配5个字才可以。显然我们可以通过使用and来提高搜索的精度。

2)Ids query

可以通过id来查询。比如:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "ids": {
  5. "values": ["1", "2"]
  6. }
  7. }
  8. }

上述查询将返回id为1、2的文档。

3)multi_match

上面的搜索之中,我们特别指明一个专有的field来进行搜索,但是在很多情况下,我们并不知道哪一个是field含有这个关键字。在这种情况下,我们可以使用multi_match来进行搜索:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "multi_match": {
  5. "query": "朝阳",
  6. "fields": [
  7. "user",
  8. "address^3",
  9. "message"
  10. ],
  11. "type": "best_fields"
  12. }
  13. }
  14. }

上述代码中,multi_search的type为best_fields,也就是说它搜索了3个字段。最终分数_score是按照得分最高那个字段分数为准。代码中可以同时对fields:user,address,message进行搜索,但是我们对address含有"朝阳"的文档的分数进行3倍加权。返回结果:

  1. "hits" : [
  2. {
  3. "_index" : "twitter",
  4. "_type" : "_doc",
  5. "_id" : "5",
  6. "_score" : 6.1777167,
  7. "_source" : {
  8. "user" : "朝阳区-老王",
  9. "message" : "Happy BirthDay My Friend!",
  10. "uid" : 6,
  11. "age" : 50,
  12. "city" : "北京",
  13. "province" : "北京",
  14. "country" : "中国",
  15. "address" : "中国北京市朝阳区国贸",
  16. "location" : {
  17. "lat" : "39.918256",
  18. "lon" : "116.467910"
  19. }
  20. }
  21. },
  22. {
  23. "_index" : "twitter",
  24. "_type" : "_doc",
  25. "_id" : "4",
  26. "_score" : 5.9349246,
  27. "_source" : {
  28. "user" : "朝阳区-老贾",
  29. "message" : "123,gogogo",
  30. "uid" : 5,
  31. "age" : 35,
  32. "city" : "北京",
  33. "province" : "北京",
  34. "country" : "中国",
  35. "address" : "中国北京市朝阳区建国门",
  36. "location" : {
  37. "lat" : "39.718256",
  38. "lon" : "116.367910"
  39. }
  40. }
  41. }
  42. ]

4)Prefix query

返回提供的字段中包含特定前缀的文档。如返回user字段中包含"朝"开头的文档:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "prefix": {
  5. "user": {
  6. "value": "朝"
  7. }
  8. }
  9. }
  10. }

5) Term query

term query 会在给定字段中进行精确的字词匹配。因此,您需要提供准确的术语以及获取正确的结果。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "term": {
  5. "user.keyword": {
  6. "value": "朝阳区-老贾"
  7. }
  8. }
  9. }
  10. }

在这里,使用 user.keyword 来对“朝阳区-老贾”进行精确匹配查询相应的文档。

6)Terms query

对多个 terms 进行查询,可以使用如下的方式。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "terms": {
  5. "user.keyword": [
  6. "双榆树-张三",
  7. "东城区-老刘"
  8. ]
  9. }
  10. }
  11. }

7)Term_set query

查询在提供的字段中包含最少数目的精确术语的文档。除可以定义返回文档所需的匹配术语数之外,terms_set 查询与术语查询相同。

  1. PUT /job-candidates
  2. {
  3. "mappings": {
  4. "properties": {
  5. "name": {
  6. "type": "keyword"
  7. },
  8. "programming_languages": {
  9. "type": "keyword"
  10. },
  11. "required_matches": {
  12. "type": "long"
  13. }
  14. }
  15. }
  16. }
  1. PUT /job-candidates/_doc/1?refresh
  2. {
  3. "name": "Jane Smith",
  4. "programming_languages": [ "c++", "java" ],
  5. "required_matches": 2
  6. }
  1. PUT /job-candidates/_doc/2?refresh
  2. {
  3. "name": "Jason Response",
  4. "programming_languages": [ "java", "php" ],
  5. "required_matches": 2
  6. }
  1. GET /job-candidates/_search
  2. {
  3. "query": {
  4. "terms_set": {
  5. "programming_languages": {
  6. "terms": [ "c++", "java", "php" ],
  7. "minimum_should_match_field": "required_matches"
  8. }
  9. }
  10. }
  11. }

在上面,我们为job-candidates索引创建了两文档。此时需要找出在programming_languages中同时包含c++、java以及php中至少两个term的文档。此时上述代码使用了一个在文档中定义的字段required_matches来定义最少满足要求的term个数。当如果没有一个专有的字段来定义这个字段的话,另外一种方式是使用mininum_should_match_script来定义:

  1. GET /job-candidates/_search
  2. {
  3. "query": {
  4. "terms_set": {
  5. "programming_languages": {
  6. "terms": [ "c++", "java", "php" ],
  7. "minimum_should_match_script": {
  8. "source": "2"
  9. }
  10. }
  11. }
  12. }
  13. }

上面标示要至少同时满足2个及以上的term。上面搜索结果为:

  1. {
  2. "took" : 30,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 1.1005894,
  16. "hits" : [
  17. {
  18. "_index" : "job-candidates",
  19. "_type" : "_doc",
  20. "_id" : "1",
  21. "_score" : 1.1005894,
  22. "_source" : {
  23. "name" : "Jane Smith",
  24. "programming_languages" : [
  25. "c++",
  26. "java"
  27. ],
  28. "required_matches" : 2
  29. }
  30. },
  31. {
  32. "_index" : "job-candidates",
  33. "_type" : "_doc",
  34. "_id" : "2",
  35. "_score" : 1.1005894,
  36. "_source" : {
  37. "name" : "Jason Response",
  38. "programming_languages" : [
  39. "java",
  40. "php"
  41. ],
  42. "required_matches" : 2
  43. }
  44. }
  45. ]
  46. }
  47. }

也就是说之前的两个文档都同时满足条件。如果使用如下方式来进行搜索:

  1. GET /job-candidates/_search
  2. {
  3. "query": {
  4. "terms_set": {
  5. "programming_languages": {
  6. "terms": [ "c++", "java", "nodejs" ],
  7. "minimum_should_match_script": {
  8. "source": "2"
  9. }
  10. }
  11. }
  12. }
  13. }

我们将看到只有一个文档是满足条件的。

9、复合查询(compound query)

格式如下:

  1. POST _search
  2. {
  3. "query": {
  4. "bool" : {
  5. "must" : {
  6. "term" : { "user" : "kimchy" }
  7. },
  8. "filter": {
  9. "term" : { "tag" : "tech" }
  10. },
  11. "must_not" : {
  12. "range" : {
  13. "age" : { "gte" : 10, "lte" : 20 }
  14. }
  15. },
  16. "should" : [
  17. { "term" : { "tag" : "wow" } },
  18. { "term" : { "tag" : "elasticsearch" } }
  19. ],
  20. "minimum_should_match" : 1,
  21. "boost" : 1.0
  22. }
  23. }
  24. }

从上面代码中可以看出,它是由 bool 下面的 must, must_not, should 及 filter 共同来组成的。

用例:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "city": "北京"
  9. }
  10. },
  11. {
  12. "match": {
  13. "age": "30"
  14. }
  15. }
  16. ]
  17. }
  18. }
  19. }

这个查询要求是必须是北京城市的,并且年龄刚好是30岁的。

如果想知道为什么的出来这样的结果,我们可以在搜索的指令中加如"explained":"true"。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "city": "北京"
  9. }
  10. },
  11. {
  12. "match": {
  13. "age": "30"
  14. }
  15. }
  16. ]
  17. }
  18. },
  19. "explain": true
  20. }

这样在显示结果中可以看到一些解释:

同样如果需要排除某些条件,我们可以使用must_not。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must_not": [
  6. {
  7. "match": {
  8. "city": "北京"
  9. }
  10. }
  11. ]
  12. }
  13. }
  14. }

这个代码表示我们想寻找不在北京的所有文档。所以显示的文档只有一个,即来自上海的文档。

should表述"或"的意思,也就是有就更好,没有就算了。比如:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "age": "30"
  9. }
  10. }
  11. ],
  12. "should": [
  13. {
  14. "match_phrase": {
  15. "message": "Happy birthday"
  16. }
  17. }
  18. ]
  19. }
  20. }
  21. }

搜索意思是age必须是30岁,如果文档中含有"Happy birthday",则相关性会更高,那么搜索得到的结果会排在前面。

在上面的结果中,我们可以看到:同样是年龄30岁的两个文档,第一个文档由于含有 “Happy birthday” 这个字符串在 message 里,所以它的结果是排在前面的,相关性更高。我们可以从它的 _score 中可以看出来。第二个文档里 age 是30,但是它的 message 里没有 “Happy birthday” 字样,但是它的结果还是有显示,只是得分比较低一些。

在使用上面的复合查询时,bool 请求通常是 must,must_not, should 及 filter 的一个或其中的几个一起组合形成的。我们必须注意的是:

查询类型对 hits 及 _score 的影响

Clause 影响 #hits 影响 _score
must Yes Yes
must_not Yes No
should No* Yes
filter Yes No

如上面的表格所示,should 只有在特殊的情况下才会影响 hits。在正常的情况下它不会影响搜索文档的个数。那么在哪些情况下会影响搜索的结果呢?这种情况就是针对只有 should 的搜索情况,也就是如果你在 bool query 里,不含有 must, must_not 及 filter 的情况下,一个或更多的 should 必须有一个匹配才会有结果,比如:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "should": [
  6. {
  7. "match": {
  8. "city": "北京"
  9. }
  10. },
  11. {
  12. "match": {
  13. "city": "武汉"
  14. }
  15. }
  16. ]
  17. }
  18. }
  19. }

10、位置查询

Elasticsearch最厉害的是位置查询。这在很多关系数据库里并没有。例如:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "address": "北京"
  9. }
  10. }
  11. ]
  12. }
  13. },
  14. "post_filter": {
  15. "geo_distance": {
  16. "distance": "3km",
  17. "location": {
  18. "lat": 39.920086,
  19. "lon": 116.454182
  20. }
  21. }
  22. }
  23. }

上述代码表示,查找地址来有北京,并且在以位置(116.454182, 39.920086)为中心的3公里以内的所有文档。

查询结果:

  1. {
  2. "took" : 4,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 1,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 0.48232412,
  16. "hits" : [
  17. {
  18. "_index" : "twitter",
  19. "_type" : "_doc",
  20. "_id" : "5",
  21. "_score" : 0.48232412,
  22. "_source" : {
  23. "user" : "朝阳区-老王",
  24. "message" : "Happy BirthDay My Friend!",
  25. "uid" : 6,
  26. "age" : 50,
  27. "city" : "北京",
  28. "province" : "北京",
  29. "country" : "中国",
  30. "address" : "中国北京市朝阳区国贸",
  31. "location" : {
  32. "lat" : "39.918256",
  33. "lon" : "116.467910"
  34. }
  35. }
  36. }
  37. ]
  38. }
  39. }

下面,我们找出5公里以内的所有位置信息,并按照远近大小进行排序:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "address": "北京"
  9. }
  10. }
  11. ]
  12. }
  13. },
  14. "post_filter": {
  15. "geo_distance": {
  16. "distance": "5km",
  17. "location": {
  18. "lat": 39.920086,
  19. "lon": 116.454182
  20. }
  21. }
  22. },
  23. "sort": [
  24. {
  25. "_geo_distance": {
  26. "location": "39.920086,116.454182",
  27. "order": "asc",
  28. "unit": "km"
  29. }
  30. }
  31. ]
  32. }

在这里,使用sort来对搜索结果进行排序,按照升序排列:

  1. {
  2. "took" : 32,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 3,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "twitter",
  19. "_type" : "_doc",
  20. "_id" : "5",
  21. "_score" : null,
  22. "_source" : {
  23. "user" : "朝阳区-老王",
  24. "message" : "Happy BirthDay My Friend!",
  25. "uid" : 6,
  26. "age" : 50,
  27. "city" : "北京",
  28. "province" : "北京",
  29. "country" : "中国",
  30. "address" : "中国北京市朝阳区国贸",
  31. "location" : {
  32. "lat" : "39.918256",
  33. "lon" : "116.467910"
  34. }
  35. },
  36. "sort" : [
  37. 1.1882901656104885
  38. ]
  39. },
  40. {
  41. "_index" : "twitter",
  42. "_type" : "_doc",
  43. "_id" : "2",
  44. "_score" : null,
  45. "_source" : {
  46. "user" : "东城区-老刘",
  47. "message" : "出发,下一站云南!",
  48. "uid" : 3,
  49. "age" : 30,
  50. "city" : "北京",
  51. "province" : "北京",
  52. "country" : "中国",
  53. "address" : "中国北京市东城区台基厂三条3号",
  54. "location" : {
  55. "lat" : "39.904313",
  56. "lon" : "116.412754"
  57. }
  58. },
  59. "sort" : [
  60. 3.9447355972239952
  61. ]
  62. },
  63. {
  64. "_index" : "twitter",
  65. "_type" : "_doc",
  66. "_id" : "3",
  67. "_score" : null,
  68. "_source" : {
  69. "user" : "东城区-李四",
  70. "message" : "happy birthday!",
  71. "uid" : 4,
  72. "age" : 30,
  73. "city" : "北京",
  74. "province" : "北京",
  75. "country" : "中国",
  76. "address" : "中国北京市东城区",
  77. "location" : {
  78. "lat" : "39.893801",
  79. "lon" : "116.408986"
  80. }
  81. },
  82. "sort" : [
  83. 4.837769064666224
  84. ]
  85. }
  86. ]
  87. }
  88. }

可以看到有三个显示结果。在sort里面可以看到距离越来越大。另外,如果_score不是sort的field,那么在使用sort后,所有的结果_score都变为null。所有上述的搜索也可以直接写为:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": {
  6. "match": {
  7. "address": "北京"
  8. }
  9. },
  10. "filter": {
  11. "geo_distance": {
  12. "distance": "5km",
  13. "location": {
  14. "lat": 39.920086,
  15. "lon": 116.454182
  16. }
  17. }
  18. }
  19. }
  20. },
  21. "sort": [
  22. {
  23. "_geo_distance": {
  24. "location": "39.920086,116.454182",
  25. "order": "asc",
  26. "unit": "km"
  27. }
  28. }
  29. ]
  30. }

11、范围查询

如查询年龄介于30到40的文档:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "range": {
  5. "age": {
  6. "gte": 30,
  7. "lte": 40
  8. }
  9. }
  10. }
  11. }

同样的也可以进行排序

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "range": {
  5. "age": {
  6. "gte": 30,
  7. "lte": 40
  8. }
  9. }
  10. },
  11. "sort": [
  12. {
  13. "age": {
  14. "order": "desc"
  15. }
  16. }
  17. ]
  18. }

12、Exists查询

我们可以通过exists来查询一个字段是否存在。比如我们可以增加一个文档:

  1. PUT twitter/_doc/20
  2. {
  3. "user" : "王二",
  4. "message" : "今儿天气不错啊,出去转转去",
  5. "uid" : 20,
  6. "age" : 40,
  7. "province" : "北京",
  8. "country" : "中国",
  9. "address" : "中国北京市海淀区",
  10. "location" : {
  11. "lat" : "39.970718",
  12. "lon" : "116.325747"
  13. }
  14. }

这个文档中,city这个字段不存在。那么以下搜索将不会返回上面这个文档。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "exists": {
  5. "field": "city"
  6. }
  7. }
  8. }

要查询不含city的这个字段的所有文档,可以这样查询:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must_not": {
  6. "exists": {
  7. "field": "city"
  8. }
  9. }
  10. }
  11. }
  12. }

假如创建另外一个索引twitter10:

  1. PUT twitter10/_doc/1
  2. {
  3. "locale": null
  4. }

执行查询:

  1. GET twitter10/_search
  2. {
  3. "query": {
  4. "exists": {
  5. "field": "locale"
  6. }
  7. }
  8. }

并没有查询到结果。如下所示:

  1. {
  2. "took" : 0,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 0,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. }
  18. }

如果想要找到一个missing的字段,可以这样:

  1. GET twitter10/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must_not": [
  6. {
  7. "exists": {
  8. "field": "locale"
  9. }
  10. }
  11. ]
  12. }
  13. }
  14. }

返回结果如下:

  1. {
  2. "took" : 0,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 1,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 0.0,
  16. "hits" : [
  17. {
  18. "_index" : "twitter10",
  19. "_type" : "_doc",
  20. "_id" : "1",
  21. "_score" : 0.0,
  22. "_source" : {
  23. "locale" : null
  24. }
  25. }
  26. ]
  27. }
  28. }

这这是我们想要的结果。

13、匹配短语

可以用如下方法来查找happy birthday。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "message": "happy birthday"
  6. }
  7. }
  8. }

默认情况下,这个匹配是"或",也就是找打文档里含有"happy"或"birthday"的文档。如果在增加一个文档:

  1. PUT twitter/_doc/8
  2. {
  3. "user": "朝阳区-老王",
  4. "message": "Happy",
  5. "uid": 6,
  6. "age": 50,
  7. "city": "北京",
  8. "province": "北京",
  9. "country": "中国",
  10. "address": "中国北京市朝阳区国贸",
  11. "location": {
  12. "lat": "39.918256",
  13. "lon": "116.467910"
  14. }
  15. }

重新进行搜索,可以看到新增加的id为8的文档也在搜索之列。

如果想得到"与"的关系,则可以用如下方法:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "message": {
  6. "query": "happy birthday",
  7. "operator": "and"
  8. }
  9. }
  10. }
  11. }

这样就搜索不到id为8的文档了。因为必须在message中同时匹配"happy"或"birthday"这两个词。当然还可以用如下方法:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match": {
  5. "message": {
  6. "query": "happy birthday",
  7. "minimum_should_match": 2
  8. }
  9. }
  10. }
  11. }

在这里,采用了"minimum_should_match"来表示至少有2个匹配才可以。

在搜索结果中,无论文档是大小写字母,都能匹配到,并且在message中,happy birthday这两个词先后顺序也不是很重要。比如我们把id为5的文档改为:

  1. PUT twitter/_doc/5
  2. {
  3. "user": "朝阳区-老王",
  4. "message": "BirthDay My Friend Happy !",
  5. "uid": 6,
  6. "age": 50,
  7. "city": "北京",
  8. "province": "北京",
  9. "country": "中国",
  10. "address": "中国北京市朝阳区国贸",
  11. "location": {
  12. "lat": "39.918256",
  13. "lon": "116.467910"
  14. }
  15. }

有意识的把birthday和happy顺序调换后,再次使用上面”或“和”与“查询,仍然可以搜索到。显然,match查询时不分先后顺序。

如果要固定顺序时,可以使用match_phrase。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match_phrase": {
  5. "message": "Happy birthday"
  6. }
  7. },
  8. "highlight": {
  9. "fields": {
  10. "message": {}
  11. }
  12. }
  13. }

搜索结果中显示 happy 在birthday前面的文档才能被搜索到。

再次把id为5的文档修改为:

  1. PUT twitter/_doc/5
  2. {
  3. "user": "朝阳区-老王",
  4. "message": "Happy Good BirthDay My Friend!",
  5. "uid": 6,
  6. "age": 50,
  7. "city": "北京",
  8. "province": "北京",
  9. "country": "中国",
  10. "address": "中国北京市朝阳区国贸",
  11. "location": {
  12. "lat": "39.918256",
  13. "lon": "116.467910"
  14. }
  15. }

此时用之前的match_phrase是找不到文明的的。此时可以这样:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "match_phrase": {
  5. "message": {
  6. "query": "Happy birthday",
  7. "slop": 1
  8. }
  9. }
  10. },
  11. "highlight": {
  12. "fields": {
  13. "message": {}
  14. }
  15. }
  16. }

注意:在这里,我们使用了 slop 为1,表面 Happy 和 birthday 之前是可以允许一个 token 的差别。

14、Name queries

可以使用_name为一个filter或query来获取一个名字。比如:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "city": {
  9. "query": "北京",
  10. "_name": "城市"
  11. }
  12. }
  13. },
  14. {
  15. "match": {
  16. "country": {
  17. "query": "中国",
  18. "_name": "国家"
  19. }
  20. }
  21. }
  22. ],
  23. "should": [
  24. {
  25. "match": {
  26. "_id": {
  27. "query": "1",
  28. "_name": "ID"
  29. }
  30. }
  31. }
  32. ]
  33. }
  34. }
  35. }

返回结果:

  1. {
  2. "took" : 2,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 6,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 1.4672297,
  16. "hits" : [
  17. {
  18. "_index" : "twitter",
  19. "_type" : "_doc",
  20. "_id" : "1",
  21. "_score" : 1.4672297,
  22. "_source" : {
  23. "user" : "双榆树-张三",
  24. "message" : "今儿天气不错啊,出去转转去",
  25. "uid" : 2,
  26. "age" : 20,
  27. "city" : "北京",
  28. "province" : "北京",
  29. "country" : "中国",
  30. "address" : "中国北京市海淀区",
  31. "location" : {
  32. "lat" : "39.970718",
  33. "lon" : "116.325747"
  34. }
  35. },
  36. "matched_queries" : [
  37. "国家",
  38. "ID",
  39. "城市"
  40. ]
  41. },
  42. {
  43. "_index" : "twitter",
  44. "_type" : "_doc",
  45. "_id" : "2",
  46. "_score" : 0.46722972,
  47. "_source" : {
  48. "user" : "东城区-老刘",
  49. "message" : "出发,下一站云南!",
  50. "uid" : 3,
  51. "age" : 30,
  52. "city" : "北京",
  53. "province" : "北京",
  54. "country" : "中国",
  55. "address" : "中国北京市东城区台基厂三条3号",
  56. "location" : {
  57. "lat" : "39.904313",
  58. "lon" : "116.412754"
  59. }
  60. },
  61. "matched_queries" : [
  62. "国家",
  63. "城市"
  64. ]
  65. },
  66. {
  67. "_index" : "twitter",
  68. "_type" : "_doc",
  69. "_id" : "3",
  70. "_score" : 0.46722972,
  71. "_source" : {
  72. "user" : "东城区-李四",
  73. "message" : "happy birthday!",
  74. "uid" : 4,
  75. "age" : 30,
  76. "city" : "北京",
  77. "province" : "北京",
  78. "country" : "中国",
  79. "address" : "中国北京市东城区",
  80. "location" : {
  81. "lat" : "39.893801",
  82. "lon" : "116.408986"
  83. }
  84. },
  85. "matched_queries" : [
  86. "国家",
  87. "城市"
  88. ]
  89. },
  90. {
  91. "_index" : "twitter",
  92. "_type" : "_doc",
  93. "_id" : "4",
  94. "_score" : 0.46722972,
  95. "_source" : {
  96. "user" : "朝阳区-老贾",
  97. "message" : "123,gogogo",
  98. "uid" : 5,
  99. "age" : 35,
  100. "city" : "北京",
  101. "province" : "北京",
  102. "country" : "中国",
  103. "address" : "中国北京市朝阳区建国门",
  104. "location" : {
  105. "lat" : "39.718256",
  106. "lon" : "116.367910"
  107. }
  108. },
  109. "matched_queries" : [
  110. "国家",
  111. "城市"
  112. ]
  113. },
  114. {
  115. "_index" : "twitter",
  116. "_type" : "_doc",
  117. "_id" : "8",
  118. "_score" : 0.46722972,
  119. "_source" : {
  120. "user" : "朝阳区-老王",
  121. "message" : "Happy",
  122. "uid" : 6,
  123. "age" : 50,
  124. "city" : "北京",
  125. "province" : "北京",
  126. "country" : "中国",
  127. "address" : "中国北京市朝阳区国贸",
  128. "location" : {
  129. "lat" : "39.918256",
  130. "lon" : "116.467910"
  131. }
  132. },
  133. "matched_queries" : [
  134. "国家",
  135. "城市"
  136. ]
  137. },
  138. {
  139. "_index" : "twitter",
  140. "_type" : "_doc",
  141. "_id" : "5",
  142. "_score" : 0.46722972,
  143. "_source" : {
  144. "user" : "朝阳区-老王",
  145. "message" : "Happy Good BirthDay My Friend!",
  146. "uid" : 6,
  147. "age" : 50,
  148. "city" : "北京",
  149. "province" : "北京",
  150. "country" : "中国",
  151. "address" : "中国北京市朝阳区国贸",
  152. "location" : {
  153. "lat" : "39.918256",
  154. "lon" : "116.467910"
  155. }
  156. },
  157. "matched_queries" : [
  158. "国家",
  159. "城市"
  160. ]
  161. }
  162. ]
  163. }
  164. }

从上面的返回结果可以看出来多了一个叫做 matched_queries 的字段。在它的里面罗列了每个匹配了的查询。第一个返回的查询结果是三个都匹配了的,但是第二个来说就只有两项是匹配的。

15、通配符查询

可以使用wildcard查询一个字符串里含有的字符。

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "wildcard": {
  5. "city.keyword": {
  6. "value": "*海"
  7. }
  8. }
  9. }
  10. }

上述查询在city字段中含有"海"的文档。所以返回结果中显示了city为"上海"的文档。

16、Disjunction max 查询

返回与一个或多个包在一起的查询(称为查询子句或子句)匹配的文档。

如果返回的文档与多个查询子句匹配,则 dis_max 查询为该文档分配来自任何匹配子句的最高相关性得分,并为任何其他匹配子查询分配平局打破增量。

可以使用 dis_max 在以不同  boost 因子映射的字段中搜索术语。比如:

  1. GET twitter/_search
  2. {
  3. "query": {
  4. "dis_max": {
  5. "queries": [
  6. {
  7. "term": {
  8. "city.keyword": "北京"
  9. }
  10. },
  11. {
  12. "match": {
  13. "address": "北京"
  14. }
  15. }
  16. ],
  17. "tie_breaker": 0.7
  18. }
  19. }
  20. }

在上面的 dis_max 查询中,它将返回任何一个在 queries 中所定的查询的文档。每个匹配分分数是按照如下的规则来进行计算的:

  • 如果一个文档匹配其中的一个或多个查询,那么最终的得分将以其中最高的那个得分来进行计算。
  • 在默认的情况下,tie_breaker 的值为0。它可以是 0 到 1.0 之间的数。

如果文档匹配多个子句,则 dis_max 查询将计算该文档的相关性得分,如下所示:

  • 从具有最高分数的匹配子句中获取相关性分数。
  • 将来自其他任何匹配子句的得分乘以 tie_breaker 值。
  • 将最高分数加到相乘的分数上。

如果 tie_breaker 值大于0.0,则所有匹配子句均计数,但得分最高的子句计数最高。

17、SQL查询

对于与很多已经习惯用 RDMS 数据库的工作人员,他们更喜欢使用 SQL 来进行查询。Elasticsearch 也对 SQL 有支持:

  1. GET /_sql?
  2. {
  3. "query": """
  4. SELECT * FROM twitter
  5. WHERE age = 30
  6. """
  7. }

得到的结果是:

  1. {
  2. "columns" : [
  3. {
  4. "name" : "address",
  5. "type" : "text"
  6. },
  7. {
  8. "name" : "age",
  9. "type" : "long"
  10. },
  11. {
  12. "name" : "city",
  13. "type" : "text"
  14. },
  15. {
  16. "name" : "country",
  17. "type" : "text"
  18. },
  19. {
  20. "name" : "location",
  21. "type" : "geo_point"
  22. },
  23. {
  24. "name" : "message",
  25. "type" : "text"
  26. },
  27. {
  28. "name" : "province",
  29. "type" : "text"
  30. },
  31. {
  32. "name" : "uid",
  33. "type" : "long"
  34. },
  35. {
  36. "name" : "user",
  37. "type" : "text"
  38. }
  39. ],
  40. "rows" : [
  41. [
  42. "中国北京市东城区台基厂三条3号",
  43. 30,
  44. "北京",
  45. "中国",
  46. "point (116.41275395639241 39.90431299433112)",
  47. "出发,下一站云南!",
  48. "北京",
  49. 3,
  50. "东城区-老刘"
  51. ],
  52. [
  53. "中国北京市东城区",
  54. 30,
  55. "北京",
  56. "中国",
  57. "point (116.40898595564067 39.8938009981066)",
  58. "happy birthday!",
  59. "北京",
  60. 4,
  61. "东城区-李四"
  62. ]
  63. ]
  64. }

可以通过如下方法得到对于的DSL语句:

  1. GET /_sql/translate
  2. {
  3. "query": """
  4. SELECT * FROM twitter
  5. WHERE age = 30
  6. """
  7. }

结果:

  1. {
  2. "size" : 1000,
  3. "query" : {
  4. "term" : {
  5. "age" : {
  6. "value" : 30,
  7. "boost" : 1.0
  8. }
  9. }
  10. },
  11. "_source" : {
  12. "includes" : [
  13. "address",
  14. "city",
  15. "country",
  16. "message",
  17. "province",
  18. "user"
  19. ],
  20. "excludes" : [ ]
  21. },
  22. "docvalue_fields" : [
  23. {
  24. "field" : "age"
  25. },
  26. {
  27. "field" : "location"
  28. },
  29. {
  30. "field" : "uid"
  31. }
  32. ],
  33. "sort" : [
  34. {
  35. "_doc" : {
  36. "order" : "asc"
  37. }
  38. }
  39. ]
  40. }

18、Multi Search API

使用单个 API 请求执行几次搜索。这个 API 的好处是节省 API 的请求个数,把多个请求放到一个 API 请求中来实现。
为了说明问题的方便,我们可以多加一个叫做 twitter1 的 index。它的内容如下:

  1. POST _bulk
  2. {"index":{"_index":"twitter1","_id":1}}
  3. {"user":"张庆","message":"今儿天气不错啊,出去转转去","uid":2,"age":20,"city":"重庆","province":"重庆","country":"中国","address":"中国重庆地区","location":{"lat":"39.970718","lon":"116.325747"}}

这样在我们的 Elasticsearch 中就有两个索引了。我们可以做如下的 _msearch。

  1. GET twitter/_msearch
  2. {"index":"twitter"}
  3. {"query":{"match_all":{}},"from":0,"size":1}
  4. {"index":"twitter"}
  5. {"query":{"bool":{"filter":{"term":{"city.keyword":"北京"}}}}, "size":1}
  6. {"index":"twitter1"}
  7. {"query":{"match_all":{}}}

通过 _msearch 终点来实现在一个 API 请求中做多个查询,对多个 index 进行同时操作。显示结果为:

  1. {
  2. "took" : 18,
  3. "responses" : [
  4. {
  5. "took" : 0,
  6. "timed_out" : false,
  7. "_shards" : {
  8. "total" : 1,
  9. "successful" : 1,
  10. "skipped" : 0,
  11. "failed" : 0
  12. },
  13. "hits" : {
  14. "total" : {
  15. "value" : 8,
  16. "relation" : "eq"
  17. },
  18. "max_score" : 1.0,
  19. "hits" : [
  20. {
  21. "_index" : "twitter",
  22. "_type" : "_doc",
  23. "_id" : "1",
  24. "_score" : 1.0,
  25. "_source" : {
  26. "user" : "双榆树-张三",
  27. "message" : "今儿天气不错啊,出去转转去",
  28. "uid" : 2,
  29. "age" : 20,
  30. "city" : "北京",
  31. "province" : "北京",
  32. "country" : "中国",
  33. "address" : "中国北京市海淀区",
  34. "location" : {
  35. "lat" : "39.970718",
  36. "lon" : "116.325747"
  37. }
  38. }
  39. }
  40. ]
  41. },
  42. "status" : 200
  43. },
  44. {
  45. "took" : 0,
  46. "timed_out" : false,
  47. "_shards" : {
  48. "total" : 1,
  49. "successful" : 1,
  50. "skipped" : 0,
  51. "failed" : 0
  52. },
  53. "hits" : {
  54. "total" : {
  55. "value" : 6,
  56. "relation" : "eq"
  57. },
  58. "max_score" : 0.0,
  59. "hits" : [
  60. {
  61. "_index" : "twitter",
  62. "_type" : "_doc",
  63. "_id" : "1",
  64. "_score" : 0.0,
  65. "_source" : {
  66. "user" : "双榆树-张三",
  67. "message" : "今儿天气不错啊,出去转转去",
  68. "uid" : 2,
  69. "age" : 20,
  70. "city" : "北京",
  71. "province" : "北京",
  72. "country" : "中国",
  73. "address" : "中国北京市海淀区",
  74. "location" : {
  75. "lat" : "39.970718",
  76. "lon" : "116.325747"
  77. }
  78. }
  79. }
  80. ]
  81. },
  82. "status" : 200
  83. },
  84. {
  85. "took" : 0,
  86. "timed_out" : false,
  87. "_shards" : {
  88. "total" : 1,
  89. "successful" : 1,
  90. "skipped" : 0,
  91. "failed" : 0
  92. },
  93. "hits" : {
  94. "total" : {
  95. "value" : 1,
  96. "relation" : "eq"
  97. },
  98. "max_score" : 1.0,
  99. "hits" : [
  100. {
  101. "_index" : "twitter1",
  102. "_type" : "_doc",
  103. "_id" : "1",
  104. "_score" : 1.0,
  105. "_source" : {
  106. "user" : "张庆",
  107. "message" : "今儿天气不错啊,出去转转去",
  108. "uid" : 2,
  109. "age" : 20,
  110. "city" : "重庆",
  111. "province" : "重庆",
  112. "country" : "中国",
  113. "address" : "中国重庆地区",
  114. "location" : {
  115. "lat" : "39.970718",
  116. "lon" : "116.325747"
  117. }
  118. }
  119. }
  120. ]
  121. },
  122. "status" : 200
  123. }
  124. ]
  125. }

19、多个索引操作

在上面我们引入了另外一个索引 twitter1。在实际的操作中,我们可以通过通配符,或者直接使用多个索引来进行搜索:

  1. GET twitter*/_search

上面的操作是对所有的以 twitter 为开头的索引来进行搜索,显示的结果是在所有的 twitter 及 twitter1 中的文档。

同样也可以写成:

  1. GET /twitter,twitter1/_search

在写上面的查询的时候,在两个索引之间不能加入空格。

20、Profile API

rofile API 是调试工具。 它添加了有关执行的详细信息搜索请求中的每个组件。 它为用户提供有关搜索的每个步骤的洞察力。请求执行并可以帮助确定某些请求为何缓慢。

  1. GET twitter/_search
  2. {
  3. "profile": "true",
  4. "query": {
  5. "match": {
  6. "city": "北京"
  7. }
  8. }
  9. }

在上面,我们加上了 "profile":"true" 后,除了显示搜索的结果之外,还显示 profile 的信息:

  1. "profile" : {
  2. "shards" : [
  3. {
  4. "id" : "[qjdrmbcpS7aSUxbTtR4mTg][twitter][0]",
  5. "searches" : [
  6. {
  7. "query" : [
  8. {
  9. "type" : "BooleanQuery",
  10. "description" : "city:北 city:京",
  11. "time_in_nanos" : 4260236,
  12. "breakdown" : {
  13. "set_min_competitive_score_count" : 0,
  14. "match_count" : 6,
  15. "shallow_advance_count" : 0,
  16. "set_min_competitive_score" : 0,
  17. "next_doc" : 83628,
  18. "match" : 12840,
  19. "next_doc_count" : 7,
  20. "score_count" : 6,
  21. "compute_max_score_count" : 0,
  22. "compute_max_score" : 0,
  23. "advance" : 178844,
  24. "advance_count" : 3,
  25. "score" : 48384,
  26. "build_scorer_count" : 7,
  27. "create_weight" : 1113902,
  28. "shallow_advance" : 0,
  29. "create_weight_count" : 1,
  30. "build_scorer" : 2822608
  31. },
  32. "children" : [
  33. {
  34. "type" : "TermQuery",
  35. "description" : "city:北",
  36. "time_in_nanos" : 293348,
  37. "breakdown" : {
  38. "set_min_competitive_score_count" : 0,
  39. "match_count" : 0,
  40. "shallow_advance_count" : 9,
  41. "set_min_competitive_score" : 0,
  42. "next_doc" : 0,
  43. "match" : 0,
  44. "next_doc_count" : 0,
  45. "score_count" : 6,
  46. "compute_max_score_count" : 9,
  47. "compute_max_score" : 61365,
  48. "advance" : 12242,
  49. "advance_count" : 10,
  50. "score" : 11943,
  51. "build_scorer_count" : 10,
  52. "create_weight" : 52244,
  53. "shallow_advance" : 39245,
  54. "create_weight_count" : 1,
  55. "build_scorer" : 116264
  56. }
  57. },
  58. {
  59. "type" : "TermQuery",
  60. "description" : "city:京",
  61. "time_in_nanos" : 122102,
  62. "breakdown" : {
  63. "set_min_competitive_score_count" : 0,
  64. "match_count" : 0,
  65. "shallow_advance_count" : 9,
  66. "set_min_competitive_score" : 0,
  67. "next_doc" : 0,
  68. "match" : 0,
  69. "next_doc_count" : 0,
  70. "score_count" : 6,
  71. "compute_max_score_count" : 9,
  72. "compute_max_score" : 18830,
  73. "advance" : 17598,
  74. "advance_count" : 10,
  75. "score" : 6348,
  76. "build_scorer_count" : 10,
  77. "create_weight" : 33689,
  78. "shallow_advance" : 9560,
  79. "create_weight_count" : 1,
  80. "build_scorer" : 36032
  81. }
  82. }
  83. ]
  84. }
  85. ],
  86. "rewrite_time" : 34800,
  87. "collector" : [
  88. {
  89. "name" : "CancellableCollector",
  90. "reason" : "search_cancelled",
  91. "time_in_nanos" : 596185,
  92. "children" : [
  93. {
  94. "name" : "SimpleTopScoreDocCollector",
  95. "reason" : "search_top_hits",
  96. "time_in_nanos" : 77604
  97. }
  98. ]
  99. }
  100. ]
  101. }
  102. ],
  103. "aggregations" : [ ]
  104. }
  105. ]
  106. }

从上面可以看出,这个搜索是搜索了“北”及“京”,而不是把北京作为一个整体来进行搜索的。我们可以在以后的文档中可以学习使用中文分词器来进行分词搜索。

除了上面的通过命令来进行 profile 以外,我们也可以通过 Kibana 的 UI 对我们的搜索进行 profile:

https://blog.csdn.net/UbuntuTouch/article/details/99546568

Elasticsearch(2) 数据搜索的更多相关文章

  1. Elasticsearch 数据搜索篇·【入门级干货】

    ES即简单又复杂,你可以快速的实现全文检索,又需要了解复杂的REST API.本篇就通过一些简单的搜索命令,帮助你理解ES的相关应用.虽然不能让你理解ES的原理设计,但是可以帮助你理解ES,探寻更多的 ...

  2. Elasticsearch 数据搜索篇·【入门级干货】===转

    ES即简单又复杂,你可以快速的实现全文检索,又需要了解复杂的REST API.本篇就通过一些简单的搜索命令,帮助你理解ES的相关应用.虽然不能让你理解ES的原理设计,但是可以帮助你理解ES,探寻更多的 ...

  3. ElasticSearch大数据分布式弹性搜索引擎使用

    阅读目录: 背景 安装 查找.下载rpm包 .执行rpm包安装 配置elasticsearch专属账户和组 设置elasticsearch文件所有者 切换到elasticsearch专属账户测试能否成 ...

  4. ElasticSearch大数据分布式弹性搜索引擎使用—从0到1

    阅读目录: 背景 安装 查找.下载rpm包 .执行rpm包安装 配置elasticsearch专属账户和组 设置elasticsearch文件所有者 切换到elasticsearch专属账户测试能否成 ...

  5. elasticsearch的rest搜索--- 查询

    目录: 一.针对这次装B 的解释 二.下载,安装插件elasticsearch-1.7.0   三.索引的mapping 四. 查询 五.对于相关度的大牛的文档 四. 查询 1. 查询的官网的文档   ...

  6. 工作随笔—Elasticsearch大量数据提交优化

    问题:当有大量数据提交到Elasticsearch时,怎么优化处理效率? 回答: 批量提交 当有大量数据提交的时候,建议采用批量提交. 比如在做 ELK 过程中 ,Logstash indexer 提 ...

  7. Elasticsearch的数据导出和导入操作(elasticdump工具),以及删除指定type的数据(delete-by-query插件)

    Elasticseach目前作为查询搜索平台,的确非常实用方便.我们今天在这里要讨论的是如何做数据备份和type删除.我的ES的版本是2.4.1. ES的备份,可不像MySQL的mysqldump这么 ...

  8. elasticsearch实现网站搜索

    使用elasticsearch 实现网站搜索,可以支持商品搜索,筛选项过滤搜索 ,价格排序, 打分 筛选项聚合,还有其他综合排序 后续推出搜索人工干预排序,根据销量,好评率,售卖率 进行全方位的搜索实 ...

  9. Python 和 Elasticsearch 构建简易搜索

    Python 和 Elasticsearch 构建简易搜索 作者:白宁超 2019年5月24日17:22:41 导读:件开发最大的麻烦事之一就是环境配置,操作系统设置,各种库和组件的安装.只有它们都正 ...

随机推荐

  1. B. Destroying Roads

    Destroying Roads 题目链接 题意 n个点,m条边每两个点之间不会有两个相同的边,然后给你两个起s1,s2和终点t1,t2; 求删除最多的边后满足两个s1到t1距离\(<=l1\) ...

  2. anaconda 命令小览

    一 查看conda环境中安装了什么库: conda list 参考文献: 怎么查看anaconda安装了什么库?-Python学习网

  3. Universal adversarial perturbations

    目录 概 主要内容 算法 实验部分 实验1 实验2 实验3 代码 Moosavidezfooli S, Fawzi A, Fawzi O, et al. Universal Adversarial P ...

  4. PostgreSQL相关知识概念

    本文主要介绍PostgreSQL数据库的一些重要知识点, 包括数据库.模式.表空间.用户/角色等概念和关系, 帮助用户理解PostgreSQL数据库的重要概念, 从而能够更好的使用PostgreSQL ...

  5. MongoDB与微服务

    1. 微服务的优势 * 开发速度快 * 变化响应快 * 易维护 * 扩容简单2. 微服务架构设计要素 * 服务解耦(Decouple) * HTTP API - 简单接口(Dumb Pipes) * ...

  6. OpenIddict 登录及详细流程解析

    GitHub上实例都是集成了Identity来实现,我这里去掉了相关东西,实现自定义的登录满足自己的结构要求 服务端配置添加数据库服务以及定时任务服务 builder.Services.AddDbCo ...

  7. Python中去除字符串中的单个或多个空格的方法总结

    python中去除字符串中空格的方法比较多,单个看起来也都比较简单 但是使用起来容易发生混淆 为了加深记忆 将常用的去除字符串中空格的方法汇总如下 方法一:strip()方法 >>> ...

  8. Pytest_钩子方法setup、teardown、setup_class、teardown_class(8)

    pytest提供了以下 4 种钩子方法: 方法 说明 setup 在每一个测试用例执行之前,会执行此方法.一般用于每个用例相同的初始化工作. teardown 在每一个测试用例执行之后,会执行此方法. ...

  9. vue 传入后台的数据多了个=

    解决方法: 在前端值参时用{} 在后台接收时用Map 来自为知笔记(Wiz)

  10. [ flask ] 解耦models(解决models文件太臃肿的问题)

    问题描述 用博客项目来描述,我们在models中定义了用户表(User).文章表(Post).通知表(Notification).等等.随着我们开发的深入,添加的功能越来越多,到后期models文件会 ...