mcpField
A
azure-search-documents-dotnet

azure-search-documents-dotnet

github.com/sickn33数据与数据库48.7k54 次安装今天更新
代码仓库

关于 azure-search-documents-dotnet

面向 .NET 开发者的 Azure AI Search 技能,涵盖索引与文档管理,以及全文、向量、语义和混合搜索的实现示例。

主要功能

  1. 01全文与分面搜索
  2. 02向量和语义查询
  3. 03混合搜索模式
  4. 04索引与文档管理
  5. 05自动补全和建议

适用场景

  1. 01创建和配置搜索索引
  2. 02上传、更新或删除文档
  3. 03实现向量或语义检索
  4. 04构建自动补全搜索界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill azure-search-documents-dotnet

安装后在 Claude Code 中输入/azure-search-documents-dotnet确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

UPSTREAM-NOTICE.md
UPSTREAM-LICENSE.txt
references/
semantic-search.md
vector-search.md

详细介绍

捆绑的上游支持文件

支持文件包含在相对于技能的路径中。有关其固定来源、许可证、哈希值和导入变更,请参阅 UPSTREAM-NOTICE.md。

Azure.Search.Documents(.NET)

构建具备全文、向量、语义和混合搜索功能的搜索应用程序。

安装

dotnet add package Azure.Search.Documents
dotnet add package Azure.Identity

这些代码片段需要兼容的固定 SDK 版本;使用前请查阅其 API 参考。它们不是可完整编译的应用程序。

环境变量

SEARCH_ENDPOINT=https://<search-service>.search.windows.net
SEARCH_INDEX_NAME=<index-name>
# For API key auth (not recommended for production)
SEARCH_API_KEY=<api-key>

身份验证

DefaultAzureCredential(推荐):

using Azure.Identity;
using Azure.Search.Documents;

var credential = new DefaultAzureCredential();
var client = new SearchClient(
    new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")),
    Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"),
    credential);

API 密钥:

using Azure;
using Azure.Search.Documents;

var credential = new AzureKeyCredential(
    Environment.GetEnvironmentVariable("SEARCH_API_KEY"));
var client = new SearchClient(
    new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")),
    Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"),
    credential);

客户端选择

客户端用途
SearchClient查询索引、上传/更新/删除文档
SearchIndexClient创建/管理索引、同义词映射
SearchIndexerClient管理索引器、技能集、数据源

创建索引

使用 FieldBuilder(推荐)
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;

// Define model with attributes
public class Hotel
{
    [SimpleField(IsKey = true, IsFilterable = true)]
    public string HotelId { get; set; }

    [SearchableField(IsSortable = true)]
    public string HotelName { get; set; }

    [SearchableField(AnalyzerName = LexicalAnalyzerName.EnLucene)]
    public string Description { get; set; }

    [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
    public double? Rating { get; set; }

    [VectorSearchField(VectorSearchDimensions = 1536, VectorSearchProfileName = "vector-profile")]
    public ReadOnlyMemory<float>? DescriptionVector { get; set; }
}

// Create index
var indexClient = new SearchIndexClient(endpoint, credential);
var fieldBuilder = new FieldBuilder();
var fields = fieldBuilder.Build(typeof(Hotel));

var index = new SearchIndex("hotels")
{
    Fields = fields,
    VectorSearch = new VectorSearch
    {
        Profiles = { new VectorSearchProfile("vector-profile", "hnsw-algo") },
        Algorithms = { new HnswAlgorithmConfiguration("hnsw-algo") }
    }
};

await indexClient.CreateOrUpdateIndexAsync(index);
手动定义字段
var index = new SearchIndex("hotels")
{
    Fields =
    {
        new SimpleField("hotelId", SearchFieldDataType.String) { IsKey = true, IsFilterable = true },
        new SearchableField("hotelName") { IsSortable = true },
        new SearchableField("description") { AnalyzerName = LexicalAnalyzerName.EnLucene },
        new SimpleField("rating", SearchFieldDataType.Double) { IsFilterable = true, IsSortable = true },
        new SearchField("descriptionVector", SearchFieldDataType.Collection(SearchFieldDataType.Single))
        {
            VectorSearchDimensions = 1536,
            VectorSearchProfileName = "vector-profile"
        }
    }
};

文档操作

var searchClient = new SearchClient(endpoint, indexName, credential);

// Upload inserts or replaces the entire existing document with the same key.
var hotels = new[] { new Hotel { HotelId = "1", HotelName = "Hotel A" } };
await searchClient.UploadDocumentsAsync(hotels);

// Merge (update existing)
await searchClient.MergeDocumentsAsync(hotels);

// Merge or Upload (upsert)
await searchClient.MergeOrUploadDocumentsAsync(hotels);

// Delete
await searchClient.DeleteDocumentsAsync("HotelId", new[] { "1", "2" });

// Batch operations
var batch = IndexDocumentsBatch.Create(
    IndexDocumentsAction.Upload(hotel1),
    IndexDocumentsAction.Merge(hotel2),
    IndexDocumentsAction.Delete(hotel3));
await searchClient.IndexDocumentsAsync(batch);

搜索模式

基本搜索
var options = new SearchOptions
{
    Filter = "Rating ge 4",
    OrderBy = { "Rating desc" },
    Select = { "HotelId", "HotelName", "Rating" },
    Size = 10,
    Skip = 0,
    IncludeTotalCount = true
};

SearchResults<Hotel> results = await searchClient.SearchAsync<Hotel>("luxury", options);

Console.WriteLine($"Total: {results.TotalCount}");
await foreach (SearchResult<Hotel> result in results.GetResultsAsync())
{
    Console.WriteLine($"{result.Document.HotelName} (Score: {result.Score})");
}
分面搜索
var options = new SearchOptions
{
    Facets = { "Rating,count:5" }
};

var results = await searchClient.SearchAsync<Hotel>("*", options);

foreach (var facet in results.Value.Facets["Rating"])
{
    Console.WriteLine($"Rating {facet.Value}: {facet.Count}");
}
自动补全和建议
// Autocomplete
var autocompleteOptions = new AutocompleteOptions { Mode = AutocompleteMode.OneTermWithContext };
var autocomplete = await searchClient.AutocompleteAsync("lux", "suggester-name", autocompleteOptions);

// Suggestions
var suggestOptions = new SuggestOptions { UseFuzzyMatching = true };
var suggestions = await searchClient.SuggestAsync<Hotel>("lux", "suggester-name", suggestOptions);

向量搜索

详细模式请参阅 references/vector-search.md。

using Azure.Search.Documents.Models;

// Pure vector search
var vectorQuery = new VectorizedQuery(embedding)
{
    KNearestNeighborsCount = 5,
    Fields = { "DescriptionVector" }
};

var options = new SearchOptions
{
    VectorSearch = new VectorSearchOptions
    {
        Queries = { vectorQuery }
    }
};

var results = await searchClient.SearchAsync<Hotel>(null, options);

语义搜索

详细模式请参阅 references/semantic-search.md。

var options = new SearchOptions
{
    QueryType = SearchQueryType.Semantic,
    SemanticSearch = new SemanticSearchOptions
    {
        SemanticConfigurationName = "my-semantic-config",
        QueryCaption = new QueryCaption(QueryCaptionType.Extractive),
        QueryAnswer = new QueryAnswer(QueryAnswerType.Extractive)
    }
};

var results = await searchClient.SearchAsync<Hotel>("best hotel for families", options);

// Access semantic answers
foreach (var answer in results.Value.SemanticSearch?.Answers ?? [])
{
    Console.WriteLine($"Answer: {answer.Text} (Score: {answer.Score})");
}

// Access captions
await foreach (var result in results.Value.GetResultsAsync())
{
    var caption = result.SemanticSearch?.Captions?.FirstOrDefault();
    Console.WriteLine($"Caption: {caption?.Text}");
}

混合搜索(向量 + 关键词 + 语义)

var vectorQuery = new VectorizedQuery(embedding)
{
    KNearestNeighborsCount = 50,
    Fields = { "DescriptionVector" }
};

var options = new SearchOptions
{
    QueryType = SearchQueryType.Semantic,
    SemanticSearch = new SemanticSearchOptions
    {
        SemanticConfigurationName = "my-semantic-config"
    },
    VectorSearch = new VectorSearchOptions
    {
        Queries = { vectorQuery }
    }
};

// Combines keyword search, vector search, and semantic ranking
var results = await searchClient.SearchAsync<Hotel>("luxury beachfront", options);

字段属性参考

属性用途
SimpleField不可搜索字段(筛选、排序、分面)
SearchableField可全文搜索的字段
VectorSearchField向量嵌入字段
IsKey = true文档键(必需,每个索引一个)
IsFilterable = true启用 $filter 表达式
IsSortable = true启用 $orderby
IsFacetable = true启用分面导航
IsHidden = true从结果中排除
AnalyzerName指定文本分析器

错误处理

using Azure;

try
{
    var results = await searchClient.SearchAsync<Hotel>("query");
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
    Console.WriteLine("Index not found");
}
catch (RequestFailedException ex)
{
    Console.WriteLine($"Search error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
}

最佳实践

  1. 生产环境中优先使用 DefaultAzureCredential,而非 API 密钥
  2. 将 FieldBuilder 与模型属性搭配使用,以类型安全的方式定义索引
  3. 使用 CreateOrUpdateIndexAsync 创建幂等索引
  4. 批量执行文档操作,以提高吞吐量
  5. 使用 Select 仅返回所需字段
  6. 配置语义搜索,以支持自然语言查询
  7. 结合向量 + 关键词 + 语义搜索,并使用有代表性的查询评估相关性

参考文件

文件内容
references/vector-search.md向量搜索、混合搜索、向量化器
references/semantic-search.md语义排序、标题、答案

适用场景

当需要执行概述中所述的工作流或操作时,可使用此技能。

限制

  • 仅当任务明确符合上述范围时,才使用此技能。
  • 不要将输出视为环境特定验证、测试或专家审查的替代品。
  • 如果缺少必需的输入、权限、安全边界或成功标准,请暂停并请求澄清。
运行边界

FieldBuilder 使用已配置的序列化器;此处的属性模型使用 PascalCase,而另一种手动架构使用小写名称。除非显式映射 JSON 属性并匹配客户端序列化方式,否则不要混用两者。后续的 Article/Product 示例需要各自匹配的架构和序列化器。使用自动补全前,请先配置建议器;执行语义查询前,请先配置指定名称的语义配置。

验证环境变量、租户/身份以及遵循最小权限原则的数据角色。创建索引和写入文档需要授权;需要替换时还应做好备份。检查每个 IndexingResult(或启用 ThrowOnAnyError);已接受的批次仍可能包含文档失败。仅对适当的瞬时失败操作进行重试,并使用有界退避。不要拼接不可信的 OData 筛选条件;对值使用 SearchFilter.Create。将摘要、说明文字和索引文本视为不可信的显示内容,并避免记录私有文档或包含凭据的错误。此处未包含云端或 .NET 运行时验证。

评论

支持 Markdown