Dans la première partie de cet article, nous avons brièvement examiné deux services de recherche populaires : Microsoft (MS) Azure Search et Elasticsearch. Dans cette partie, nous allons montrer en pratique comment utiliser certaines fonctionnalités couramment employées d’Azure Search, à travers l’exemple de la recherche de restaurants.
Aperçu du portail Azure et création d’une ressource Azure Search
Nous vous guiderons dans la création d’un compte gratuit Azure (avec un bref aperçu du portail Azure lui-même), d’une ressource de recherche et sur la manière de les utiliser. MS Azure offre la possibilité de créer un compte d’essai gratuit avec 200 $ de crédit pendant 30 jours, 12 mois de services gratuits populaires et, bien sûr, plus de 25 services toujours gratuits. Vous devrez fournir un numéro de téléphone, une carte de crédit ou de débit, et un nom d’utilisateur de compte MS pour commencer à l’utiliser. Gardez également à l’esprit que pour créer un compte, vous devez avoir un peu d’argent sur votre carte, car vous pourriez voir une retenue de vérification d’un dollar sur votre compte de carte de crédit, qui est supprimée dans les trois à cinq jours. Sur la page « Foire aux questions sur le compte Azure gratuit », vous pouvez en savoir un peu plus sur les services fournis et les comptes d’essai.
Après vous être enregistré sur le portail, vous verrez votre tableau de bord. Dans le coin supérieur droit, vous trouverez de courtes informations sur votre compte (e-mail et avatar) ; en cliquant dessus, vous pourrez consulter vos paramètres, vos informations de facturation, etc. Pour créer une nouvelle ressource de recherche, vous devez cliquer sur l’option du menu de gauche « Créer une ressource », qui se trouve en haut de la liste. En cliquant sur cette option, vous accéderez au marketplace, où vous pourrez trouver le service souhaité grâce à un champ de recherche. Après avoir sélectionné « Ressource Azure Search », vous verrez un formulaire avec les champs suivants :
- URL – Vous devez fournir un nom de service, qui fait également partie du point de terminaison de l’URL sur lequel les appels API sont effectués. Par exemple, notre point de terminaison de test sera https://intro.search.windows.net, donc dans le champ URL, nous avons entré « intro ».
- Abonnement – Sélectionnez l’abonnement souhaité (vous pouvez en avoir plusieurs). Si vous créez un compte d’essai gratuit, vous verrez l’abonnement « Essai gratuit » sélectionné. Azure Search peut détecter automatiquement Azure Table et Blob Storage, SQL Database et Azure Cosmos DB pour l’indexation, mais uniquement pour les services au sein du même abonnement.
- Groupe de ressources est une collection de services et de ressources Azure utilisés ensemble. Par exemple, si vous utilisez Azure Search pour indexer une base de données SQL, les deux services feront partie du même groupe de ressources. Si vous venez de créer un compte, vous pouvez cliquer sur le bouton « Créer nouveau » sous le champ et créer le groupe de ressources en saisissant un nom.
- Emplacement, c’est là qu’un service Azure sera hébergé. Et pour référence future, vous devez garder à l’esprit que les prix peuvent différer selon la géographie, et, si vous prévoyez d’utiliser la recherche cognitive, vous devez choisir une région où la fonctionnalité est disponible.
- Niveau tarifaire – Actuellement, les niveaux tarifaires Gratuit, Basique et Standard sont proposés. Chacun d’eux a sa propre capacité et ses limites. Pour cet exemple, nous choisirons le niveau tarifaire « Gratuit », limité à trois index, trois sources de données et trois indexeurs. Pour vos besoins futurs, vous devez vous rappeler qu’un niveau tarifaire ne peut pas être modifié une fois le service créé. Si vous avez besoin d’un niveau supérieur ou inférieur plus tard, vous devrez recréer le service. Consultez les instructions « Choisir un niveau tarifaire ou un SKU pour Azure Search » du site officiel pour plus d’informations.
Les captures d’écran ci-dessous montrent un bref processus de création :

Après avoir cliqué sur le bouton « Créer », vous verrez la ressource créée. Cliquez dessus pour obtenir des informations détaillées, comme indiqué ci-dessous :

Sur la page d’aperçu du service, vous pouvez localiser le point de terminaison de l’URL, les informations d’utilisation, les journaux d’activité, les clés et de nombreuses autres informations utiles. Vous aurez également besoin de vos clés, que vous pouvez trouver en cliquant sur la section « Clés » dans le volet de navigation. Lors de la création de notre projet de test, vous devrez copier l’une des clés d’administrateur (elles sont équivalentes), car cela est nécessaire pour créer, mettre à jour et supprimer des objets sur le service.
Alors, créons le projet et commençons à explorer les fonctionnalités d’Azure Search. Nous ajouterons de nouvelles fonctions progressivement afin de passer en revue toutes les étapes de manière cohérente et d’obtenir une compréhension maximale de ce qui est fait.
Création du projet et de l’index
Les fonctionnalités d’Azure Search sont exposées via une API REST ou un SDK .NET. Dans cet exemple, nous utiliserons le SDK .NET d’Azure Search, qui prend en charge les applications ciblant .NET Framework 4.5.2 et supérieur, ainsi que .NET Core. Vous pouvez choisir celui qui vous convient le mieux, mais dans l’exemple, nous créerons un projet .NET Core :
- Ouvrez Visual Studio ;
- Cliquez sur l’option de menu « Fichier », puis sélectionnez les options « Nouveau » et « Projet » ;
- Choisissez le type « Application console (.NET Core) » ;
- Entrez le nom de votre projet, son emplacement et cliquez sur « OK ».
Le nom de notre projet sera « AzureSearchIntro », et voici la capture d’écran de l’étape de création du projet :

Comme nous allons utiliser le SDK .NET, nous devons maintenant le télécharger via le gestionnaire de packages NuGet. Pour ce faire, faites simplement un clic droit sur le nom du projet, sélectionnez « Gérer les packages NuGet… », basculez vers l’onglet « Parcourir » et recherchez le package « Microsoft.Azure.Search ».
Nous ajouterons également un fichier de configuration pour conserver toutes les clés au même endroit. Pour cela, vous devez :
- Ajouter les packages NuGet suivants :
- Microsoft.Extensions.Configuration ;
- Microsoft.Extensions.Configuration.FileExtensions ;
- Microsoft.Extensions.Configuration.Json ;
- Créer un fichier de configuration JSON (JavaScript) : faites un clic droit sur le nom du projet, sélectionnez « Ajouter », puis « Nouvel élément… », choisissez le type « Fichier de configuration JSON (JavaScript) » et nommez-le (par exemple, « appsettings.json ») ;
- Assurez-vous que la propriété « Copier dans le répertoire de sortie » du fichier appsettings.json est définie sur « Copier si plus récent » afin que l’application puisse y accéder lorsqu’elle est publiée.
Entrez votre nom de service et votre clé API afin que le fichier de configuration ressemble à ceci :
{
"SearchServiceName": "intro",
"SearchServiceAPIKey": "[API key]"
}
Après cela, nous devons créer un modèle qui représentera un document dans l’index. Ajoutez une nouvelle classe (clic droit sur le nom du projet, sélectionnez « Ajouter », puis « Nouvel élément… », choisissez le type « Classe » et nommez-la « Restaurant »). Vous pouvez entrer n’importe quel nom au lieu de celui indiqué dans l’exemple, gardez simplement à l’esprit cela lors de la copie du code. Voici le code de ce fichier, que vous devez insérer :
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Spatial;
using System.ComponentModel.DataAnnotations;
namespace AzureSearchIntro {
[SerializePropertyNamesAsCamelCase]
public class Restaurant {
[Key]
[IsFilterable]
public string RestaurantId { get; set; }
[IsSearchable, IsFilterable, IsSortable]
public string Name { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public double? Rating { get; set; }
[IsFilterable, IsSortable]
public GeographyPoint Location { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public int? WorkingHoursStart { get; set; }
[IsFilterable, IsSortable, IsFacetable]
public int? WorkingHoursEnd { get; set; }
[IsSearchable, IsFilterable, IsSortable]
public string PhoneNumber { get; set; }
}
}
Comme vous pouvez le voir, chaque propriété publique est décorée d’attributs, dont la définition est décrite ci-dessous :
- IsSearchable marque le champ comme indexable en texte intégral. Cela signifie également qu’il subira une analyse telle que la découpe en mots lors de l’indexation. Par exemple, si vous attribuez à un champ indexable la valeur « azure search », il sera internement divisé en deux jetons distincts : « azure » et « search ».
- IsFilterable permet de référencer le champ dans les requêtes de filtre. Cet attribut diffère de IsSearchable dans la manière dont les chaînes sont traitées. Les champs filtrables de type Edm.String ou Collection(Edm.String) ne subissent pas de découpe en mots, les comparaisons sont donc des correspondances exactes uniquement. Par exemple, si vous définissez la valeur d’un tel champ sur « azure search », alors en filtrant par « azure », aucune correspondance ne sera trouvée, mais en filtrant par « azure search », des correspondances apparaîtront.
- IsSortable indique si le champ peut être utilisé dans des expressions OrderBy. Par défaut, le système trie les résultats par score, mais selon l’expérience, les utilisateurs voudront trier par des champs dans les documents. Les champs de type Collection(Edm.String) ne peuvent pas être triables.
- IsFacetable est généralement utilisé dans une présentation des résultats de recherche qui comprend le nombre de correspondances par catégorie. Cette option ne peut pas être utilisée avec des champs de type Edm.GeographyPoint.
Vous pouvez trouver plus d’informations sur les attributs dans la documentation officielle. N’oubliez pas non plus que la longueur des champs Edm.String filtrables, triables ou facetables ne peut pas dépasser 32 kilo-octets. Cela est dû au fait que ces champs sont traités comme un seul terme de recherche, et la longueur maximale d’un terme dans Azure Search est de 32 Ko. Vous pouvez stocker plus de texte dans un seul champ de chaîne s’il est exclu de l’index. Le champ est considéré comme exclu lorsqu’il n’a pas les attributs filterable, sortable et facetable (ou dans l’API REST, ils sont explicitement définis sur false). Ceci est utile pour les champs qui ne sont pas utilisés dans les requêtes mais qui sont nécessaires dans les résultats de recherche. L’exclusion des champs de l’index améliore également les performances.
L’attribut `SerializePropertyNamesAsCamelCase` indique en fait au SDK de mapper automatiquement les noms de propriétés en camel case. Il garantit que les noms de propriétés en Pascal case dans la classe modèle sont mappés à des noms de champs en camel case dans l’index.
Lors de la conception de classes de modèles pour les mapper à un index Azure Search, la recommandation officielle de Microsoft est de déclarer les propriétés de types valeur comme bool et int comme nullables. Si vous n’utilisez pas une propriété nullable, vous devez garantir qu’aucun document de votre index ne contient de valeur nulle pour le champ correspondant. Par exemple, tous les types sont nullables dans Azure Search, donc lorsque vous ajoutez un nouveau champ de type Edm.Int32 à un index existant, après avoir mis à jour la définition de l’index, tous les documents auront une valeur nulle pour ce nouveau champ. Si vous utilisez ensuite une classe modèle avec une propriété int non nullable pour ce champ, vous obtiendrez une `JsonSerializationException` lors de la récupération des documents.
Maintenant, mettons à jour le programme principal avec le code suivant :
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Extensions.Configuration;
using System;
namespace AzureSearchIntro {
class Program {
static void Main(string[] args) {
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot configuration = builder.Build();
string searchServiceName = configuration["SearchServiceName"];
string apiKey = configuration["SearchServiceAPIKey"];
SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(apiKey));
if (!serviceClient.Indexes.Exists("restaurants")) {
var restaurantsIndex = new Index() {
Name = "restaurants",
Fields = FieldBuilder.BuildForType()
};
serviceClient.Indexes.Create(restaurantsIndex);
}
Console.WriteLine("{0}", "Terminé. Appuyez sur une touche pour quitter l'application...n");
Console.ReadKey();
}
}
}
Comme vous pouvez le constater, nous récupérons d’abord le nom du service et la clé API du fichier appsettings.json et créons un nouvel objet `SearchServiceClient`, qui permet de gérer les index. Ensuite, le Main vérifie si l’index nommé « restaurants » existe et, si ce n’est pas le cas, le crée. Si vous exécutez cet exemple, vous verrez dans le portail Azure que vous avez maintenant 1 index avec 0 document et 0 taille de stockage.

Remplir l’index avec des documents
La prochaine étape consiste à remplir l’index avec des documents. Le code ci-dessous représente le fichier « Program.cs » et contient le code pour remplir l’index avec des données de test :
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Spatial;
using System;
using System.Linq;
using System.Threading;
namespace AzureSearchIntro {
class Program {
static void Main(string[] args) {
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot configuration = builder.Build();
string searchServiceName = configuration["SearchServiceName"];
string apiKey = configuration["SearchServiceAPIKey"];
SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(apiKey));
if (!serviceClient.Indexes.Exists("restaurants")) {
var restaurantsIndex = new Index() {
Name = "restaurants",
Fields = FieldBuilder.BuildForType()
};
serviceClient.Indexes.Create(restaurantsIndex);
}
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient("restaurants");
if (indexClient.Documents.Count() == 0)
UploadDataToIndex(indexClient);
Console.WriteLine("{0}", "Terminé. Appuyez sur une touche pour quitter l'application...n");
Console.ReadKey();
}
public static void UploadDataToIndex(ISearchIndexClient indexClient) {
var restaurants = new Restaurant[] {
new Restaurant() {
RestaurantId = "1",
Name = "Best restaurant",
Rating = 2.7,
WorkingHoursStart = 8,
WorkingHoursEnd = 22,
PhoneNumber = "1-800-437-4370",
Location = GeographyPoint.Create(47.679512, -122.132441)
},
new Restaurant() {
RestaurantId = "2",
Name = "Italian food",
Rating = 4.8,
WorkingHoursStart = 10,
WorkingHoursEnd = 23,
PhoneNumber = "1-800-477-4777",
Location = GeographyPoint.Create(50.496163, 30.523571)
},
new Restaurant() {
RestaurantId = "3",
Name = "Chinese food",
Rating = 4.9,
WorkingHoursStart = 7,
WorkingHoursEnd = 23,
PhoneNumber = "1-800-480-4800",
Location = GeographyPoint.Create(50.447258, 30.526541)
}
};
var batch = IndexBatch.MergeOrUpload(restaurants);
try {
indexClient.Documents.Index(batch);
}
catch (IndexBatchException e) {
string failedDocuments = String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key));
Console.WriteLine($"Impossible d'indexer les documents suivants : {failedDocuments}");
}
Console.WriteLine("Indexation des documents.n");
Thread.Sleep(2000);
}
}
}
Dans `Main`, nous vérifions si l’index contient des documents. Si ce n’est pas le cas, la méthode `UploadDataToIndex` crée un tableau d’objets `Restaurant`, puis un `IndexBatch` contenant les documents est créé, et l’opération à appliquer au lot est spécifiée. Le lot est ensuite téléchargé dans l’index Azure Search par la méthode `Documents.Index`. Veuillez noter que tous les numéros de téléphone et coordonnées sont fictifs et n’appartiennent à aucun lieu existant. Dans cet exemple, nous utilisons la méthode `MergeOrUpload` pour charger les documents, mais vous pouvez utiliser n’importe quelle méthode appropriée de la liste suivante :
- Upload, le document sera inséré s’il est nouveau et mis à jour (remplacé) s’il est existant. Notez que tous les champs sont remplacés dans le cas d’une mise à jour.
- Merge met à jour un document existant avec les champs spécifiés. Si le document n’existe pas, la fusion échoue. Tout champ spécifié dans la fusion remplacera le champ existant dans le document.
- MergeOrUpload, si le document avec la clé donnée existe déjà dans l’index, il sera remplacé, sinon il sera ajouté.
L’autre chose à remarquer est le bloc catch qui gère une erreur d’indexation. Le service Azure Search peut ne pas réussir à indexer certains des documents du lot si votre service est sous forte charge. La recommandation officielle de Microsoft est de gérer ce cas et de retenter l’indexation des documents échoués, ou de faire autre chose en fonction de vos besoins, par exemple, au moins écrire dans un fichier journal.
Recherche de documents
La dernière étape, et la plus intéressante, consiste à rechercher des documents dans l’index. Mettez à jour le fichier « Program.cs » avec le code suivant :
using Microsoft.Azure.Search;
using Microsoft.Azure.Search.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Spatial;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AzureSearchIntro {
class Program {
static void Main(string[] args) {
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot configuration = builder.Build();
string searchServiceName = configuration["SearchServiceName"];
string apiKey = configuration["SearchServiceAPIKey"];
SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(apiKey));
if (!serviceClient.Indexes.Exists("restaurants")) {
var restaurantsIndex = new Index() {
Name = "restaurants",
Fields = FieldBuilder.BuildForType()
};
serviceClient.Indexes.Create(restaurantsIndex);
}
ISearchIndexClient indexClient = serviceClient.Indexes.GetClient("restaurants");
if (indexClient.Documents.Count() == 0)
UploadDataToIndex(indexClient);
Console.WriteLine("Recherche...n");
DocumentSearchResult results = SearchRestaurants(indexClient).GetAwaiter().GetResult();
Console.WriteLine($"Nombre de résultats : {results.Count}n");
foreach (var item in results.Results) {
Console.WriteLine($"Score : {item.Score}");
Console.WriteLine($"Nom : {item.Document.Name}");
Console.WriteLine($"Note : {item.Document.Rating}");
Console.WriteLine($"Heure d'ouverture : {item.Document.WorkingHoursStart}");
Console.WriteLine($"Heure de fermeture : {item.Document.WorkingHoursEnd}");
Console.WriteLine($"Numéro de téléphone : {item.Document.PhoneNumber}");
Console.WriteLine("------------------------");
}
Console.WriteLine("Terminé. Appuyez sur une touche pour quitter l'application...n");
Console.ReadKey();
}
public static void UploadDataToIndex(ISearchIndexClient indexClient) {
var restaurants = new Restaurant[] {
new Restaurant() {
RestaurantId = "1",
Name = "Best restaurant",
Rating = 2.7,
WorkingHoursStart = 8,
WorkingHoursEnd = 22,
PhoneNumber = "1-800-437-4370",
Location = GeographyPoint.Create(47.679512, -122.132441)
},
new Restaurant() {
RestaurantId = "2",
Name = "Italian food",
Rating = 4.8,
WorkingHoursStart = 10,
WorkingHoursEnd = 23,
PhoneNumber = "1-800-477-4777",
Location = GeographyPoint.Create(50.496163, 30.523571)
},
new Restaurant() {
RestaurantId = "3",
Name = "Chinese food",
Rating = 4.9,
WorkingHoursStart = 7,
WorkingHoursEnd = 23,
PhoneNumber = "1-800-480-4800",
Location = GeographyPoint.Create(50.447258, 30.526541)
}
};
var batch = IndexBatch.MergeOrUpload(restaurants);
try {
indexClient.Documents.Index(batch);
}
catch (IndexBatchException e) {
string failedDocuments = String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key));
Console.WriteLine($"Impossible d'indexer les documents suivants : {failedDocuments}");
}
Console.WriteLine("Indexation des documents.n");
Thread.Sleep(2000);
}
public static async Task<DocumentSearchResult> SearchRestaurants(ISearchIndexClient indexClient) {
List resultsList = new List();
var parameters = new SearchParameters();
parameters.Filter = "rating gt 4 and (geo.distance(location, geography'POINT(30.521541 50.444158)') le 30)";
parameters.OrderBy = new[] { "rating desc" };
parameters.QueryType = QueryType.Full;
parameters.SearchMode = SearchMode.All;
parameters.IncludeTotalResultCount = true;
parameters.Top = 10;
string azureSearch = $"(name:(('/.*food.*/'))) || (name:(('/.*Chinese.*/')))";
try {
var docResults = await indexClient.Documents.SearchAsync(azureSearch, parameters);
return docResults;
}
catch (Exception e) {
}
return null;
}
}
}
Le prochain exemple renverra les résultats de recherche suivants après exécution :
Recherche...
Nombre de résultats : 2
Score : 1.4142135
Nom : Chinese food
Note : 4.9
Heure d'ouverture : 7
Heure de fermeture : 23
Numéro de téléphone : 1-800-480-4800
------------------------
Score : 0.35355338
Nom : Italian food
Note : 4.8
Heure d'ouverture : 10
Heure de fermeture : 23
Numéro de téléphone : 1-800-477-4777
------------------------
Terminé. Appuyez sur une touche pour quitter l'application...
Examinons en détail la méthode « SearchRestaurants ». Une requête accepte plusieurs paramètres qui fournissent les critères de requête et spécifient également le comportement de la recherche :
-
- Les expressions Filter contraignent la recherche à des champs spécifiques ou ajoutent des critères de correspondance. Elles peuvent être exécutées de manière autonome en tant que requête entièrement formulée, ou clarifier une requête comportant des paramètres supplémentaires. Nous examinerons les expressions de filtre plus loin, après l’aperçu des paramètres de recherche.
- Le paramètre OrderBy accepte une liste de critères de tri, où chaque critère peut être le nom d’un champ triable, un appel aux fonctions geo.distance ou search.score. Vous pouvez utiliser asc (ordre par défaut) ou desc pour spécifier l’ordre de tri. L’ordre des expressions détermine l’ordre de tri final.
- Le QueryType spécifie quel analyseur doit être utilisé. Peut être de deux types :
- l’analyseur de requête simple par défaut, optimal pour la recherche en texte intégral (`QueryType.Simple`) ;
- l’analyseur de requête Lucene complet utilisé pour les constructions de requêtes avancées telles que les expressions régulières, la recherche de proximité, la recherche floue et par caractère générique, etc. (`QueryType.Full`).
- Le SearchMode indique si n’importe lequel (« SearchMode.Any » option par défaut) ou si tous (« SearchMode.All ») les termes de recherche doivent être mis en correspondance pour que le document soit considéré comme une correspondance.
- IncludeTotalResultCount spécifie si le nombre total de résultats est nécessaire ou non dans la réponse. La valeur par défaut est false.
- Top spécifie combien d’éléments doivent être retournés. Peut également être utilisé avec le paramètre « Skip » pour la pagination, qui spécifie combien d’éléments doivent être ignorés.
- SearchFields est un paramètre optionnel et est utilisé pour restreindre la recherche à des champs spécifiques. Ainsi, vous pouvez remplacer la variable azureSearch, montrée ci-dessus, par les quelques lignes de code suivantes :
parameters.SearchFields = new[] { "name" };
string azureSearch = $"('/.*food.*/') || ('/.*Chinese.*/')";
Le paramètre de filtre est la base de plusieurs expériences de recherche, telles que la recherche géolocalisée, la navigation par facettes, etc. Comme mentionné précédemment, nous examinons la liste des opérateurs de filtre utilisables :
- Opérateurs logiques (`and`, `or`, `not`) ;
- Expressions de comparaison :
- `eq` – égal à ;
- `ne` – différent de ;
- `gt` – supérieur à ;
- `lt` – inférieur à ;
- `ge` – supérieur ou égal à ;
- `le` – inférieur ou égal à.
- Constantes des types pris en charge et références aux noms de champs (avec l’attribut Filterable) ;
- `any` et `all`. Les deux sont pris en charge pour les champs de type `Collection(Edm.String)` mais peuvent être utilisés avec des expressions différentes :
- `any` ne peut être utilisé qu’avec des expressions d’égalité simples ou avec une fonction `search.in` ;
- `all` ne peut être utilisé qu’avec des expressions d’inégalité simples ou avec une fonction `not search.in`.
- Fonctions géospatiales `geo.distance` et `geo.intersects`, utilisées pour les contrôles de recherche « trouver près de moi » ou basés sur une carte ;
- La fonction `search.in`, qui vérifie si un champ de chaîne donné est égal à l’une des valeurs d’une liste donnée ;
- La fonction `search.ismatch` évalue une requête de recherche dans le cadre d’une expression de filtre et renvoie tous les documents qui correspondent à cette requête ;
- La fonction `search.ismatchscoring` est très similaire à la fonction `search.ismatch`. La seule différence est que le score de pertinence des documents correspondant à la requête `search.ismatchscoring` influencera le score global du document, tandis que dans le cas de `search.ismatch`, le score du document ne sera pas modifié.
Vous pouvez en savoir plus sur les subtilités de chacun des paramètres décrits dans la documentation officielle.
La dernière chose, mais non la moindre, que nous allons aborder est le boost de terme. Il classe un document plus haut s’il contient le terme boosté, par rapport aux documents qui ne contiennent pas le terme. Il diffère des profils de scoring en ce que les profils de scoring boostent certains champs, pas des termes spécifiques. Pour booster un terme, vous devez utiliser le symbole « ^ » avec un facteur de boost (un nombre) à la fin du terme que vous recherchez. Plus le facteur de boost est élevé, plus le terme sera pertinent par rapport aux autres termes de recherche. Par défaut, le facteur de boost est de 1. Il peut être inférieur à 1, mais jamais négatif. L’exemple suivant illustre l’utilisation du boost de terme. Mettez à jour la méthode `SearchRestaurants` avec le code suivant :
public static async Task<DocumentSearchResult> SearchRestaurants(ISearchIndexClient indexClient) {
List resultsList = new List();
var parameters = new SearchParameters();
parameters.Filter = "rating gt 1";
parameters.QueryType = QueryType.Full;
parameters.SearchMode = SearchMode.All;
parameters.IncludeTotalResultCount = true;
parameters.Top = 10;
string azureSearch = $"(name:food^2) || (name:restaurant)";
try {
var docResults = await indexClient.Documents.SearchAsync(azureSearch, parameters);
return docResults;
}
catch (Exception e) {
}
return null;
}
Vous verrez les résultats suivants après l’exécution :
Recherche...
Nombre de résultats : 3
Score : 0.30778623
Nom : Italian food
Note : 4.8
Heure d'ouverture : 10
Heure de fermeture : 23
Numéro de téléphone : 1-800-477-4777
------------------------
Score : 0.30778623
Nom : Chinese food
Note : 4.9
Heure d'ouverture : 7
Heure de fermeture : 23
Numéro de téléphone : 1-800-480-4800
------------------------
Score : 0.07599751
Nom : Best restaurant
Note : 2.7
Heure d'ouverture : 8
Heure de fermeture : 22
Numéro de téléphone : 1-800-437-4370
------------------------
Terminé. Appuyez sur une touche pour quitter l'application...
Comme vous pouvez le constater, les documents qui contiennent le terme boosté « food » sont en tête des résultats. Si vous supprimez le boost de terme, la variable azureSearch ressemblera à ceci :
string azureSearch = $"(name:food) || (name:restaurant)";
Vous verrez d’autres résultats où les trois documents sont classés dans un ordre différent :
Recherche...
Nombre de résultats : 3
Score : 0.35786763
Nom : Best restaurant
Note : 2.7
Heure d'ouverture : 8
Heure de fermeture : 22
Numéro de téléphone : 1-800-437-4370
------------------------
Score : 0.18116833
Nom : Italian food
Note : 4.8
Heure d'ouverture : 10
Heure de fermeture : 23
Numéro de téléphone : 1-800-477-4777
------------------------
Score : 0.18116833
Nom : Chinese food
Note : 4.9
Heure d'ouverture : 7
Heure de fermeture : 23
Numéro de téléphone : 1-800-480-4800
------------------------
Terminé. Appuyez sur une touche pour quitter l'application...
Le boost de terme peut être utile lorsque vous souhaitez implémenter une recherche dans laquelle l’utilisateur pourra entrer quelques mots séparés et que l’ordre de ces mots indiquera la pertinence de chaque mot. Par exemple, si l’utilisateur sur le site d’un cinéma saisit « horreur, thriller, drame » dans la boîte de recherche, avec le boost de terme, les films d’horreur apparaîtront en haut des résultats, puis les thrillers, et enfin les drames.
Et ce n’est même pas la moitié…
Dans cet article, nous avons examiné en pratique quelques-unes des utilisations les plus courantes d’Azure Search. Les fonctionnalités proposées sont beaucoup plus larges et, nous l’espérons, en lisant cet article, vous aurez compris les principes de base de son utilisation et serez inspiré pour approfondir vos études.
À propos de Redwerk
Notre entreprise est spécialisée dans le développement de logiciels personnalisés pour des secteurs tels que le commerce électronique, l’automatisation des entreprises, la santé électronique, les médias et le divertissement, le gouvernement électronique, le développement de jeux, les startups et l’innovation. L’une des technologies que nous utilisons et proposons aux entreprises est le développement d’applications Azure. Notre équipe de développement dédiée a déjà livré des dizaines de solutions réussies via la technologie Azure. Découvrez la puissance des plateformes SaaS avec nous.