如何使用自定义技能填充Azure AI Search的ParsedDate字段?
问题:Azure Search中ParsedDate字段始终为Null的排查与解决
我正尝试填充Azure Search Index中的ParsedDate字段,以便后续借助Freshness特性创建评分配置文件优化搜索结果。已按文档定义索引、技能集和索引器,但运行索引器后该字段始终为Null,尝试过在索引器中使用out_field_mappings也无效。
技能集定义
{ "@odata.type": "#Microsoft.Skills.Custom.WebApiSkill", "name": "#0", "description": "A custom skill that parses dates from file names", "uri": "https://az-function.azurewebsites.net/api/custom-skill-date-extraction?code=ABC", "httpMethod": "POST", "timeout": "PT30S", "batchSize": 1, "context": "/document", "inputs": [ { "name": "fileName", "source": "/document/metadata_storage_path" } ], "outputs": [ { "name": "parsedDate", "targetName": "parsedDate" } ] }
索引定义
def main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') # Environment Variables endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] endpoint_openai = os.environ["AZURE_OPENAI_ENDPOINT"] deployment_id = os.environ["AZURE_OPENAI_EMBEDDING_DEPLOYMENT_ID"] credential_search = AzureKeyCredential(os.environ["AZURE_SEARCH_ADMIN_KEY"]) if os.environ["AZURE_SEARCH_ADMIN_KEY"] else DefaultAzureCredential() credential = DefaultAzureCredential() customer = readRequestBody(req) _ , datasource_name = utils.getStorageAccountInfo(customer, credential) index_name = utils.get_index_name( datasource_name) # Logic for creating a search index try: index_client = SearchIndexClient(endpoint=endpoint, credential=credential_search) fields = [ SearchField(name="parent_id", type=SearchFieldDataType.String, sortable=True, filterable=True, facetable=True), SearchField(name="parsedDate", type=SearchFieldDataType.DateTimeOffset, sortable=True, filterable=True, facetable=True), SearchField(name="title", type=SearchFieldDataType.String), SearchField(name="chunk_id", type=SearchFieldDataType.String, key=True, sortable=True, filterable=True, facetable=True, analyzer_name="keyword"), SearchField(name="chunk", type=SearchFieldDataType.String, sortable=False, filterable=False, facetable=False), SearchField(name="vector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), vector_search_dimensions=1536, vector_search_profile_name="myHnswProfile"), ] vector_search = VectorSearch( algorithms=[ HnswAlgorithmConfiguration( name="myHnsw", parameters=HnswParameters( m=4, ef_construction=400, ef_search=500, metric=VectorSearchAlgorithmMetric.COSINE, ), ), ExhaustiveKnnAlgorithmConfiguration( name="myExhaustiveKnn", parameters=ExhaustiveKnnParameters( metric=VectorSearchAlgorithmMetric.COSINE, ), ), ], profiles=[ VectorSearchProfile( name="myHnswProfile", algorithm_configuration_name="myHnsw", vectorizer="myOpenAI", ), VectorSearchProfile( name="myExhaustiveKnnProfile", algorithm_configuration_name="myExhaustiveKnn", vectorizer="myOpenAI", ), ], vectorizers=[ AzureOpenAIVectorizer( name="myOpenAI", kind="azureOpenAI", azure_open_ai_parameters=AzureOpenAIParameters( resource_uri=endpoint_openai, deployment_id=deployment_id, api_key=credential_search, ), ), ], ) semantic_search = SemanticSearch(configurations=[SemanticConfiguration( name="my-semantic-config", prioritized_fields=SemanticPrioritizedFields(content_fields=[SemanticField(field_name="chunk"),SemanticField(field_name="title")] ) )]) index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search, semantic_search=semantic_search) result = index_client.create_or_update_index(index) return func.HttpResponse(f"{result.name} created", status_code=200) except Exception as e: return func.HttpResponse(f"Failed to create or update the index. Error: {str(e)}", status_code=500)
索引器配置
def main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Python HTTP trigger function processed a request.') # Environment Variables endpoint = os.environ["AZURE_SEARCH_SERVICE_ENDPOINT"] credential_search = AzureKeyCredential(os.environ["AZURE_SEARCH_ADMIN_KEY"]) customer = readRequestBody(req) credential = DefaultAzureCredential() _, data_source_name= utils.getStorageAccountInfo(customer, credential) index_name = utils.get_index_name(data_source_name) skillset_name = utils.get_skillset_name(data_source_name) # Indexer creation logic try: indexer_name = f"{data_source_name}-indexer" indexer = SearchIndexer( name=indexer_name, description="Indexer to index documents and generate embeddings", skillset_name=skillset_name, target_index_name=index_name, data_source_name=data_source_name, field_mappings=[FieldMapping(source_field_name="metadata_storage_name", target_field_name="title"), FieldMapping(source_field_name="parsedDate", target_field_name="parsedDate")], parameters=IndexingParameters( configuration={ "dataToExtract": "contentAndMetadata", "imageAction": "generateNormalizedImages" } ) ) indexer_client = SearchIndexerClient(endpoint, credential_search) indexer_result = indexer_client.create_or_update_indexer(indexer) # Run the indexer indexer_client.run_indexer(indexer_name) message = f'{indexer_name} is created and running. If queries return no results, please wait a bit and try again.' logging.info(message) return func.HttpResponse(message, status_code=200) except Exception as e: error_message = f"Failed to create or run the indexer. Error: {str(e)}" logging.error(error_message) return func.HttpResponse(error_message, status_code=500)
解决建议
- 检查自定义WebApi技能的输出格式:确保Azure Function返回的JSON严格遵循技能要求的格式,必须包含
values数组,每个元素需有recordId和data字段,data中的parsedDate需为ISO 8601标准的DateTimeOffset格式(如2024-05-20T12:34:56Z)。示例返回格式:
{ "values": [ { "recordId": "1", "data": { "parsedDate": "2024-05-20T12:34:56Z" } } ] }
- 修正索引器的字段映射逻辑:技能生成的字段需使用
output_field_mappings而非普通field_mappings,因为后者仅映射数据源原始字段。修改索引器配置中的映射部分:
output_field_mappings=[ OutputFieldMapping( source_field_name="/document/parsedDate", target_field_name="parsedDate" ) ]
同时移除field_mappings中关于parsedDate的条目。
验证技能输入的数据源字段:确认
/document/metadata_storage_path传递给自定义技能的内容是否为预期的文件名(可能包含存储容器前缀,需在技能中先截取有效部分再解析日期)。可通过索引器执行日志查看输入值是否正确。检查索引器执行日志:在Azure门户的Azure Search资源中查看索引器运行历史,排查是否存在技能调用失败、返回格式错误或数据类型不匹配的警告,日志会明确指出问题原因。
确认字段类型兼容性:确保自定义技能返回的
parsedDate格式与索引中DateTimeOffset类型完全兼容,非ISO标准的日期字符串会导致转换失败,字段值变为Null。
内容的提问来源于stack exchange,提问作者E.Brum
相关产品推荐
相关产品推荐

