在 Azure AI 搜索中更新并重新生成索引

本文介绍如何通过增量索引利用架构更改或内容更改来更新 Azure AI 搜索中的现有索引。 其中解释了在哪种情况下需要重新生成,并提供了有关缓解重新生成索引对正在进行的查询请求的影响的建议。

在活跃的开发过程中,经常需要在迭代索引设计时删除并重新生成索引。 大多数开发人员使用具有代表性的小型数据示例,使重新编制索引速度更快。

对于已投入生产的应用程序的架构更改,建议创建一个与现有索引并排运行的新索引并对其进行测试。 使用索引别名在新索引中进行交换,同时避免更改应用程序代码。

更新内容

针对源数据的更改进行增量索引和同步索引是大多数搜索应用程序的基础。 本部分介绍用于更新搜索索引中的字段内容的工作流。

  1. 使用相同的技术来加载文档:文档 - 索引 (REST) 或 Azure SDK 中的等效 API。 有关索引技术的详细信息,请参阅加载文档

  2. 设置 @search.action 参数以确定对现有文档的影响:

    操作 效果
    delete 从索引中移除整个文档。 如果你想删除某个字段,请改用合并,将相应字段设置为 NULL。 已删除的文档和字段不会立即释放索引中的空间。 每隔几分钟,后台进程就会执行物理删除。 无论是使用门户还是 API 来返回索引统计信息,门户和 API 反映出该删除之前都会出现一点小延迟。
    merge 更新已存在的文档,如果找不到该文档,则会失败。 合并将替换现有值。 因此,请务必检查是否有包含多个值的集合字段,例如类型为 Collection(Edm.String) 的字段。 例如,如果 tags 字段以 ["budget"] 值开头,并且你执行与 ["economy", "pool"] 的合并,则 tags 字段的最终值为 ["economy", "pool"]。 而不会是 ["budget", "economy", "pool"]
    mergeOrUpload 如果文档已存在,则行为类似于合并;如果文档是新的,则类似于上传。 这是增量更新的最常见操作。
    upload 类似于“更新插入”,如果文档是新文档,则插入;如果文档已经存在,则进行更新或替换。 如果文档缺少索引所需的值,则将文档字段的值设置为 null。
  3. 发布更新。

查询继续运行,但如果正在更新或移除现有字段,则可以预期混合的结果,以及更高的限制发生率。

增量索引编制的提示

  • 索引器自动执行增量索引编制。 如果你可以使用索引器,并且数据源支持更改跟踪,则可以按定期计划运行索引器来添加、更新或覆盖可搜索的内容,以便将其同步到你的外部数据。

  • 如果要直接通过推送 API 进行索引调用,请使用 mergeOrUpload 作为搜索操作。

  • 有效负载必须包含要添加、更新或删除的每个文档的键或标识符。

  • 如果索引包含矢量字段,且 stored 属性设置为 false,请确保在部分文档更新中提供矢量,即使该值保持不变也是如此。 将 stored 设置为 false 的副作用是,在执行重新编制索引操作时会丢弃向量。 在文档有效负载中提供矢量可防止发生这种情况。

  • 若要更新复杂类型中简单字段和子字段的内容,请仅列出要更改的字段。 例如,如果只需要更新说明字段,则有效负载应包含文档键和修改后的说明。 省略其他字段会保留其现有值。

  • 要将内联更改合并到字符串集合中,请提供整个值。 回想上一部分中的 tags 字段示例。 新值将覆盖整个字段的旧值,并且字段内容中没有合并。

下面是演示这些使用技巧的 REST API 示例

### Get Stay-Kay City Hotel by ID
GET  {{baseUrl}}/indexes/hotels-vector-quickstart/docs('1')?api-version=2024-07-01  HTTP/1.1
    Content-Type: application/json
    api-key: {{apiKey}}

### Change the description, city, and tags for Stay-Kay City Hotel
POST {{baseUrl}}/indexes/hotels-vector-quickstart/docs/search.index?api-version=2024-07-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}}

    {
        "value": [
            {
            "@search.action": "mergeOrUpload",
            "HotelId": "1",
            "Description": "I'm overwriting the description for Stay-Kay City Hotel.",
            "Tags": ["my old item", "my new item"],
            "Address": {
                "City": "Gotham City"
                }
            }
        ]
    }

### Retrieve the same document, confirm the overwrites and retention of all other values
GET  {{baseUrl}}/indexes/hotels-vector-quickstart/docs('1')?api-version=2024-07-01  HTTP/1.1
    Content-Type: application/json
    api-key: {{apiKey}}

更改索引架构

索引架构定义在搜索服务上创建的物理数据结构,因此,没有很多无需完全重新生成即可进行的架构更改。 以下列表枚举了可无缝引入现有索引的架构更改。 通常,该列表包括查询执行期间使用的新字段和功能。

  • 添加新字段
  • 在现有字段上设置 retrievable 特性
  • 在具有现有的 indexAnalyzer 的字段上更新 searchAnalyzer
  • 在索引中添加新的分析器定义(该定义可应用于新字段)
  • 添加、更新或删除计分概要文件
  • 添加、更新或删除 CORS 设置
  • 添加、更新或删除 synonymMaps
  • 添加、更新或删除语义配置

操作顺序为:

  1. 获取索引定义

  2. 使用上一个列表中的更新来修订架构。

  3. 更新搜索服务上的索引架构

  4. 如果添加了新字段,请更新索引内容以匹配修订后的架构。 对于所有其他更改,将按原样使用现有索引内容。

更新索引架构以包含新字段时,将为索引中的现有文档提供该字段的 null 值。 在下一个索引作业中,外部源数据中的值将替换 Azure AI 搜索添加的 null 值。

更新期间不应发生查询中断,但查询结果将会因更新生效而发生变化。

删除并重新生成索引

某些修改需要删除和重新生成索引,从而将当前索引替换为新的索引。

操作 说明
删除字段 若要以物理方式删除字段的所有跟踪,必须重新生成索引。 在立即重新生成不可行时,可修改应用程序代码以重定向访问,使其远离过时的字段,或者使用 searchFieldsselect 查询参数来选择要搜索和返回的字段。 实际上,当你应用省略了相关字段的架构时,字段定义和内容会一直保留在索引中,直至下次重新生成。
更改字段定义 对字段名称、数据类型或特定的索引属性(可搜索、可筛选、可排序、可查找)的修改需要完全重新生成。
向字段分配分析器 分析器在索引中定义,分配给字段,然后在索引编制期间进行调用,以告知令牌的创建方式。 随时都可以向索引添加新的分析器定义,但只有在创建字段时才能分配分析器。 对于 analyzerindexAnalyzer 属性都是如此。 searchAnalyzer 属性是一个例外(可以向现有字段分配此属性)。
更新或删除索引中的分析器定义 无法删除或更改索引中的现有分析器配置(分析器、tokenizer、令牌筛选器或字符筛选器),除非重新生成整个索引。
将字段添加到建议器 如果某个字段已存在,并且希望将其添加到建议器构造,则重新生成索引。
切换层 不支持就地升级。 如果需要更多容量,创建新服务并从头开始重新生成索引。 若要自动完成此过程,可以使用此 Azure AI 搜索 .NET 示例存储库中的 index-backup-restore 示例代码。 此应用会将索引备份到一系列 JSON 文件,然后在指定的搜索服务中重新创建索引。

操作顺序为:

  1. 如果需要索引定义以供将来参考,或将其用作新版本的基础,请获取索引定义

  2. 请考虑使用备份和还原解决方案来保留索引内容的副本。 C#Python 都有解决方案。 建议使用 Python 版本,因为它更新。

    如果搜索服务上具有容量,请在创建和测试新索引的同时保留现有索引。

  3. 删除现有索引。 针对该索引的查询会被立即删除。 请注意,删除索引是不可逆的,此操作会销毁字段集合和其他构造的物理存储空间。

  4. 发布修订后的索引,其中,请求正文包括已更改或已修改的字段定义和配置。

  5. 通过外部源使用文件加载索引。 文档会通过新架构的字段定义和配置进行索引编制。

创建索引时,将为索引架构中的每个字段分配物理存储,并为每个可搜索字段和为每个矢量字段创建的矢量索引创建一个反向索引。 不可搜索的字段可以用于筛选器或表达式中,但没有反向索引也不支持全文或模糊搜索。 在重新生成索引时,这些反向索引和矢量索引会被删除,并根据你提供的索引架构重新创建。

实现工作负载均衡

索引不在后台运行,但搜索服务会将所有索引作业与正在进行的查询进行均衡。 在编制索引期间,可以在门户中监视查询请求,以确保查询及时完成。

如果索引工作负载导致查询延迟达到不可接受的级别,请执行性能分析,并查看这些性能提示来了解潜在的缓解措施。

检查更新

在加载第一个文档时就可以开始查询索引。 如果你知道文档的 ID,那么查找文档 REST API 将返回特定的文档。 对于更大型的测试,应该等待索引完全加载,然后使用查询来验证你想看到的上下文。

可使用搜索资源管理器REST 客户端来检查更新的内容。

如果添加或重命名了某个字段,请使用 $select 返回该字段:search=*&$select=document-id,my-new-field,some-old-field&$count=true

Azure 门户提供了索引大小和矢量索引大小。 更新索引后,可以检查这些值,但请记住,在服务处理更改并考虑门户刷新率时会出现一点小延迟(可能为几分钟)。

删除孤立文档

Azure AI 搜索支持文档级操作,因此你可单独查找、更新和删除特定文档。 以下示例说明了如何删除文档。

删除文档不会立即释放索引中的空间。 每隔几分钟,后台进程就会执行物理删除。 无论是使用门户还是 API 来返回索引统计信息,门户和 API 指标反映出该删除之前都会出现一点小延迟。

  1. 确定哪个字段是文档键。 在门户中,可查看每个索引的字段。 文档键是字符串字段,并使用键图标表示,便于更容易识别。

  2. 检查文档键字段的值:search=*&$select=HotelId。 简单字符串非常简单,但是如果索引使用 base-64 编码的字段,或者搜索文档是从 parsingMode 设置生成的,则你可能正在使用不熟悉的值。

  3. 查找文档来验证文档 ID 的值,并在删除之前查看其内容。 在请求中指定键或文档 ID。 下面的示例演示了酒店示例索引的简单字符串,以及 cog-search-demo index 的 metadata_storage_path 键的 base-64 编码字符串。

    GET https://[service name].search.azure.cn/indexes/hotel-sample-index/docs/1111?api-version=2024-07-01
    
    GET https://[service name].search.azure.cn/indexes/cog-search-demo/docs/aHR0cHM6Ly9oZWlkaWJsb2JzdG9yYWdlMi5ibG9iLmNvcmUud2luZG93cy5uZXQvY29nLXNlYXJjaC1kZW1vL2d1dGhyaWUuanBn0?api-version=2024-07-01
    
  4. 使用删除 @search.action删除文档,将其从搜索索引中移除。

    POST https://[service name].search.azure.cn/indexes/hotels-sample-index/docs/index?api-version=2024-07-01
    Content-Type: application/json   
    api-key: [admin key] 
    {  
      "value": [  
        {  
          "@search.action": "delete",  
          "id": "1111"  
        }  
      ]  
    }
    

另请参阅