azure-search-documents-dotnet
microsoft/skills
Azure AI Search SDK for .NET을 사용하여 전체 텍스트, 벡터, 시맨틱 및 하이브리드 검색 기능을 갖춘 검색 애플리케이션을 구축하세요.
...모든 것을 확장하십시오Azure.Search.Documents (.NET)
전체 텍스트, 벡터, 시맨틱 및 하이브리드 검색 기능을 갖춘 검색 애플리케이션을 구축하세요.
설치
dotnet add package Azure.Search.Documents
dotnet add package Azure.Identity
현재 버전: 안정 버전 v11.7.0, 미리 보기 버전 v11.8.0-beta.1
환경 변수
SEARCH_ENDPOINT=https://.search.windows.net # 필수: 검색 서비스 엔드포인트
SEARCH_ENDPOINT=https:// .search.windows.net # 필수: 검색 서비스 엔드포인트
SEARCH_INDEX_NAME= # 필수: 검색 인덱스 이름
AZURE_TOKEN_CREDENTIALS=prod # 프로덕션 환경에서 DefaultAzureCredential을 사용하는 경우에만 필수
SEARCH_API_KEY= # AzureKeyCredential 인증에만 필요
인증
Microsoft Entra 토큰 자격 증명:
using Azure.Identity;
using Azure.Search.Documents;
// 로컬 개발 환경: DefaultAzureCredential. 프로덕션: AZURE_TOKEN_CREDENTIALS=prod 또는 AZURE_TOKEN_CREDENTIALS=로 설정
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
// 또는 프로덕션 환경에서 특정 자격 증명을 직접 사용:
// https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes 참조
// var credential = new ManagedIdentityCredential();
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 |
인덱스 쿼리, 문서 업로드/업데이트/삭제 |
검색 인덱스 클라이언트 |
인덱스 및 동의어 맵 생성/관리 |
SearchIndexerClient |
인덱서, 스킬셋, 데이터 소스 관리 |
인덱스 생성
FieldBuilder 사용 (권장)
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
// 속성을 사용하여 모델 정의
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? DescriptionVector { get; set; }
}
// 인덱스 생성
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);
// 업로드 (새 문서 추가)
var hotels = new[] { new Hotel { HotelId = "1", HotelName = "Hotel A" } };
await searchClient.UploadDocumentsAsync(hotels);
// 병합 (기존 문서 업데이트)
await searchClient.MergeDocumentsAsync(hotels);
// 병합 또는 업로드 (업서트)
await searchClient.MergeOrUploadDocumentsAsync(hotels);
// 삭제
await searchClient.DeleteDocumentsAsync("hotelId", new[] { "1", "2" });
// 일괄 작업
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 results = await searchClient.SearchAsync("luxury", options);
Console.WriteLine($"총 개수: {results.TotalCount}");
await foreach (SearchResult result in results.GetResultsAsync())
{
Console.WriteLine($"{result.Document.HotelName} (점수: {result.Score})");
}
패싯 검색
var options = new SearchOptions
{
Facets = { "rating,count:5", "category" }
};
var results = await searchClient.SearchAsync("*", options);
foreach (var facet in results.Value.Facets["rating"])
{
Console.WriteLine($"평점 {facet.Value}: {facet.Count}");
}
자동 완성 및 추천
// 자동 완성
var autocompleteOptions = new AutocompleteOptions { Mode = AutocompleteMode.OneTermWithContext };
var autocomplete = await searchClient.AutocompleteAsync("lux", "suggester-name", autocompleteOptions);
// 추천
var suggestOptions = new SuggestOptions { UseFuzzyMatching = true };
var suggestions = await searchClient.SuggestAsync("lux", "suggester-name", suggestOptions);
벡터 검색
자세한 패턴은 references/vector-search.md를 참조하세요.
using Azure.Search.Documents.Models;
// 순수 벡터 검색
var vectorQuery = new VectorizedQuery(embedding)
{
KNearestNeighborsCount = 5,
Fields = { "descriptionVector" }
};
var options = new SearchOptions
{
VectorSearch = new VectorSearchOptions
{
Queries = { vectorQuery }
}
};
var results = await searchClient.SearchAsync(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("best hotel for families", options);
// 시맨틱 답변에 접근
foreach (var answer in results.Value.SemanticSearch.Answers)
{
Console.WriteLine($"답변: {answer.Text} (점수: {answer.Score})");
}
// 캡션 가져오기
await foreach (var result in results.Value.GetResultsAsync())
{
var caption = result.SemanticSearch?.Captions?.FirstOrDefault();
Console.WriteLine($"캡션: {caption?.Text}");
}
하이브리드 검색 (벡터 + 키워드 + 시맨틱)
var vectorQuery = new VectorizedQuery(embedding)
{
KNearestNeighborsCount = 5,
Fields = { "descriptionVector" }
};
var options = new SearchOptions
{
QueryType = SearchQueryType.Semantic,
SemanticSearch = new SemanticSearchOptions
{
SemanticConfigurationName = "my-semantic-config"
},
VectorSearch = new VectorSearchOptions
{
Queries = { vectorQuery }
}
};
// 키워드 검색, 벡터 검색 및 시맨틱 순위를 결합합니다.
var results = await searchClient.SearchAsync("luxury beachfront", options);
필드 속성 참조
| 속성 | 용도 |
|---|---|
SimpleField |
검색 불가능한 필드(필터, 정렬, 패싯) |
SearchableField |
전체 텍스트 검색 가능 필드 |
벡터 검색 필드 |
벡터 임베딩 필드 |
IsKey = true |
문서 키 (필수, 인덱스당 하나) |
IsFilterable = true |
$filter 표현식 활성화 |
정렬 가능 = true |
$orderby 활성화 |
IsFacetable = true |
패싯 탐색 활성화 |
IsHidden = true |
결과에서 제외 |
분석기 이름 |
텍스트 분석기 지정 |
오류 처리
using Azure;
try
{
var results = await searchClient.SearchAsync("query");
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("인덱스를 찾을 수 없음");
}
catch (RequestFailedException ex)
{
Console.WriteLine($"검색 오류: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
}
모범 사례
- 프로덕션 환경에서는 API 키 대신
DefaultAzureCredential을사용하십시오 - 유형 안전하게 인덱스를 정의하려면 모델 속성과 함께
FieldBuilder를사용하십시오 - 이멱포텐트 인덱스 생성을 위해
CreateOrUpdateIndexAsync를사용하세요 - 처리량을 높이기 위해문서 작업을 일괄 처리하십시오
-
Select를사용하여 필요한 필드만 반환 - 자연어 쿼리를 위해시맨틱 검색을 구성하십시오
- 최상의 관련성을 위해벡터 + 키워드 + 시맨틱 검색을 결합하십시오
참조 파일
| 파일 | 목차 |
|---|---|
| references/vector-search.md | 벡터 검색, 하이브리드 검색, 벡터화 도구 |
| references/semantic-search.md | 의미 기반 순위 지정, 캡션, 답변 |
---
name: azure-search-documents-dotnet
description: Build search applications with full-text, vector, semantic, and hybrid search using the Azure AI Search SDK for .NET.
license: MIT
---
# Azure.Search.Documents (.NET)
Build search applications with full-text, vector, semantic, and hybrid search capabilities.
## Installation
```bash
dotnet add package Azure.Search.Documents
dotnet add package Azure.Identity
```
**Current Versions**: Stable v11.7.0, Preview v11.8.0-beta.1
## Environment Variables
```bash
SEARCH_ENDPOINT=https://<search-service>.search.windows.net # Required: search service endpoint
SEARCH_INDEX_NAME=<index-name> # Required: search index name
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
SEARCH_API_KEY=<api-key> # Only required for AzureKeyCredential auth
```
## Authentication
**Microsoft Entra Token Credential**:
```csharp
using Azure.Identity;
using Azure.Search.Documents;
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes
// var credential = new ManagedIdentityCredential();
var client = new SearchClient(
new Uri(Environment.GetEnvironmentVariable("SEARCH_ENDPOINT")),
Environment.GetEnvironmentVariable("SEARCH_INDEX_NAME"),
credential);
```
**API Key**:
```csharp
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);
```
## Client Selection
| Client | Purpose |
|--------|---------|
| `SearchClient` | Query indexes, upload/update/delete documents |
| `SearchIndexClient` | Create/manage indexes, synonym maps |
| `SearchIndexerClient` | Manage indexers, skillsets, data sources |
## Index Creation
### Using FieldBuilder (Recommended)
```csharp
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);
```
### Manual Field Definition
```csharp
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"
}
}
};
```
## Document Operations
```csharp
var searchClient = new SearchClient(endpoint, indexName, credential);
// Upload (add new)
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);
```
## Search Patterns
### Basic Search
```csharp
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})");
}
```
### Faceted Search
```csharp
var options = new SearchOptions
{
Facets = { "rating,count:5", "category" }
};
var results = await searchClient.SearchAsync<Hotel>("*", options);
foreach (var facet in results.Value.Facets["rating"])
{
Console.WriteLine($"Rating {facet.Value}: {facet.Count}");
}
```
### Autocomplete and Suggestions
```csharp
// 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);
```
## Vector Search
See [references/vector-search.md](references/vector-search.md) for detailed patterns.
```csharp
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);
```
## Semantic Search
See [references/semantic-search.md](references/semantic-search.md) for detailed patterns.
```csharp
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}");
}
```
## Hybrid Search (Vector + Keyword + Semantic)
```csharp
var vectorQuery = new VectorizedQuery(embedding)
{
KNearestNeighborsCount = 5,
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);
```
## Field Attributes Reference
| Attribute | Purpose |
|-----------|---------|
| `SimpleField` | Non-searchable field (filters, sorting, facets) |
| `SearchableField` | Full-text searchable field |
| `VectorSearchField` | Vector embedding field |
| `IsKey = true` | Document key (required, one per index) |
| `IsFilterable = true` | Enable $filter expressions |
| `IsSortable = true` | Enable $orderby |
| `IsFacetable = true` | Enable faceted navigation |
| `IsHidden = true` | Exclude from results |
| `AnalyzerName` | Specify text analyzer |
## Error Handling
```csharp
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}");
}
```
## Best Practices
1. **Use `DefaultAzureCredential`** over API keys for production
2. **Use `FieldBuilder`** with model attributes for type-safe index definitions
3. **Use `CreateOrUpdateIndexAsync`** for idempotent index creation
4. **Batch document operations** for better throughput
5. **Use `Select`** to return only needed fields
6. **Configure semantic search** for natural language queries
7. **Combine vector + keyword + semantic** for best relevance
## Reference Files
| File | Contents |
|------|----------|
| [references/vector-search.md](references/vector-search.md) | Vector search, hybrid search, vectorizers |
| [references/semantic-search.md](references/semantic-search.md) | Semantic ranking, captions, answers |
모든 파일
0개 파일azure-search-documents-dotnet 설치
스킬 파일을 다운로드하여 .claude/skills/ 디렉터리에 압축을 풀어주세요.
ZIP 다운로드저장소를 클론하고 스킬 파일을 프로젝트에 복사하세요.
git clone https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-dotnet/skills/azure-search-documents-dotnet # Copy SKILL.md to your .claude/skills/ directory
복사





집
