你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
在 Azure AI 搜索中更新并重新生成索引
本文介绍如何通过增量索引利用架构更改或内容更改来更新 Azure AI 搜索中的现有索引。 其中解释了在哪种情况下需要重新生成,并提供了有关缓解重新生成索引对正在进行的查询请求的影响的建议。
在活跃的开发过程中,经常需要在迭代索引设计时删除并重新生成索引。 大多数开发人员使用具有代表性的小型数据示例,使重新编制索引速度更快。
对于已投入生产的应用程序的架构更改,建议创建一个与现有索引并排运行的新索引并对其进行测试。 使用索引别名来换入新索引,这样就能够避免更改应用程序代码。
更新内容
针对源数据的更改进行增量索引和同步索引是大多数搜索应用程序的基础。 本部分介绍用于通过 REST API 更新搜索索引中的字段内容的工作流,但 Azure SDK 提供等效的功能。
请求的正文包含要编入索引的一个或多个文档。 文档由区分大小写的唯一键标识。 每个文档都关联下面的一个操作:“upload”、“delete”、“merge”或“mergeOrUpload”。 上传请求必须包含文档数据作为一组键/值对。
{
"value": [
{
"@search.action": "upload (default) | merge | mergeOrUpload | delete",
"key_field_name": "unique_key_of_document", (key/value pair for key field from index schema)
"field_name": field_value (key/value pairs matching index schema)
...
},
...
]
}
首先,使用 API 来加载文档,例如文档 - 索引 (REST),也可以使用 Azure SDK 中的等效 API。 有关索引技术的详细信息,请参阅加载文档。
对于大型更新,建议使用批处理(每批最多 1,000 个文档,或每批约 16 MB,具体取决于先达到哪个限制),以显著提高索引性能。
在 API 上设置
@search.action
参数以确定对现有文档的影响。操作 效果 delete 从索引中移除整个文档。 如果你想删除某个字段,请改用合并,将相应字段设置为 NULL。 已删除的文档和字段不会立即释放索引中的空间。 每隔几分钟,后台进程就会执行物理删除。 无论你是使用 Azure 门户还是 API 来返回索引统计信息,删除操作在通过 Azure 门户和 API 反映出来之前都会出现小的延迟。 merge 更新已存在的文档,如果找不到该文档,则会失败。 合并将替换现有值。 因此,请务必检查是否有包含多个值的集合字段,例如类型为 Collection(Edm.String)
的字段。 例如,如果tags
字段以["budget"]
值开头,并且你执行与["economy", "pool"]
的合并,则tags
字段的最终值为["economy", "pool"]
。 而不会是["budget", "economy", "pool"]
。
同一行为适用于复杂的集合。 如果文档包含一个名为“Rooms”的复杂集合字段,其值为[{ "Type": "Budget Room", "BaseRate": 75.0 }]
,而你执行的合并的值为[{ "Type": "Standard Room" }, { "Type": "Budget Room", "BaseRate": 60.5 }]
,则“Rooms”字段的最终值将为[{ "Type": "Standard Room" }, { "Type": "Budget Room", "BaseRate": 60.5 }]
。 它不会追加或合并新的和现有的值。mergeOrUpload 如果文档已存在,则行为类似于合并;如果文档是新的,则类似于上传。 这是增量更新的最常见操作。 upload 类似于“更新插入”,如果文档是新文档,则插入;如果文档已经存在,则进行更新或替换。 如果文档缺少索引所需的值,则将文档字段的值设置为 null。
查询会继续在索引编制期间运行,但如果你要更新或移除现有字段,则可能会出现混合的结果,发生限制的可能性会更高。
注意
对于请求正文中的哪个操作首先执行,没有任何顺序保证。 不建议在单个请求正文中将多个“merge”操作与同一文档关联。 如果同一文档需要多个“merge”操作,请先在客户端执行合并操作,然后再更新搜索索引中的文档。
响应
成功的响应会返回状态代码 200,这意味着所有项都已持久存储并将开始编入索引。 索引编制在后台运行,使新文档在索引编制操作完成几秒钟后就可用(即可供查询和搜索)。 具体延迟取决于服务的负载。
如果所有项的 status 属性都设置为 true,且 statusCode
属性设置为 201(针对新上传的文档)或 200(针对合并或删除的文档),则表明已成功编制索引:
{
"value": [
{
"key": "unique_key_of_new_document",
"status": true,
"errorMessage": null,
"statusCode": 201
},
{
"key": "unique_key_of_merged_document",
"status": true,
"errorMessage": null,
"statusCode": 200
},
{
"key": "unique_key_of_deleted_document",
"status": true,
"errorMessage": null,
"statusCode": 200
}
]
}
当至少有一个项未成功编入索引时,会返回状态代码 207。 未编入索引的项会将“status”字段设置为 false。 errorMessage
和 statusCode
属性会指示索引编制错误的原因:
{
"value": [
{
"key": "unique_key_of_document_1",
"status": false,
"errorMessage": "The search service is too busy to process this document. Please try again later.",
"statusCode": 503
},
{
"key": "unique_key_of_document_2",
"status": false,
"errorMessage": "Document not found.",
"statusCode": 404
},
{
"key": "unique_key_of_document_3",
"status": false,
"errorMessage": "Index is temporarily unavailable because it was updated with the 'allowIndexDowntime' flag set to 'true'. Please try again later.",
"statusCode": 422
}
]
}
errorMessage
属性会尽可能指示索引编制错误的原因。
下表说明可在响应中返回的各种每文档状态代码。 某些状态代码会指示请求本身的问题,而另一些代码会指示临时错误条件。 对于后者,应在延迟后重试。
状态代码 | 含义 | 可重试 | 备注 |
---|---|---|---|
200 | 文档已成功修改或删除。 | 不适用 | 删除操作是幂等的。 也就是说,即使索引中不存在某个文档键,使用该键尝试执行删除操作也会生成 200 状态代码。 |
201 | 已成功创建文档。 | 不适用 | |
400 | 文档中存在阻止其编入索引的错误。 | 否 | 响应中的错误消息指示文档存在的问题。 |
404 | 文档无法合并,因为索引中不存在给定键。 | 否 | 上传不会发生此错误,因为上传会创建新文档;删除不会发生此错误,因为删除是幂等的。 |
409 | 尝试将文档编入索引时检测到了版本冲突。 | 是 | 尝试将相同文档同时多次编入索引时,可能发生此错误。 |
422 | 索引暂时不可用,因为在将“allowIndexDowntime”标志设置为“true”的情况下更新了它。 | 是 | |
503 | 搜索服务暂时不可用,可能是因为负荷较重。 | 是 | 在此情况下,代码应在重试前等待,否则面临延长服务不可用性状态的风险。 |
如果客户端代码经常遇到 207 响应,则一个可能的原因是系统过载。 可通过检查 statusCode 属性是否为 503 来确认这种情况。 如果 statusCode 为 503,我们建议限制索引编制请求。 否则,如果索引流量不下降,系统可能开始使用 503 错误拒绝所有请求。
状态代码 429 指示已超出每个索引的文档数配额。 必须创建新索引或升级以提高容量限制。
注意
将包含时区信息的 DateTimeOffset
值上传到索引时,Azure AI 搜索会将这些值规范化为 UTC。 例如,2024-01-13T14:03:00-08:00 会存储为 2024-01-13T22:03:00Z。 如果需要存储时区信息,请在索引中为该数据点添加一个额外的列。
增量索引编制的提示
索引器自动执行增量索引编制。 如果你可以使用索引器,并且数据源支持更改跟踪,则可以按定期计划运行索引器来添加、更新或覆盖可搜索的内容,以便将其同步到你的外部数据。
如果要直接通过推送 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
- 在索引中添加新的分析器定义(该定义可应用于新字段)
- 添加、更新或删除计分概要文件
- 添加、更新或删除 synonymMaps
- 添加、更新或删除语义配置
- 添加、更新或删除 CORS 设置
操作顺序为:
使用上一个列表中的更新来修订架构。
如果添加了新字段,请更新索引内容以匹配修订后的架构。 对于所有其他更改,将按原样使用现有索引内容。
更新索引架构以包含新字段时,将为索引中的现有文档提供该字段的 null 值。 在下一个索引作业中,外部源数据中的值将替换 Azure AI 搜索添加的 null 值。
更新期间不应发生查询中断,但查询结果将会因更新生效而发生变化。
需要重新生成索引的更新
某些修改需要删除和重新生成索引,从而将当前索引替换为新的索引。
操作 | 说明 |
---|---|
删除字段 | 若要以物理方式删除字段的所有跟踪,必须重新生成索引。 在立即重新生成不可行时,可修改应用程序代码以重定向访问,使其远离过时的字段,或者使用 searchFields 和 select 查询参数来选择要搜索和返回的字段。 实际上,当你应用省略了相关字段的架构时,字段定义和内容会一直保留在索引中,直至下次重新生成。 |
更改字段定义 | 对字段名称、数据类型或特定的索引属性(可搜索、可筛选、可排序、可查找)的修改需要完全重新生成。 |
向字段分配分析器 | 分析器在索引中定义,分配给字段,然后在索引编制期间进行调用,以告知令牌的创建方式。 随时都可以向索引添加新的分析器定义,但只有在创建字段时才能分配分析器。 对于 analyzer 和 indexAnalyzer 属性都是如此。 searchAnalyzer 属性是一个例外(可以向现有字段分配此属性)。 |
更新或删除索引中的分析器定义 | 无法删除或更改索引中的现有分析器配置(分析器、tokenizer、令牌筛选器或字符筛选器),除非重新生成整个索引。 |
将字段添加到建议器 | 如果某个字段已存在,并且希望将其添加到建议器构造,则重新生成索引。 |
切换层 | 不支持就地升级。 如果需要更多容量,创建新服务并从头开始重新生成索引。 若要自动完成此过程,可以使用此 Azure AI 搜索 .NET 示例存储库中的 index-backup-restore 示例代码。 此应用会将索引备份到一系列 JSON 文件,然后在指定的搜索服务中重新创建索引。 |
操作顺序为:
如果需要索引定义以供将来参考,或将其用作新版本的基础,请获取索引定义。
请考虑使用备份和还原解决方案来保留索引内容的副本。 C# 和 Python 都有解决方案。 建议使用 Python 版本,因为它更新。
如果搜索服务上具有容量,请在创建和测试新索引的同时保留现有索引。
删除现有索引。 针对该索引的查询会被立即删除。 请注意,删除索引是不可逆的,此操作会销毁字段集合和其他构造的物理存储空间。
发布修订后的索引,其中,请求正文包括已更改或已修改的字段定义和配置。
通过外部源使用文件加载索引。 文档会通过新架构的字段定义和配置进行索引编制。
创建索引时,将为索引架构中的每个字段分配物理存储,并为每个可搜索字段和为每个矢量字段创建的矢量索引创建一个反向索引。 不可搜索的字段可以用于筛选器或表达式中,但没有反向索引也不支持全文或模糊搜索。 在重新生成索引时,这些反向索引和矢量索引会被删除,并根据你提供的索引架构重新创建。
为了最大限度地减少对应用程序代码的干扰,请考虑创建索引别名。 应用程序代码会引用该别名,而你可以更新该别名指向的索引的名称。
实现工作负载均衡
索引不在后台运行,但搜索服务会将所有索引作业与正在进行的查询进行均衡。 在编制索引期间,你可以在 Azure 门户中监视查询请求,以确保查询及时完成。
如果索引工作负载导致查询延迟达到不可接受的级别,请执行性能分析,并查看这些性能提示来了解潜在的缓解措施。
检查更新
在加载第一个文档时就可以开始查询索引。 如果你知道文档的 ID,那么查找文档 REST API 将返回特定的文档。 对于更大型的测试,应该等待索引完全加载,然后使用查询来验证你想看到的上下文。
如果添加或重命名了某个字段,请使用 select 返回该字段:
"search": "*",
"select": "document-id, my-new-field, some-old-field",
"count": true
Azure 门户提供了索引大小和矢量索引大小。 更新索引后,可以检查这些值,但请记住,在服务处理更改并考虑门户刷新率时会出现一点小延迟(可能为几分钟)。
删除孤立文档
Azure AI 搜索支持文档级操作,因此你可单独查找、更新和删除特定文档。 以下示例说明了如何删除文档。
删除文档不会立即释放索引中的空间。 每隔几分钟,后台进程就会执行物理删除。 无论你是使用 Azure 门户还是 API 来返回索引统计信息,删除操作在通过 Azure 门户和 API 指标反映出来之前都会出现小的延迟。
确定哪个字段是文档键。 在 Azure 门户中,可查看每个索引的字段。 文档键是字符串字段,并使用键图标表示,便于更容易识别。
检查文档键字段的值:
search=*&$select=HotelId
。 简单字符串非常简单,但是如果索引使用 base-64 编码的字段,或者搜索文档是从parsingMode
设置生成的,则你可能正在使用不熟悉的值。查找文档来验证文档 ID 的值,并在删除之前查看其内容。 在请求中指定键或文档 ID。 下面的示例演示了酒店示例索引的简单字符串,以及 cog-search-demo index 的 metadata_storage_path 键的 base-64 编码字符串。
GET https://[service name].search.windows.net/indexes/hotel-sample-index/docs/1111?api-version=2024-07-01
GET https://[service name].search.windows.net/indexes/cog-search-demo/docs/aHR0cHM6Ly9oZWlkaWJsb2JzdG9yYWdlMi5ibG9iLmNvcmUud2luZG93cy5uZXQvY29nLXNlYXJjaC1kZW1vL2d1dGhyaWUuanBn0?api-version=2024-07-01
使用删除
@search.action
来删除文档,将其从搜索索引中移除。POST https://[service name].search.windows.net/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" } ] }