作者:来自 Elastic Jhon Guzmán

了解如何使用 Elasticsearch 查询规则界面,在不影响自然排名的情况下,通过可自定义的规则集在 Kibana 中添加或排除搜索查询中的文档。
刚接触 Elasticsearch 吗?加入我们的 Elasticsearch 入门网络研讨会。你也可以现在开始免费的云试用,或在你的机器上体验 Elastic。
搜索引擎的工作是返回相关结果。然而,业务需求往往超越这一点 ------ 例如突出促销、优先展示季节性产品或展示赞助商品 ------ 而开发者并不总能在搜索查询中实现这些功能。
此外,这些用例通常对时间敏感,而通过典型的开发流程(创建代码分支并等待新版本发布)会非常耗时。
那么,如果我们能通过一个 API 调用,甚至只需在 Kibana 中点击几下就能完成整个过程,会怎样呢?
查询规则界面
Elasticsearch 8.10 引入了 Query Rules 和 Rule Retriever。它们是为在不影响自然结果排名的情况下,根据规则将固定结果注入查询而设计的工具。它们只是以声明性和简单的方式在结果之上添加业务逻辑。
Query Rules 的一些常见用例包括:
-
突出推广或促销列表:将打折或赞助的商品显示在顶部。
-
按上下文或地理位置排除:在当地法规不允许显示时隐藏某些项目。
-
优先关键结果:确保热门或固定搜索始终位于顶部,无论自然排名如何。
要访问界面并与这些工具交互,你需要点击 Kibana 侧边菜单,然后在 Relevance 下进入 Query Rules。

当查询规则菜单弹出后,点击 Create your first ruleset:

接下来,你需要为你的规则集命名。

定义每条规则的表单有三个关键组成部分:
- 条件:必须满足的条件才能应用该规则。例如,"当 query_string 字段包含值 Christmas"或"当 country 字段为 CO"。
- 操作:当条件满足时你希望发生的事情。可以是固定(将文档固定在顶部结果)或排除(隐藏文档)。
- 元数据:这些是在查询运行时附带的字段。它们可以包括用户信息(如位置或语言)以及搜索数据(query_string)。这些值由条件使用,以决定是否应用某条规则。
示例:热门商品
假设我们有一个包含不同商品的电商网站。查看指标时,我们注意到在游戏机类别中销量最高的商品之一是 "DualShock 4 Wireless Controller",尤其是在用户搜索关键词 "PS4" 或 "PlayStation 4" 时。因此,我们决定当用户搜索这些关键词时,将该商品放在结果的顶部。
首先,让我们使用 Bulk API 请求为每个商品建立索引:
bash
`
1. POST _bulk
2. { "index": { "_index": "products", "_id": "1" } }
3. { "id": "1", "name": "PlayStation 4 Slim 1TB", "category": "console", "brand": "Sony", "price": 1200 }
4. { "index": { "_index": "products", "_id": "2" } }
5. { "id": "2", "name": "DualShock 4 Wireless Controller", "category": "accessory", "brand": "Sony", "price": 250 }
6. { "index": { "_index": "products", "_id": "3" } }
7. { "id": "3", "name": "PlayStation 4 Camera", "category": "accessory", "brand": "Sony", "price": 200 }
8. { "index": { "_index": "products", "_id": "4" } }
9. { "id": "4", "name": "PlayStation 4 VR Headset", "category": "accessory", "brand": "Sony", "price": 900 }
10. { "index": { "_index": "products", "_id": "5" } }
11. { "id": "5", "name": "Charging Station for DualShock 4", "category": "accessory", "brand": "Sony", "price": 80 }
`AI写代码
如果我们不干预查询,该商品通常会出现在第四位。以下是查询:
bash
`
1. GET products/_search
2. {
3. "query": {
4. "match": {
5. "name": "PlayStation 4"
6. }
7. }
8. }
`AI写代码
以下是查询结果:
bash
`
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": 0.6973252,
16. "hits": [
17. {
18. "_index": "products",
19. "_id": "3",
20. "_score": 0.6973252,
21. "_source": {
22. "id": "3",
23. "name": "PlayStation 4 Camera",
24. "category": "accessory",
25. "brand": "Sony",
26. "price": 200
27. }
28. },
29. {
30. "_index": "products",
31. "_id": "1",
32. "_score": 0.6260078,
33. "_source": {
34. "id": "1",
35. "name": "PlayStation 4 Slim 1TB",
36. "category": "console",
37. "brand": "Sony",
38. "price": 1200
39. }
40. },
41. {
42. "_index": "products",
43. "_id": "4",
44. "_score": 0.6260078,
45. "_source": {
46. "id": "4",
47. "name": "PlayStation 4 VR Headset",
48. "category": "accessory",
49. "brand": "Sony",
50. "price": 900
51. }
52. },
53. {
54. "_index": "products",
55. "_id": "2",
56. "_score": 0.08701137,
57. "_source": {
58. "id": "2",
59. "name": "DualShock 4 Wireless Controller",
60. "category": "accessory",
61. "brand": "Sony",
62. "price": 250
63. }
64. },
65. {
66. "_index": "products",
67. "_id": "5",
68. "_score": 0.07893815,
69. "_source": {
70. "id": "5",
71. "name": "Charging Station for DualShock 4",
72. "category": "accessory",
73. "brand": "Sony",
74. "price": 80
75. }
76. }
77. ]
78. }
79. }
`AI写代码收起代码块
让我们创建一个查询规则来更改这个结果。首先,将它添加到规则集,如下所示:

或者等效的 API 请求:
bash
`
1. PUT _query_rules/my-rules
2. {
3. "rules": [
4. {
5. "rule_id": "rule-1232",
6. "type": "pinned",
7. "criteria": [
8. {
9. "type": "exact",
10. "metadata": "query_string",
11. "values": [
12. "PS4",
13. "Playstation 4"
14. ]
15. }
16. ],
17. "actions": {
18. "docs": [
19. {
20. "_index": "products",
21. "_id": "2"
22. }
23. ]
24. }
25. }
26. ]
27. }
`AI写代码
要在查询中使用该 ruleset,我们必须使用查询规则类型。此类查询由两个主要部分组成:
bash
`
1. GET /products/_search
2. {
3. "retriever": {
4. "rule": {
5. "retriever": {
6. "standard": {
7. "query": {
8. "match": { "name": "PlayStation 4" }
9. }
10. }
11. },
12. "match_criteria": {
13. "query_string": "PlayStation 4"
14. },
15. "ruleset_ids": ["my-rules"]
16. }
17. }
18. }
`AI写代码
- match_criteria:这些是用于与用户查询进行比较的元数据。在此示例中,当 query_string 字段的值为 "PlayStation 4" 时,规则集将被激活。
- query:将用于搜索并获取自然结果的实际查询。
这样,你首先运行自然查询,然后 Elasticsearch 会应用规则集中的规则:
bash
`
1. {
2. "took": 17,
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": 1.7014122e+38,
16. "hits": [
17. {
18. "_index": "products",
19. "_id": "2",
20. "_score": 1.7014122e+38,
21. "_source": {
22. "id": "2",
23. "name": "DualShock 4 Wireless Controller",
24. "category": "accessory",
25. "brand": "Sony",
26. "price": 250
27. }
28. },
29. {
30. "_index": "products",
31. "_id": "3",
32. "_score": 0.6973252,
33. "_source": {
34. "id": "3",
35. "name": "PlayStation 4 Camera",
36. "category": "accessory",
37. "brand": "Sony",
38. "price": 200
39. }
40. },
41. {
42. "_index": "products",
43. "_id": "1",
44. "_score": 0.6260078,
45. "_source": {
46. "id": "1",
47. "name": "PlayStation 4 Slim 1TB",
48. "category": "console",
49. "brand": "Sony",
50. "price": 1200
51. }
52. },
53. {
54. "_index": "products",
55. "_id": "4",
56. "_score": 0.6260078,
57. "_source": {
58. "id": "4",
59. "name": "PlayStation 4 VR Headset",
60. "category": "accessory",
61. "brand": "Sony",
62. "price": 900
63. }
64. },
65. {
66. "_index": "products",
67. "_id": "5",
68. "_score": 0.07893815,
69. "_source": {
70. "id": "5",
71. "name": "Charging Station for DualShock 4",
72. "category": "accessory",
73. "brand": "Sony",
74. "price": 80
75. }
76. }
77. ]
78. }
79. }
`AI写代码收起代码块
示例:基于用户的元数据
Query Rules 的另一个有趣应用是根据用户或网页的上下文信息使用元数据来显示特定文档。
例如,假设我们想根据用户的忠诚度等级(用数值表示)来突出显示商品或定制销售。
我们可以通过将这些元数据直接注入查询来实现,当该数值满足特定条件时,规则就会被激活。
首先,我们索引一条只有高忠诚度用户才能看到的文档:
json
`
1. POST _bulk
2. { "index": { "_index": "products", "_id": "6" } }
3. { "id": "6", "name": "PlayStation Plus Deluxe Card - 12 months", "category": "membership", "brand": "Sony", "price": 300 }
`AI写代码
现在,让我们在同一个 ruleset 中创建一个新规则,当 loyalty_level 等于或高于 80 时,该商品将显示在结果顶部。

保存该规则和 ruleset。
下面是等效的 REST 请求:
bash
`
1. PUT _query_rules/my-rules
2. {
3. "rules": [
4. {
5. "rule_id": "pin-premiun-user",
6. "type": "pinned",
7. "criteria": [
8. {
9. "type": "gte",
10. "metadata": "loyalty_level",
11. "values": [
12. 80
13. ]
14. }
15. ],
16. "actions": {
17. "docs": [
18. {
19. "_index": "products",
20. "_id": "6"
21. }
22. ]
23. }
24. }
25. ]
26. }
`AI写代码
现在,在运行查询时,我们需要在 metadata 中包含新的参数 loyalty_level。如果规则中的条件满足,新的文档将出现在结果顶部。
例如,当发送 loyalty_level 为 80 的查询时:
bash
`
1. POST /products/_search
2. {
3. "retriever": {
4. "rule": {
5. "retriever": {
6. "standard": {
7. "query": {
8. "match": {
9. "name": "PlayStation"
10. }
11. }
12. }
13. },
14. "match_criteria": {
15. "query_string": "PlayStation",
16. "loyalty_level": 80
17. },
18. "ruleset_ids": ["my-rules"]
19. }
20. }
21. }
`AI写代码
我们会看到 loyalty 文档出现在结果顶部:
bash
`
1. {
2. "took": 31,
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": 4,
13. "relation": "eq"
14. },
15. "max_score": 1.7014122e+38,
16. "hits": [
17. {
18. "_index": "products",
19. "_id": "6",
20. "_score": 1.7014122e+38,
21. "_source": {
22. "id": "6",
23. "name": "PlayStation Plus Deluxe Card - 12 months",
24. "category": "membership",
25. "brand": "Sony",
26. "price": 300
27. }
28. },
29. {
30. "_index": "products",
31. "_id": "3",
32. "_score": 0.5054567,
33. "_source": {
34. "id": "3",
35. "name": "PlayStation 4 Camera",
36. "category": "accessory",
37. "brand": "Sony",
38. "price": 200
39. }
40. },
41. {
42. "_index": "products",
43. "_id": "1",
44. "_score": 0.45618832,
45. "_source": {
46. "id": "1",
47. "name": "PlayStation 4 Slim 1TB",
48. "category": "console",
49. "brand": "Sony",
50. "price": 1200
51. }
52. },
53. {
54. "_index": "products",
55. "_id": "4",
56. "_score": 0.45618832,
57. "_source": {
58. "id": "4",
59. "name": "PlayStation 4 VR Headset",
60. "category": "accessory",
61. "brand": "Sony",
62. "price": 900
63. }
64. }
65. ]
66. }
67. }
`AI写代码收起代码块
在下面的情况下,因为 loyalty level 是 70,该规则不满足,项目不会出现在结果顶部:
bash
`
1. POST /products/_search
2. {
3. "retriever": {
4. "rule": {
5. "retriever": {
6. "standard": {
7. "query": {
8. "match": {
9. "name": "PlayStation"
10. }
11. }
12. }
13. },
14. "match_criteria": {
15. "query_string": "PlayStation",
16. "loyalty_level": 70
17. },
18. "ruleset_ids": ["my-rules"]
19. }
20. }
21. }
`AI写代码
以下是结果:
bash
`
1. {
2. "took": 7,
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": 4,
13. "relation": "eq"
14. },
15. "max_score": 0.5054567,
16. "hits": [
17. {
18. "_index": "products",
19. "_id": "3",
20. "_score": 0.5054567,
21. "_source": {
22. "id": "3",
23. "name": "PlayStation 4 Camera",
24. "category": "accessory",
25. "brand": "Sony",
26. "price": 200
27. }
28. },
29. {
30. "_index": "products",
31. "_id": "1",
32. "_score": 0.45618832,
33. "_source": {
34. "id": "1",
35. "name": "PlayStation 4 Slim 1TB",
36. "category": "console",
37. "brand": "Sony",
38. "price": 1200
39. }
40. },
41. {
42. "_index": "products",
43. "_id": "4",
44. "_score": 0.45618832,
45. "_source": {
46. "id": "4",
47. "name": "PlayStation 4 VR Headset",
48. "category": "accessory",
49. "brand": "Sony",
50. "price": 900
51. }
52. },
53. {
54. "_index": "products",
55. "_id": "6",
56. "_score": 0.3817649,
57. "_source": {
58. "id": "6",
59. "name": "PlayStation Plus Deluxe Card - 12 months",
60. "category": "membership",
61. "brand": "Sony",
62. "price": 300
63. }
64. }
65. ]
66. }
67. }
`AI写代码收起代码块
示例:即时排除
假设我们的 **DualShock 4 Wireless Controller(ID 2)**暂时缺货,无法销售。所以,业务团队决定暂时将其从搜索结果中移除,而不是手动删除文档或等待数据流程生效。
我们将使用与之前处理热门商品类似的流程,但这次选择 Exclude,而不是 Pinned。这个规则类似于黑名单。将条件改为 Always,以确保每次查询运行时都生效。
规则应如下所示:

保存规则和规则集以应用更改。等效的 REST 请求如下:
bash
`
1. PUT _query_rules/my-rules
2. {
3. "rules": [
4. {
5. "rule_id": "rule-6358",
6. "type": "pinned",
7. "criteria": [
8. {
9. "type": "always"
10. }
11. ],
12. "actions": {
13. "docs": [
14. {
15. "_index": "products",
16. "_id": "2"
17. }
18. ]
19. }
20. }
21. ]
22. }
`AI写代码
现在,当我们再次运行查询时,你会看到该商品不再出现在结果中,即使之前的规则是将其置顶。这是因为排除规则优先于置顶结果。
bash
`
1. {
2. "took": 6,
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": 4,
13. "relation": "eq"
14. },
15. "max_score": 2.205655,
16. "hits": [
17. {
18. "_index": "products",
19. "_id": "3",
20. "_score": 2.205655,
21. "_source": {
22. "id": "3",
23. "name": "PlayStation 4 Camera",
24. "category": "accessory",
25. "brand": "Sony",
26. "price": 200
27. }
28. },
29. {
30. "_index": "products",
31. "_id": "1",
32. "_score": 1.9738505,
33. "_source": {
34. "id": "1",
35. "name": "PlayStation 4 Slim 1TB",
36. "category": "console",
37. "brand": "Sony",
38. "price": 1200
39. }
40. },
41. {
42. "_index": "products",
43. "_id": "4",
44. "_score": 1.9738505,
45. "_source": {
46. "id": "4",
47. "name": "PlayStation 4 VR Headset",
48. "category": "accessory",
49. "brand": "Sony",
50. "price": 900
51. }
52. },
53. {
54. "_index": "products",
55. "_id": "5",
56. "_score": 0.69247496,
57. "_source": {
58. "id": "5",
59. "name": "Charging Station for DualShock 4",
60. "category": "accessory",
61. "brand": "Sony",
62. "price": 80
63. }
64. }
65. ]
66. }
67. }
`AI写代码收起代码块
总结
Query Rules 让你可以非常轻松地在不修改任何代码的情况下调整相关性。新的 Kibana UI 允许你在几秒钟内完成这些更改,为你和你的业务团队提供了对搜索结果的更多控制。
除了电商场景外,Query Rules 还能支持许多其他应用:在支持门户中突出显示故障排查指南,在知识库中显示关键内部文档,在新闻网站上推广突发新闻,或过滤过期的职位或内容列表。它们甚至可以执行合规规则,例如根据用户角色或地区隐藏受限内容。