azure-search-documents-dotnet

npx skills add https://github.com/microsoft/skills --skill azure-search-documents-dotnet

Azure.Search.Documents (.NET)

Créez des applications de recherche avec des capacités de recherche textuelle, vectorielle, sémantique et hybride.

Installation

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

Versions actuelles : Stable v11.7.0, Preview v11.8.0-beta.1

Variables d'environnement

SEARCH_ENDPOINT=https://<search-service>.search.windows.net
SEARCH_INDEX_NAME=<index-name>
# Pour l'authentification par clé API (non recommandée en production)
SEARCH_API_KEY=<api-key>

Authentification

DefaultAzureCredential (préféré) :

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);

Clé 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);

Sélection du client

Client Objectif
SearchClient Interroger les index, charger/mettre à jour/supprimer des documents
SearchIndexClient Créer/gérer les index, mappages de synonymes
SearchIndexerClient Gérer les indexeurs, skillsets, sources de données

Création d'index

Utilisation de FieldBuilder (recommandé)

using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;

// Définir le modèle avec attributs
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; }
}

// Créer l'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);

Définition manuelle des champs

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"
        }
    }
};

Opérations sur les documents

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

// Charger (ajouter nouveau)
var hotels = new[] { new Hotel { HotelId = "1", HotelName = "Hotel A" } };
await searchClient.UploadDocumentsAsync(hotels);

// Fusionner (mettre à jour existant)
await searchClient.MergeDocumentsAsync(hotels);

// Fusionner ou charger (upsert)
await searchClient.MergeOrUploadDocumentsAsync(hotels);

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

// Opérations par lot
var batch = IndexDocumentsBatch.Create(
    IndexDocumentsAction.Upload(hotel1),
    IndexDocumentsAction.Merge(hotel2),
    IndexDocumentsAction.Delete(hotel3));
await searchClient.IndexDocumentsAsync(batch);

Modèles de recherche

Recherche basique

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})");
}

Recherche facettée

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}");
}

Autocomplétion et suggestions

// Autocomplétion
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);

Recherche vectorielle

Voir references/vector-search.md pour les modèles détaillés.

using Azure.Search.Documents.Models;

// Recherche vectorielle pure
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);

Recherche sémantique

Voir references/semantic-search.md pour les modèles détaillés.

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);

// Accéder aux réponses sémantiques
foreach (var answer in results.Value.SemanticSearch.Answers)
{
    Console.WriteLine($"Answer: {answer.Text} (Score: {answer.Score})");
}

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

Recherche hybride (vectorielle + mots-clés + sémantique)

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 }
    }
};

// Combine la recherche par mots-clés, la recherche vectorielle et le classement sémantique
var results = await searchClient.SearchAsync<Hotel>("luxury beachfront", options);

Référence des attributs de champ

Attribut Objectif
SimpleField Champ non interrogeable (filtres, tri, facettes)
SearchableField Champ interrogeable en texte intégral
VectorSearchField Champ d'embedding vectoriel
IsKey = true Clé du document (obligatoire, un par index)
IsFilterable = true Activer les expressions $filter
IsSortable = true Activer $orderby
IsFacetable = true Activer la navigation facettée
IsHidden = true Exclure des résultats
AnalyzerName Spécifier l'analyseur de texte

Gestion des erreurs

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}");
}

Bonnes pratiques

  1. Utiliser DefaultAzureCredential plutôt que les clés API en production
  2. Utiliser FieldBuilder avec les attributs de modèle pour les définitions d'index type-safe
  3. Utiliser CreateOrUpdateIndexAsync pour la création idempotente d'index
  4. Regrouper les opérations de documents pour un meilleur débit
  5. Utiliser Select pour retourner uniquement les champs nécessaires
  6. Configurer la recherche sémantique pour les requêtes en langage naturel
  7. Combiner vectoriel + mots-clés + sémantique pour la meilleure pertinence

Fichiers de référence

Fichier Contenu
references/vector-search.md Recherche vectorielle, recherche hybride, vectoriseurs
references/semantic-search.md Classement sémantique, captions, réponses

Skills similaires