Elastic 搜索应用

edit

搜索应用程序使用户能够构建利用Elasticsearch及其查询DSL的全部功能的搜索驱动应用程序,同时提供简化的用户体验。 基于您的Elasticsearch索引创建搜索应用程序,使用搜索模板构建查询,并轻松在Kibana搜索界面中直接预览您的结果。

您还可以使用搜索应用程序API与您的搜索应用程序进行交互。 搜索应用程序旨在简化使用Elastic平台在各种企业搜索用例中构建统一的搜索体验。

可用性和先决条件

edit

搜索应用程序功能在 Elastic 版本 8.8.0 中引入。

搜索应用程序是一个测试版功能。 测试版功能可能会发生变化,并且不受一般发布(GA)功能支持服务级别协议的保障。 Elastic计划在未来版本中将此功能提升为GA。

此功能适用于所有Elastic Cloud部署。

当满足 Elastic 订阅要求时,此功能也适用于 自托管 部署。 请在 Elastic Stack 订阅 页面的 Elastic Search 部分查看此功能的要求。

您的部署必须包括 Elasticsearch 和 Kibana 服务。

管理搜索应用程序需要manage_search_application集群权限,并且还需要对与搜索应用程序关联的所有索引具有manage 索引权限

概述

edit

Elasticsearch 的 查询 DSL 功能强大且灵活,但它具有陡峭的学习曲线。 复杂的查询对于非专家来说冗长且难以理解。 我们设计了搜索应用程序,使其更易于搜索,同时保留了与 Elasticsearch 索引一起工作的灵活性。

搜索应用程序使用搜索模板来简化构建查询的过程。 模板在创建搜索应用程序时定义,并可以根据您的需求进行定制。 阅读搜索API和模板以获取详细信息。

开始使用

edit

选项 1:在用户界面中开始

edit

您可以直接在 Kibana UI 的 搜索 下创建、构建和管理您的搜索应用程序。 确保您的部署中至少有一个 Elasticsearch 索引可供使用。 搜索应用程序下的索引将一起被搜索,类似于 别名 在多个索引上进行搜索的方式。

要在 Kibana 中创建一个新的搜索应用程序:

  1. 转到 搜索 > 搜索应用程序
  2. 选择 创建
  3. 选择您要用于搜索应用程序的 Elasticsearch 索引。
  4. 为您的搜索应用程序命名。
  5. 选择 创建

您的搜索应用程序现在应该可以在搜索应用程序列表中找到。

创建后,您可以在 搜索 > 搜索应用程序 > 您的搜索应用程序 > 文档浏览器 下探索搜索应用程序中的文档。 从那里,您可以展开匹配的 Elasticsearch 文档以查看其完整内容。

选项 2:通过 API 开始使用

edit

使用 Elasticsearch 的 Put Search Application API 来创建一个搜索应用。

以下示例创建了一个名为 my_search_application 的搜索应用程序,该应用程序在 my_search_index1my_search_index2 索引上进行搜索,并定义了一个简单的搜索模板(参见 默认模板示例)。

PUT /_application/search_application/my_search_application
{
  "indices": [ "my_search_index1", "my_search_index2" ],
  "template": {
    "script": {
      "source": {
        "query": {
          "query_string": {
            "query": "{{query_string}}",
            "default_field": "{{default_field}}"
          }
        }
      },
      "params": {
        "query_string": "*",
        "default_field": "*"
      }
    }
  }
}

搜索模板

edit

搜索模板是搜索应用程序的核心。 为搜索应用程序创建的默认模板非常简单,您需要根据需求对其进行自定义。 搜索API和模板包含了许多示例,帮助您入门,包括默认模板,以及用于文本搜索、语义搜索和混合搜索的模板。

搜索应用程序搜索API和模板

edit

您的搜索应用程序使用搜索模板来执行搜索。 模板通过仅暴露模板参数来减少复杂性,同时利用Elasticsearch查询DSL的全部功能来构建查询。 模板可以在创建或更新搜索应用程序时设置,并且可以进行自定义。 此模板可以随时使用Put Search Application API API调用进行编辑或更新。

简而言之,您可以使用参数创建搜索模板,而不是使用特定的硬编码搜索值。 在搜索时,您传入这些参数的实际值,从而无需重写整个查询结构即可实现定制搜索。 搜索应用程序模板:

  • 简化查询请求
  • 减少请求大小
  • 确保安全性和性能,因为查询是预定义的,不能随意更改

本文档提供了信息和示例模板,帮助您开始使用搜索应用程序以满足其他用例的需求。 这些模板设计为易于修改以满足您的需求。 一旦您使用模板创建了搜索应用程序,您就可以使用此模板搜索您的搜索应用程序。

搜索模板使用Mustache模板语言。 Mustache变量通常用双大括号括起来,如下所示:{{my-var}}

了解更多信息,请阅读关于搜索模板的内容。

默认模板示例

edit

如果搜索应用程序没有存储模板,搜索时将应用一个最小的默认搜索模板。 默认模板实现了一个简单的搜索用例。

要使用默认模板创建搜索应用程序,请发出创建或更新搜索应用程序请求,而不指定模板:

PUT _application/search_application/my_search_application
{
  "indices": ["index1", "index2"]
}

然后,您可以使用获取搜索应用程序 API 调用来查看您新创建的搜索应用程序,其中还将包括为您创建的默认模板:

GET _application/search_application/my_search_application

在这种情况下,响应将是:

{
  "name": "my_search_application",
  "indices": [
    "index1",
    "index2"
  ],
  "updated_at_millis": 1715802354482,
  "template": {
    "script": {
      "source": """{
  "query": {
    "query_string": {
        "query": "{{query_string}}",
        "default_field": "{{default_field}}"
        }
    }
}
""",
      "lang": "mustache",
      "params": {
        "default_field": "*",
        "query_string": "*"
      }
    }
  }
}

默认模板非常简洁:

{
  "template": {
    "script": {
      "source": {
        "query": {
          "query_string": {
            "query": "{{query_string}}",
            "default_field": "{{default_field}}"
          }
        }
      },
      "params": {
        "query_string": "*",
        "default_field": "*"
      }
    }
  }
}

这可能对搜索模板的初步探索有用,但您可能需要更新它。

此模板不支持额外参数,包括 fromsizeboost。 如果您需要使用这些参数,您可以根据需要自定义与您的搜索应用程序关联的模板,以将它们作为参数包含在内。

您可以通过查看模板了解参数及其默认值,但如果您使用各种参数搜索您的搜索应用程序,查看将生成的查询也可能很有价值。

您可以使用渲染搜索应用程序查询来查看此模板将生成的查询,包括使用默认参数。 例如,在没有参数的情况下搜索搜索应用程序:

POST _application/search_application/my_search_application/_render_query

将返回:

{
  "query": {
    "query_string": {
      "query": "*",
      "default_field": "*",
      "fields": []
    }
  }
}

这使用了模板中定义的默认参数。 您也可以在渲染调用中指定一个或多个参数,例如:

POST _application/search_application/my_search_application/_render_query
{
  "params": {
    "query_string": "rock climbing"
  }
}

将返回:

{
  "query": {
    "query_string": {
      "query": "rock climbing",
      "default_field": "*",
      "fields": []
    }
  }
}

在这种情况下,{{query_string}} 参数已被替换为值 rock climbing,而 {{default_field}} 参数未指定,因此使用了默认值 *

当你在没有参数的情况下实际执行搜索时,它将执行渲染调用返回的底层查询。 在这种情况下,没有参数的搜索将返回所有结果,类似于对 /_search 的无参数调用。

POST _application/search_application/my_search_application/_search

使用query_string和/或default_field参数将执行query_string查询。

默认模板在未来的搜索应用程序功能版本中可能会发生变化。

尝试本文档中的其他示例,以实验特定的用例,或者尝试创建您自己的示例!

搜索搜索应用程序

edit
模板搜索
edit

与搜索应用程序交互的最简单方法是使用与其一起创建和存储的搜索模板。 每个搜索应用程序都有一个与之关联的模板,该模板定义了搜索条件、参数和默认值。

您使用Search Application Search API向搜索应用程序发送搜索请求。

使用默认模板,搜索看起来像这样:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "query_string": "kayaking"
  }
}

在这个例子中,我们覆盖了query_string参数的默认值*。 由于我们没有指定default_field,这个参数的值仍然是*

别名搜索
edit

如果您不想为您的搜索应用程序设置搜索模板,将会创建一个与您的搜索应用程序同名的别名。 这在试验您希望在构建搜索应用程序的搜索模板时使用的特定搜索查询时可能会有所帮助。

如果你的搜索应用名称是 my_search_application,你的别名将是 my_search_application。 你可以使用 search API 进行搜索。

跨集群搜索
edit

搜索应用程序目前不支持跨集群搜索,因为无法将远程集群的索引或索引模式添加到索引别名中。

您应该使用搜索应用程序管理API来更新您的应用程序,而不是直接使用Elasticsearch API,例如别名API。 例如,使用PUT Search Application并带有indices参数。 这将自动保持关联的别名更新,并确保索引正确添加到搜索应用程序中。

搜索模板示例

edit

我们已经创建了许多示例来探索特定的使用场景。 将这些示例作为创建您自己的搜索模板的起点。

文本搜索示例
edit

以下模板支持在指定字段和提升权重上进行multi_match搜索:

PUT _application/search_application/my_search_application
{
  "indices": ["index1", "index2"],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
      {
        "query": {
          "multi_match": {
            "query": "{{query_string}}",
            "fields": [{{#text_fields}}"{{name}}^{{boost}}",{{/text_fields}}]
          }
        },
        "explain": "{{explain}}",
        "from": "{{from}}",
        "size": "{{size}}"
      }
      """,
      "params": {
        "query_string": "*",
        "text_fields": [
          {"name": "title", "boost": 10},
          {"name": "description", "boost": 5}
        ],
        "explain": false,
        "from": 0,
        "size": 10
      }
    }
  }
}

使用此模板进行搜索查询可能如下所示:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "size": 5,
    "query_string": "mountain climbing",
    "text_fields": [
          {"name": "title", "boost": 10},
          {"name": "description", "boost": 2},
          {"name": "state", "boost": 1}
     ]
  }
}

可以通过使用新的/不同的字段和提升来覆盖text_fields参数,以实验出最适合您使用场景的配置。 此模板还通过参数支持分页和explain

文本搜索 + ELSER 与 RRF
edit

此示例支持用于结合BM25和ELSER搜索的倒数排名融合(RRF)方法。 倒数排名融合一致地改进了不同搜索算法的组合结果。 它在所有其他排名算法中表现最佳,并且通常超越最佳的单个结果,无需校准。

PUT _application/search_application/my-search-app
{
  "indices": [
    "index1"
  ],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
      {
        "retriever": {
          "rrf": {
            "retrievers": [
              {{#text_fields}}
              {
                "standard": {
                  "query": {
                    "match": {
                      "{{.}}": "{{query_string}}"
                    }
                  }
                }
              },
              {{/text_fields}}
              {{#elser_fields}}
              {
                "standard": {
                  "query": {
                    "sparse_vector": {
                      "field": "ml.inference.{{.}}_expanded.predicted_value",
                      "inference_id": "<elser_inference_id>",
                      "query": "{{query_string}}"
                    }
                  }
                }
              },
              {{/elser_fields}}
            ],
            "rank_window_size": {{rrf.rank_window_size}},
            "rank_constant": {{rrf.rank_constant}}
          }
        }
      }
      """,
      "params": {
        "elser_fields": ["title", "meta_description"],
        "text_fields": ["title", "meta_description"],
        "query_string": "",
        "rrf": {
          "rank_window_size": 100,
          "rank_constant": 60
        }
      }
    }
  }
}

替换为您的 ELSER 部署的模型 ID。

此模板的示例查询将如下例所示:

POST _application/search_application/my-search-app/_search
{
  "params": {
    "query_string": "What is the most popular brand of coffee sold in the United States?",
    "elser_fields": ["title", "meta_description"],
    "text_fields": ["title", "meta_description"],
    "rrf": {
      "rank_window_size": 50,
      "rank_constant": 25
    }
  }
}
文本搜索 + ELSER
edit

Elastic 学习型稀疏编码器(ELSER)通过文本扩展提高了搜索相关性,从而实现了语义搜索。 此实验模板要求为一个或多个字段启用 ELSER。 有关如何使用 ELSER 的更多信息,请参阅 使用 ELSER 进行语义搜索。 在这种情况下,ELSER 在 titledescription 字段上启用。

此示例提供了一个单一模板,您可以用于各种搜索应用场景:文本搜索、ELSER,或以上全部。 如果没有指定参数,它还提供了一个简单的默认 query_string 查询。

PUT _application/search_application/my_search_application
{
  "indices": [
    "index1",
    "index2"
  ],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
      {
        "query": {
          "bool": {
            "should": [
              {{#text}}
              {
                "multi_match": {
                  "query": "{{query_string}}",
                  "fields": [{{#text_fields}}"{{name}}^{{boost}}",{{/text_fields}}],
                  "boost": "{{text_query_boost}}"
                }
              },
              {{/text}}
              {{#elser}}
              {{#elser_fields}}
              {
                "sparse_vector": {
                  "field": "ml.inference.{{.}}_expanded.predicted_value",
                  "inference_id": "<elser_inference_id>",
                  "query": "{{query_string}}"
                }
              },
              {{/elser_fields}}
              { "bool": { "must": [] } },
              {{/elser}}
              {{^text}}
              {{^elser}}
              {
                "query_string": {
                  "query": "{{query_string}}",
                  "default_field": "{{default_field}}",
                  "default_operator": "{{default_operator}}",
                  "boost": "{{text_query_boost}}"
                }
              },
              {{/elser}}
              {{/text}}
              { "bool": { "must": [] } }
              ],
            "minimum_should_match": 1
          }
        },
        "min_score": "{{min_score}}",
        "explain": "{{explain}}",
        "from": "{{from}}",
        "size": "{{size}}"
      }
      """,
      "params": {
        "text": false,
        "elser": false,
        "elser_fields": [
          {"name": "title", "boost": 1},
          {"name": "description", "boost": 1}
        ],
        "text_fields": [
          {"name": "title", "boost": 10},
          {"name": "description", "boost": 5},
          {"name": "state", "boost": 1}
        ],
        "query_string": "*",
        "text_query_boost": 4,
        "default_field": "*",
        "default_operator": "OR",
        "explain": false,
        "from": 0,
        "size": 10,
        "min_score": 0
      }
    }
  }
}

使用此模板的文本搜索查询可能如下所示:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "text": true,
    "size": 5,
    "query_string": "mountain climbing",
    "text_fields": [
          {"name": "title", "boost": 10},
          {"name": "description", "boost": 5},
          {"name": "state", "boost": 1}
     ]
  }
}

使用此模板的ELSER搜索查询将如下例所示:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "elser": true,
    "query_string": "where is the best mountain climbing?",
    "elser_fields": [
      {"name": "title", "boost": 1},
      {"name": "description", "boost": 1}
    ]
  }
}

使用此模板的组合文本搜索和ELSER搜索查询将如下例所示:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "elser": true,
    "text": true,
    "query_string": "where is the best mountain climbing?",
    "elser_fields": [
      {"name": "title", "boost": 1},
      {"name": "description", "boost": 1}
    ],
    "text_query_boost": 4,
    "min_score": 10
  }
}

文本搜索结果和ELSER搜索结果在某些情况下预计会有显著不同的分数,这使得排名变得具有挑战性。 为了找到最适合您数据集的搜索结果组合,我们建议尝试示例模板中提供的提升值:

  • text_query_boost 用于提升整个BM25查询的权重
  • boost 用于提升个别文本搜索字段的权重
  • min_score 参数用于排除置信度显著较低的结果

上述增强应该足以满足许多用例,但在某些情况下,向模板中添加一个重新评分查询或索引增强可能会带来好处。 请记住,使用放置搜索应用程序命令更新您的搜索应用程序以使用新模板。

最后,使用此模板的无参数搜索将回退到返回所有文档的默认搜索:

POST _application/search_application/my_search_application/_search
ELSER 搜索
edit

此示例支持ELSER搜索的简化版本。

PUT _application/search_application/my_search_application
{
  "indices": [
    "index1",
    "index2"
    ],
    "template": {
      "script": {
        "lang": "mustache",
        "source": """
        {
          "query": {
            "bool": {
              "should": [
                {{#elser_fields}}
                {
                  "sparse_vector": {
                      "field": "ml.inference.{{.}}_expanded.predicted_value",
                      "inference_id": "<elser_inference_id>",
                      "query": "{{query_string}}"
                    }
                },
                {{/elser_fields}}
                ]
            }
          },
          "min_score": "{{min_score}}"
        }
        """,
        "params": {
          "query_string": "*",
          "min_score": "10",
          "elser_fields": [
            {
              "name": "title"
            },
            {
              "name": "description"
            }
            ]
        }
      }
    }
}

替换为您的 ELSER 部署的模型 ID。

此模板的示例查询将如下例所示:

POST _application/search_application/my_search_application/_search
  {
    "params": {
      "query_string": "Where is the best place for mountain climbing?"
    }
  }
kNN 搜索
edit

此示例支持 k-最近邻 (kNN) 搜索

支持精确kNN搜索的模板将如下例所示:

PUT _application/search_application/my_search_application
{
  "indices": [
    "index1"
  ],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
        {
          "query": {
            "script_score": {
              "query": {
                "bool": {
                  "filter": {
                    "range": {
                      "{{field}}": {
                        "{{operator}}": {{value}}
                      }
                    }
                  }
                }
              },
              "script": {
                "source": "cosineSimilarity({{#toJson}}query_vector{{/toJson}}, '{{dense_vector_field}}') + 1.0"
              }
            }
          }
        }
        """,
      "params": {
        "field": "price",
        "operator": "gte",
        "value": 1000,
        "dense_vector_field": "product-vector",
        "query_vector": []
      }
    }
  }
}

使用此模板的搜索查询将如下例所示:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "field": "price",
    "operator": "gte",
    "value": 500
  }
}

支持近似kNN搜索的模板将如下例所示:

PUT _application/search_application/my_search_application
{
  "indices": [
    "index1"
  ],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
      {
          "knn": {
            "field": "{{knn_field}}",
            "query_vector": {{#toJson}}query_vector{{/toJson}},
            "k": "{{k}}",
            "num_candidates": {{num_candidates}}
          },
          "fields": {{#toJson}}fields{{/toJson}}
      }
      """,
      "params": {
        "knn_field": "image-vector",
        "query_vector": [],
        "k": 10,
        "num_candidates": 100,
        "fields": ["title", "file-type"]
      }
    }
  }
}

使用此模板的搜索查询将如下例所示:

POST _application/search_application/my_search_application/_search
{
  "params": {
    "knn_field": "image-vector",
        "query_vector": [-5, 9, -12],
        "k": 10,
        "num_candidates": 100,
        "fields": ["title", "file-type"]
  }
}

使用不受信任客户端的搜索应用程序

edit

在为搜索用例构建前端应用程序时,返回搜索结果主要有两种方法:

  1. 客户端(用户的浏览器)向应用程序后端发出API请求,后者又向Elasticsearch发出请求。 Elasticsearch集群不对最终用户开放。
  2. 客户端(用户的浏览器)直接向搜索服务发出API请求 - 在这种情况下,Elasticsearch集群对客户端是可访问的。

本指南描述了采用第二种方法时的最佳实践。 具体来说,我们将解释如何使用搜索应用程序与前端应用程序,这些应用程序直接向搜索应用程序搜索API发出请求。

这种方法有几个优点:

  • 无需在前端应用程序和Elasticsearch之间维护透传查询系统
  • 直接请求Elasticsearch可以加快响应时间
  • 查询配置在一个地方管理:Elasticsearch中的搜索应用程序配置

我们将涵盖:

使用带有角色限制的Elasticsearch API密钥

edit

当前端应用程序可以直接向 Elasticsearch 发起 API 请求时,限制它们可以执行的操作非常重要。 在我们的例子中,前端应用程序应该只能调用搜索应用程序的 搜索 API。 为了确保这一点,我们将创建带有 角色限制 的 Elasticsearch API 密钥。 角色限制用于指定在什么条件下角色应该生效。

以下 Elasticsearch API 密钥可以通过 Search Application Search API 访问 website-product-search 搜索应用程序:

POST /_security/api_key
{
  "name": "my-restricted-api-key",
  "expiration": "7d",
  "role_descriptors": {
    "my-restricted-role-descriptor": {
      "indices": [
        {
          "names": ["website-product-search"], 
          "privileges": ["read"]
        }
      ],
      "restriction":  {
        "workflows": ["search_application_query"] 
      }
    }
  }
}

indices.name 必须是搜索应用程序的名称,而不是底层 Elasticsearch 索引的名称。

restriction.workflows 必须设置为具体值 search_application_query

指定工作流限制至关重要。 如果没有这个限制,Elasticsearch API 密钥可以直接调用 _search 并发出任意的 Elasticsearch 查询。 这在处理不受信任的客户端时是不安全的。

响应将会是这样的:

{
  "id": "v1CCJYkBvb5Pg9T-_JgO",
  "name": "my-restricted-api-key",
  "expiration": 1689156288526,
  "api_key": "ztVI-1Q4RjS8qFDxAVet5w",
  "encoded": "djFDQ0pZa0J2YjVQZzlULV9KZ086enRWSS0xUTRSalM4cUZEeEFWZXQ1dw"
}

编码后的值可以直接用于授权头中。 以下是使用cURL的示例:

curl -XPOST "http://localhost:9200/_application/search_application/website-product-search/_search" \
 -H "Content-Type: application/json" \
 -H "Authorization: ApiKey djFDQ0pZa0J2YjVQZzlULV9KZ086enRWSS0xUTRSalM4cUZEeEFWZXQ1dw" \
 -d '{
  "params": {
    "field_name": "color",
    "field_value": "red",
    "agg_size": 5
  }
}'

如果 expiration 不存在,默认情况下 Elasticsearch API 密钥永不过期。 可以使用 invalidate API key API 使 API 密钥失效。

具有角色限制的Elasticsearch API密钥也可以使用字段和文档级安全功能。 这进一步限制了前端应用程序查询搜索应用程序的方式。

使用搜索应用程序进行参数验证

edit

您的搜索应用程序使用搜索模板来渲染查询。 模板参数被传递给搜索应用程序搜索API。 在前端应用程序或不受信任的客户端使用的API的情况下,我们需要进行严格的参数验证。 搜索应用程序定义了一个JSON模式,描述了搜索应用程序搜索API允许的参数。

以下示例定义了一个具有严格参数验证的搜索应用程序:

PUT _application/search_application/website-product-search
{
  "indices": [
    "website-products"
  ],
  "template": {
    "script": {
      "source": {
        "query": {
          "term": {
            "{{field_name}}": "{{field_value}}"
          }
        },
        "aggs": {
          "color_facet": {
            "terms": {
              "field": "color",
              "size": "{{agg_size}}"
            }
          }
        }
      },
      "params": {
        "field_name": "product_name",
        "field_value": "hello world",
        "agg_size": 5
      }
    },
    "dictionary": {
      "properties": {
        "field_name": {
          "type": "string",
          "enum": ["name", "color", "description"]
        },
        "field_value": {
          "type": "string"
        },
        "agg_size": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10
        }
      },
      "required": [
        "field_name"
      ],
      "additionalProperties": false
    }
  }
}

根据该定义,搜索应用程序搜索API执行以下参数验证:

  • 它只接受field_namefield_valueaggs_size参数
  • field_name仅限于取值为“name”、“color”和“description”
  • agg_size定义了词项聚合的大小,并且它只能取110之间的值

使用CORS

edit

使用这种方法意味着用户的浏览器将直接向Elasticsearch API发出请求。 Elasticsearch支持跨域资源共享(CORS),但此功能默认情况下是禁用的。 因此,浏览器将阻止这些请求。

有两种解决方法:

在 Elasticsearch 上启用 CORS
edit

这是最简单的选项。 通过在您的 elasticsearch.yml 文件中添加以下内容来启用 Elasticsearch 上的 CORS:

http.cors.allow-origin: "*" # Only use unrestricted value for local development
# Use a specific origin value in production, like `http.cors.allow-origin: "https://<my-website-domain.example>"`
http.cors.enabled: true
http.cors.allow-credentials: true
http.cors.allow-methods: OPTIONS, POST
http.cors.allow-headers: X-Requested-With, X-Auth-Token, Content-Type, Content-Length, Authorization, Access-Control-Allow-Headers, Accept

在 Elastic Cloud 上,您可以通过 编辑您的 Elasticsearch 用户设置 来完成此操作。

  1. 从您的部署菜单中,转到编辑页面。
  2. Elasticsearch部分,选择管理用户设置和扩展
  3. 使用上述配置更新用户设置。
  4. 选择保存更改
通过支持CORS的服务器代理请求
edit

如果你无法在Elasticsearch上启用CORS,你可以通过一个支持CORS的服务器代理请求。 这更复杂,但这是一个可行的选择。

了解更多

edit

使用搜索应用程序客户端构建搜索体验

edit

本文档是使用搜索应用程序构建搜索体验的指南,使用了搜索应用程序客户端。 该客户端是一个设计用于在浏览器中使用的JavaScript库。 您将把这个库集成到您的Web应用程序中,以简化对搜索应用程序的查询。

提供了一个沙盒环境,用于测试和试验search-application-client库。如果你不想设置自己的Web应用程序,可以直接跳转到那里尝试使用客户端。

克隆仓库并按照README中的说明开始使用。

目标

edit

本指南假设您想要构建一个具有以下搜索功能的Web应用程序:

  • 带有自定义相关性的搜索栏和结果
  • 对结果展示的控制,例如包含/排除字段以及匹配项的高亮显示
  • UI控件,如分面、过滤器、排序、分页

你可以将搜索应用程序视为“服务器端”,它将更改持久化到Elasticsearch。 你的Web应用程序充当“客户端”,用于查询搜索应用程序。 你将同时对你的搜索应用程序和Web应用程序进行编辑以完成实现。

先决条件

edit

要遵循本指南,您需要:

  • 一个满足运行搜索应用程序的前提条件弹性部署

    • 如果你没有弹性部署,可以在Elastic Cloud上开始免费试用。
  • 一个搜索应用

    • Kibana UI中创建和管理搜索应用程序,或使用API
  • 一个web应用,用于使用Search Application客户端查询你的搜索应用程序。

安装并配置客户端

edit

安装客户端

edit

安装 使用 npm、yarn 或 CDN 安装客户端。

选项1:使用包管理器

要使用npm安装客户端,请运行以下命令:

npm install @elastic/search-application-client

要使用yarn安装客户端,请运行以下命令:

yarn add @elastic/search-application-client

选项 2:使用带有 HTML

或者,您可以使用CDN安装客户端。 将以下

<script src="https://cdn.jsdelivr.net/npm/@elastic/search-application-client@latest"></script>

导入并初始化客户端

edit

安装完成后,您可以将客户端导入到您的Web应用程序中。 您需要以下信息来初始化客户端:

  • 您的搜索应用程序的名称
  • 您的搜索应用程序的URL端点
  • 您的搜索应用程序的API密钥

在 Kibana UI 的 连接 页面找到此信息。

选项 1: 使用 JavaScript 模块
edit

使用以下导入语句:

import SearchApplicationClient from '@elastic/search-application-client';

配置客户端以您的部署详情开始进行搜索请求。 您可以在Kibana UI的连接页面生成一个API密钥。 转到搜索 > 搜索应用 > > 连接。 您将找到以下信息预先填充以初始化客户端:

import Client from '@elastic/search-application-client'

const request = Client(
  'my-search-application', // search application name
  'url-from-connect-page', // url-host
  'api-key-from-connect-page',  // api-key
  {
    // optional configuration
  }
)

配置完成后,您将能够使用客户端API向您的搜索应用程序发出搜索请求,如下所示:

const results = await request()
  .query('star wars')
  .search()
选项 2:使用 CDN
edit

或者,如果您使用的是CDN,可以使用以下语句导入客户端:

<script>
  const Client = window['SearchApplicationClient'];
</script>

配置客户端与您的部署详细信息,以开始进行搜索请求。 您可以在Kibana UI的连接页面生成API密钥。 转到搜索 > 搜索应用程序 > > 连接。 您将找到以下预填充信息以初始化客户端:

<script>
  const request = Client(
    'my-example-app', // search application name
    'url-from-connect-page', // url-host
    'api-key-from-connect-page',  // api-key
    {
    // optional configuration
    }
)
</script>

配置完成后,您将能够使用客户端API向您的搜索应用程序发出搜索请求,如下所示:

<script>
  const results = await request()
    .query('star wars')
    .search()
</script>

使用您的搜索模板

edit

搜索应用程序客户端旨在与您创建的任何 搜索模板一起使用。 您将使用搜索应用程序API来创建和管理您的搜索模板。

在使用搜索应用程序API管理模板时,我们提供了使用Kibana控制台语法的API示例。

这是一个示例模板:

PUT _application/search_application/my-example-app
{
  "indices": ["my-example-app"],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
        {
          "query": {
            "bool": {
              "must": [
              {{#query}}
              {
                "query_string": {
                  "query": "{{query}}",
                  "search_fields": {{#toJson}}search_fields{{/toJson}}
                }
              }
              {{/query}}
            ]
            }
          }
        }
      """,
      "params": {
        "query": "",
        "search_fields": ""
      }
    }
  }
}

这将允许您向模板添加所需的任何模板参数,并在客户端请求中提供这些值。 使用 addParameter 将实际值注入到模板参数中。

例如,像这样传入search_fields的值:

const results = await request()
  .query('star wars') // requires the template to use query parameter
  .addParameter('search_fields', ['title', 'description'])
  .search()

示例模板

edit

我们建议从客户端仓库中提供的样板模板开始。 查看此脚本以了解其使用方法。 dictionary参数用于传递一个JSON模式定义,该定义描述了请求对象的结构和验证规则。 此模式非常重要,因为它限制了在Elasticsearch查询中使用某些功能。 查看模式

本指南中的每个搜索功能都需要包含在此模板中的一个功能。 这些功能需要模板中存在特定的参数:

  • 查询: query
  • 过滤器: _es_filters
  • 分面: _es_filters_es_aggs
  • 排序: _es_sort_fields
  • 分页: fromsize

搜索功能

edit

我们将探索搜索体验所需的所有基本基础。 您将学习如何使用您的搜索应用程序实现它们,并使用客户端查询它们。

有关可用方法及其参数的信息,请参阅客户端仓库

自定义相关性

edit

我们的简单模板使用 query_string 在所有字段中进行搜索,但这可能不适合您的使用场景。 您可以更新模板以提供更好的相关性召回。

在下面的示例中,我们使用了一个multi-match查询来针对我们的模板,使用了best_fieldsphrase_prefix查询来针对不同的搜索字段。

PUT _application/search_application/my-example-app
{
  "indices": ["example-index"],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
        {
          "query": {
            "bool": {
              "must": [
              {{#query}}
              {
                "multi_match" : {
                  "query":    "{{query}}",
                  "fields": [ "title^4", "plot", "actors", "directors" ]
                }
              },
              {
                "multi_match" : {
                  "query":    "{{query}}",
                  "type": "phrase_prefix",
                  "fields": [ "title^4", "plot"]
                }
              },
              {{/query}}
            ],
            "filter": {{#toJson}}_es_filters{{/toJson}}
            }
          },
          "aggs": {{#toJson}}_es_aggs{{/toJson}},
          "from": {{from}},
          "size": {{size}},
          "sort": {{#toJson}}_es_sort_fields{{/toJson}}
        }
      """,
      "params": {
        "query": "",
        "_es_filters": {},
        "_es_aggs": {},
        "_es_sort_fields": {},
        "size": 10,
        "from": 0
      },
      "dictionary": {
          //  add dictionary restricting
          // _es_filters, _es_sort_fields & _es_aggs params
          // Use example provided in repo: https://github.com/elastic/search-application-client/blob/main/bin/request_schema.json
      }
    }
  }
}

参考 不同类型查询的示例,包括文本搜索、kNN 搜索、ELSER 搜索、使用 RRF 的混合搜索等。

用例:我想动态调整搜索字段

如果您需要在查询请求时调整search_fields,您可以向模板添加一个新参数(例如:search_fields),并使用addParameter方法将字段提供给模板。

用例:我希望根据与用户的特定接近度来提升结果

您可以添加额外的模板参数来发送用户的地理位置坐标。 然后使用function_score来提升与用户一定geo_distance匹配的文档。

结果字段

edit

默认情况下,所有字段都会在 _source 字段中返回。 要限制返回的字段,请在模板中指定字段。

PUT _application/search_application/my-example-app
{
  "indices": ["example-index"],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
        {
          "query": {
            "bool": {
              "must": [
              {{#query}}
                // ...
              {{/query}}
            ],
            "filter": {{#toJson}}_es_filters{{/toJson}}
            }
          },
          "_source": {
            "includes": ["title", "plot"]
          },
          "aggs": {{#toJson}}_es_aggs{{/toJson}},
          "from": {{from}},
          "size": {{size}},
          "sort": {{#toJson}}_es_sort_fields{{/toJson}}
        }
      """,
      "params": {
        "query": "",
        "_es_filters": {},
        "_es_aggs": {},
        "_es_sort_fields": {},
        "size": 10,
        "from": 0
      },
      "dictionary": {
          //  add dictionary restricting _es_filters and _es_aggs params
          // Use the dictionary example provided in repo: https://github.com/elastic/search-application-client/blob/main/bin/request_schema.json
      }
    }
  }
}

用例:我想动态调整结果字段

如果您需要在查询请求时调整返回的字段,可以向模板添加一个新参数(例如:result_fields),并使用 addParameter 方法将字段提供给模板。

高亮和片段

edit

在模板中添加高亮支持非常简单。 通过高亮 API,您可以指定要为匹配项高亮的字段。

在下面的示例中,我们指定 titleplot 作为高亮显示的字段。 title 通常具有较短的值长度,而 plot 的长度是可变的,并且往往更长。

我们指定标题的fragment_size0,以便在有高亮显示时返回所有文本。 我们指定图表的fragment_size200,其中每个高亮片段最多为200个字符长。

PUT _application/search_application/my-example-app
{
  "indices": ["example-index"],
  "template": {
    "script": {
      "lang": "mustache",
      "source": """
        {
          "query": {
            "bool": {
              "must": [
              {{#query}}
                // ...
              {{/query}}
            ],
            "filter": {{#toJson}}_es_filters{{/toJson}}
            }
          },
          "_source": {
            "includes": ["title", "plot"]
            },
            "highlight": {
              "fields": {
                "title": { "fragment_size": 0 },
                "plot": { "fragment_size": 200 }
                }
                },
                "aggs": {{#toJson}}_es_aggs{{/toJson}},
                "from": {{from}},
                "size": {{size}},
                "sort": {{#toJson}}_es_sort_fields{{/toJson}}
                }
                """,
                "params": {
                  "query": "",
                  "_es_filters": {},
                  "_es_aggs": {},
                  "_es_sort_fields": {},
                  "size": 10,
                  "from": 0
                  },
                  "dictionary": {
                    //  add dictionary restricting _es_filters and _es_aggs params
                    // Use the dictionary example provided in repo: https://github.com/elastic/search-application-client/blob/main/bin/request_schema.json
                    }
                }
           }
}

如果找到匹配项,这将返回带有高亮字段的结果。 例如:

{
  "hits": [
    {
      "_index": "movies",
      "_type": "_doc",
      "_id": "1",
      "_score": 0.2876821,
      "_source": {
        "title": "The Great Gatsby",
        "plot": "The Great Gatsby is a novel by F. Scott Fitzgerald that follows the story of Jay Gatsby, a wealthy and mysterious man, as he tries to win back the love of his life, Daisy Buchanan."
      },
      "highlight": {
        "title": ["The Great <em>Gatsby</em>"],
        "plot": [
          "The Great <em>Gatsby</em> is a novel by F. Scott Fitzgerald that follows the story of <em>Jay</em> <em>Gatsby</em>, a wealthy and mysterious man, as he tries to win back the love of his life, Daisy Buchanan."
        ]
      }
    }
  ]
}
高亮助手
edit

在显示前端的字段时,您需要首先确定该字段是否有高亮显示。 为了简化这一过程,我们提供了一个辅助工具。

import Client, { Highlight } from '@elastic/search-application-client'

// example React component
const ResultsList = ({ hits } ) => {
  return hits.map((hit) => (
    <div className="result">
       <div className="title">{Highlight(hit, "title")}</div>
       <div className="description">{Highlight(hit, "plot")}</div>
    </div>
  ))
}

分页

edit

要使用分页,设置页码和每页大小。 默认情况下,每页大小为10。 sizefrom 参数允许你控制返回的页码和命中数量。

我们可以使用客户端的 setSizesetFrom 方法来实现这一点。

// page 1
const results = await request()
 .setSize(20)
 .setFrom(0)
 .search()

// page 2
const results = await request()
 .setSize(20)
 .setFrom(20)
 .search()

排序

edit

要使用排序,请指定字段名称和排序顺序,或者使用 pass _score 按相关性排序。 需要在搜索模板中包含 _es_sort_fields_fields 参数。 请参考我们的 示例模板 以查看此用法的示例。

默认情况下,结果将按分数排序。 如果您需要按分数以外的字段排序,请使用带有对象数组的 setSort 方法。

const results = await request()
 .setSort([{ year: 'asc' }, '_score'])
 .search()

过滤

edit

搜索应用程序客户端也支持过滤器和分面。 要使用这些功能,您需要添加两个参数:

  • _es_filters
  • _es_aggs

请参考我们的示例模板以查看这些用法。

基本过滤
edit

使用配置了过滤器的模板,使用 setFilter 方法将过滤器添加到您的查询中。

样板模板架构仅支持 term、range、match、nested、geo_bounding_box 和 geo_distance 过滤器。 如果您需要使用特定子句,可以更新模板架构。

下面是使用setFilter的示例。

// return only "star wars" movies that are rated PG
const results = await request()
  .query('star wars')
  .setFilter({
    term: {
      'rated.enum': 'PG',
    },
  })
  .search()

方面

edit

客户端支持在结果中配置分面的功能。 在客户端初始化调用中指定分面。 例如,假设我们想要为演员、导演和IMDB评分添加分面。

const request = Client(
  'my-example-app', // search application name
  'https://d1bd36862ce54c7b903e2aacd4cd7f0a.us-east4.gcp.elastic-cloud.com:443', // api-host
  'api-key-from-connect-page', // api-key
  {
    facets: {
      actors: {
        type: 'terms',
        field: 'actors.keyword',
        disjunctive: true,
      },
      directors: {
        type: 'terms',
        field: 'director.keyword',
        size: 20,
        disjunctive: true,
      },
      imdbrating: {
        type: 'stats',
        field: 'imdbrating',
      },
    },
  }
)

在Elasticsearch中,keyword类型用于需要以其精确、未修改的形式进行搜索的字段。 这意味着这些查询是区分大小写的。 我们使用这种类型进行分面,因为分面需要根据精确值或术语对数据进行聚合和过滤。

使用 addFacetFilter 方法将分面添加到您的查询中。

在下面的示例中,我们只想返回电影:

  • 由哈里森·福特主演
  • 由乔治·卢卡斯雷德利·斯科特执导
  • IMBD评分高于7.5
const results = await request()
  .addFacetFilter('actors', 'Harrison Ford')
  .addFacetFilter('directors', 'George Lucas')
  .addFacetFilter('directors', 'Ridley Scott')
  .addFacetFilter('imdbrating', {
    gte: 7.5,
  })
  .search()

您可以访问结果中的方面:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0,
    "hits": [
      {
        "_index": "imdb_movies",
        "_id": "tt0076759",
        "_score": 0,
        "_source": {
          "title": "Star Wars: Episode IV - A New Hope",
          "actors": [
            "Mark Hamill",
            "Harrison Ford",
            "Carrie Fisher",
            "Peter Cushing"
          ],
          "plot": "Luke Skywalker joins forces with a Jedi Knight, a cocky pilot, a wookiee and two droids to save the universe from the Empire's world-destroying battle-station, while also attempting to rescue Princess Leia from the evil Darth Vader.",
          "poster": "https://s3-eu-west-1.amazonaws.com/imdbimages/images/MV5BMTU4NTczODkwM15BMl5BanBnXkFtZTcwMzEyMTIyMw@@._V1_SX300.jpg"
        }
      },
      {
        "_index": "imdb_movies",
        "_id": "tt0083658",
        "_score": 0,
        "_source": {
          "title": "Blade Runner",
          "actors": [
            "Harrison Ford",
            "Rutger Hauer",
            "Sean Young",
            "Edward James Olmos"
          ],
          "plot": "Deckard, a blade runner, has to track down and terminate 4 replicants who hijacked a ship in space and have returned to Earth seeking their maker.",
          "poster": "https://s3-eu-west-1.amazonaws.com/imdbimages/images/MV5BMTA4MDQxNTk2NDheQTJeQWpwZ15BbWU3MDE2NjIyODk@._V1_SX300.jpg"
        }
      }
    ]
  },
  "aggregations": {},
  "facets": [
    {
      "name": "imdbrating_facet",
      "stats": {
        "min": 8.300000190734863,
        "max": 8.800000190734863,
        "avg": 8.550000190734863,
        "sum": 17.100000381469727,
        "count": 2
      }
    },
    {
      "name": "actors_facet",
      "entries": [
        {
          "value": "Harrison Ford",
          "count": 2
        },
        {
          "value": "Carrie Fisher",
          "count": 1
        },
        {
          "value": "Edward James Olmos",
          "count": 1
        },
        {
          "value": "Mark Hamill",
          "count": 1
        },
        {
          "value": "Peter Cushing",
          "count": 1
        },
        {
          "value": "Rutger Hauer",
          "count": 1
        },
        {
          "value": "Sean Young",
          "count": 1
        }
      ]
    },
    {
      "name": "directors_facet",
      "entries": [
        {
          "value": "Steven Spielberg",
          "count": 3
        },
        {
          "value": "Andrew Davis",
          "count": 1
        },
        {
          "value": "George Lucas",
          "count": 1
        },
        {
          "value": "Irvin Kershner",
          "count": 1
        },
        {
          "value": "Richard Marquand",
          "count": 1
        },
        {
          "value": "Ridley Scott",
          "count": 1
        }
      ]
    }
  ]
}