Quickstart: Zoeken in volledige tekst met behulp van de Azure SDK's
Meer informatie over het gebruik van de clientbibliotheek Azure.Search.Documents om een zoekindex te maken, laden en er query's op uit te voeren met voorbeeldgegevens voor zoeken in volledige tekst. Zoeken in volledige tekst maakt gebruik van Apache Lucene voor indexering en query's en een BM25-classificatie-algoritme voor scoreresultaten.
In deze quickstart wordt een kleine index voor hotels-quickstart gemaakt en opgevraagd die gegevens over vier hotels bevat.
Tip
U kunt de broncode downloaden om te beginnen met een voltooid project of deze stappen volgen om uw eigen project te maken.
Vereisten
- Een actief Azure-abonnement - Een gratis abonnement maken
- Een Azure AI-Search-service. Maak een service als u er nog geen hebt. U kunt een gratis laag gebruiken voor deze quickstart.
Vereisten voor Microsoft Entra-id
Voor de aanbevolen sleutelloze verificatie met Microsoft Entra-id moet u het volgende doen:
- Installeer de Azure CLI die wordt gebruikt voor sleutelloze verificatie met Microsoft Entra-id.
- Wijs zowel de rollen als de
Search Service Contributor
Search Index Data Contributor
rollen toe aan uw gebruikersaccount. U kunt rollen toewijzen in Azure Portal onder Toegangsbeheer (IAM)>Roltoewijzing toevoegen. Zie Verbinding maken met Azure AI Search met behulp van rollen voor meer informatie.
Resourcegegevens ophalen
U moet de volgende informatie ophalen om uw toepassing te verifiëren met uw Azure AI-Search-service:
Naam van de variabele | Weergegeven als |
---|---|
SEARCH_API_ENDPOINT |
Deze waarde vindt u in Azure Portal. Selecteer uw zoekservice en selecteer vervolgens in het linkermenu Overzicht. De URL-waarde onder Essentials is het eindpunt dat u nodig hebt. Een eindpunt ziet er bijvoorbeeld uit als https://mydemo.search.windows.net . |
Meer informatie over sleutelloze verificatie en het instellen van omgevingsvariabelen.
Instellingen
Maak een nieuwe map
full-text-quickstart
die de toepassing bevat en open Visual Studio Code in die map met de volgende opdracht:mkdir full-text-quickstart && cd full-text-quickstart
Maak een nieuwe consoletoepassing met de volgende opdracht:
dotnet new console
Installeer de Azure AI Search-clientbibliotheek (Azure.Search.Documents) voor .NET met:
dotnet add package Azure.Search.Documents
Voor de aanbevolen sleutelloze verificatie met Microsoft Entra ID installeert u het Azure.Identity-pakket met:
dotnet add package Azure.Identity
Meld u met de volgende opdracht aan bij Azure voor de aanbevolen sleutelloze verificatie met Microsoft Entra ID:
az login
Een zoekindex maken, laden en er query's op uitvoeren
In de vorige sectie voor het instellen hebt u een nieuwe consoletoepassing gemaakt en de Azure AI Search-clientbibliotheek geïnstalleerd.
In deze sectie voegt u code toe om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U voert het programma uit om de resultaten in de console te zien. Zie de uitleg van de codesectie voor een gedetailleerde uitleg van de code .
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het DefaultAzureCredential
object vervangen door een AzureKeyCredential
object.
Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
Plak in Program.cs de volgende code. Bewerk de
serviceName
enapiKey
variabelen met de naam en de beheer-API-sleutel van uw zoekservice.using System; using Azure; using Azure.Identity; using Azure.Search.Documents; using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; using Azure.Search.Documents.Models; namespace AzureSearch.Quickstart { class Program { static void Main(string[] args) { // Your search service endpoint Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/"); // Use the recommended keyless credential instead of the AzureKeyCredential credential. DefaultAzureCredential credential = new(); //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key"); // Create a SearchIndexClient to send create/delete index commands SearchIndexClient searchIndexClient = new SearchIndexClient(serviceEndpoint, credential); // Create a SearchClient to load and query documents string indexName = "hotels-quickstart"; SearchClient searchClient = new SearchClient(serviceEndpoint, indexName, credential); // Delete index if it exists Console.WriteLine("{0}", "Deleting index...\n"); DeleteIndexIfExists(indexName, searchIndexClient); // Create index Console.WriteLine("{0}", "Creating index...\n"); CreateIndex(indexName, searchIndexClient); SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName); // Load documents Console.WriteLine("{0}", "Uploading documents...\n"); UploadDocuments(ingesterClient); // Wait 2 secondsfor indexing to complete before starting queries (for demo and console-app purposes only) Console.WriteLine("Waiting for indexing...\n"); System.Threading.Thread.Sleep(2000); // Call the RunQueries method to invoke a series of queries Console.WriteLine("Starting queries...\n"); RunQueries(searchClient); // End the program Console.WriteLine("{0}", "Complete. Press any key to end this program...\n"); Console.ReadKey(); } // Delete the hotels-quickstart index to reuse its name private static void DeleteIndexIfExists(string indexName, SearchIndexClient searchIndexClient) { searchIndexClient.GetIndexNames(); { searchIndexClient.DeleteIndex(indexName); } } // Create hotels-quickstart index private static void CreateIndex(string indexName, SearchIndexClient searchIndexClient) { FieldBuilder fieldBuilder = new FieldBuilder(); var searchFields = fieldBuilder.Build(typeof(Hotel)); var definition = new SearchIndex(indexName, searchFields); var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" }); definition.Suggesters.Add(suggester); searchIndexClient.CreateOrUpdateIndex(definition); } // Upload documents in a single Upload request. private static void UploadDocuments(SearchClient searchClient) { IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create( IndexDocumentsAction.Upload( new Hotel() { HotelId = "1", HotelName = "Secret Point Motel", Description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.", DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.", Category = "Boutique", Tags = new[] { "pool", "air conditioning", "concierge" }, ParkingIncluded = false, LastRenovationDate = new DateTimeOffset(1970, 1, 18, 0, 0, 0, TimeSpan.Zero), Rating = 3.6, Address = new Address() { StreetAddress = "677 5th Ave", City = "New York", StateProvince = "NY", PostalCode = "10022", Country = "USA" } }), IndexDocumentsAction.Upload( new Hotel() { HotelId = "2", HotelName = "Twin Dome Motel", Description = "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.", DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.", Category = "Boutique", Tags = new[] { "pool", "free wifi", "concierge" }, ParkingIncluded = false, LastRenovationDate = new DateTimeOffset(1979, 2, 18, 0, 0, 0, TimeSpan.Zero), Rating = 3.60, Address = new Address() { StreetAddress = "140 University Town Center Dr", City = "Sarasota", StateProvince = "FL", PostalCode = "34243", Country = "USA" } }), IndexDocumentsAction.Upload( new Hotel() { HotelId = "3", HotelName = "Triple Landscape Hotel", Description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.", DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.", Category = "Resort and Spa", Tags = new[] { "air conditioning", "bar", "continental breakfast" }, ParkingIncluded = true, LastRenovationDate = new DateTimeOffset(2015, 9, 20, 0, 0, 0, TimeSpan.Zero), Rating = 4.80, Address = new Address() { StreetAddress = "3393 Peachtree Rd", City = "Atlanta", StateProvince = "GA", PostalCode = "30326", Country = "USA" } }), IndexDocumentsAction.Upload( new Hotel() { HotelId = "4", HotelName = "Sublime Cliff Hotel", Description = "Sublime Cliff Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace.", DescriptionFr = "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.", Category = "Boutique", Tags = new[] { "concierge", "view", "24-hour front desk service" }, ParkingIncluded = true, LastRenovationDate = new DateTimeOffset(1960, 2, 06, 0, 0, 0, TimeSpan.Zero), Rating = 4.60, Address = new Address() { StreetAddress = "7400 San Pedro Ave", City = "San Antonio", StateProvince = "TX", PostalCode = "78216", Country = "USA" } }) ); try { IndexDocumentsResult result = searchClient.IndexDocuments(batch); } catch (Exception) { // If for some reason any documents are dropped during indexing, you can compensate by delaying and // retrying. This simple demo just logs the failed document keys and continues. Console.WriteLine("Failed to index some of the documents: {0}"); } } // Run queries, use WriteDocuments to print output private static void RunQueries(SearchClient searchClient) { SearchOptions options; SearchResults<Hotel> response; // Query 1 Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n"); options = new SearchOptions() { IncludeTotalCount = true, Filter = "", OrderBy = { "" } }; options.Select.Add("HotelId"); options.Select.Add("HotelName"); options.Select.Add("Rating"); response = searchClient.Search<Hotel>("*", options); WriteDocuments(response); // Query 2 Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n"); options = new SearchOptions() { Filter = "Rating gt 4", OrderBy = { "Rating desc" } }; options.Select.Add("HotelId"); options.Select.Add("HotelName"); options.Select.Add("Rating"); response = searchClient.Search<Hotel>("hotels", options); WriteDocuments(response); // Query 3 Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n"); options = new SearchOptions() { SearchFields = { "Tags" } }; options.Select.Add("HotelId"); options.Select.Add("HotelName"); options.Select.Add("Tags"); response = searchClient.Search<Hotel>("pool", options); WriteDocuments(response); // Query 4 - Use Facets to return a faceted navigation structure for a given query // Filters are typically used with facets to narrow results on OnClick events Console.WriteLine("Query #4: Facet on 'Category'...\n"); options = new SearchOptions() { Filter = "" }; options.Facets.Add("Category"); options.Select.Add("HotelId"); options.Select.Add("HotelName"); options.Select.Add("Category"); response = searchClient.Search<Hotel>("*", options); WriteDocuments(response); // Query 5 Console.WriteLine("Query #5: Look up a specific document...\n"); Response<Hotel> lookupResponse; lookupResponse = searchClient.GetDocument<Hotel>("3"); Console.WriteLine(lookupResponse.Value.HotelId); // Query 6 Console.WriteLine("Query #6: Call Autocomplete on HotelName...\n"); var autoresponse = searchClient.Autocomplete("sa", "sg"); WriteDocuments(autoresponse); } // Write search results to console private static void WriteDocuments(SearchResults<Hotel> searchResults) { foreach (SearchResult<Hotel> result in searchResults.GetResults()) { Console.WriteLine(result.Document); } Console.WriteLine(); } private static void WriteDocuments(AutocompleteResults autoResults) { foreach (AutocompleteItem result in autoResults.Results) { Console.WriteLine(result.Text); } Console.WriteLine(); } } }
Maak in dezelfde map een nieuw bestand met de naam Hotel.cs en plak de volgende code. Deze code definieert de structuur van een hoteldocument.
using System; using System.Text.Json.Serialization; using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; namespace AzureSearch.Quickstart { public partial class Hotel { [SimpleField(IsKey = true, IsFilterable = true)] public string HotelId { get; set; } [SearchableField(IsSortable = true)] public string HotelName { get; set; } [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)] public string Description { get; set; } [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)] [JsonPropertyName("Description_fr")] public string DescriptionFr { get; set; } [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public string Category { get; set; } [SearchableField(IsFilterable = true, IsFacetable = true)] public string[] Tags { get; set; } [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public bool? ParkingIncluded { get; set; } [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public DateTimeOffset? LastRenovationDate { get; set; } [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public double? Rating { get; set; } [SearchableField] public Address Address { get; set; } } }
Maak een nieuw bestand met de naam Hotel.cs en plak de volgende code om de structuur van een hoteldocument te definiëren. Kenmerken in het veld bepalen hoe deze worden gebruikt in een toepassing. Het
IsFilterable
-kenmerk moet bijvoorbeeld worden toegewezen aan elk veld dat een filterexpressie ondersteunt.using System; using System.Text.Json.Serialization; using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; namespace AzureSearch.Quickstart { public partial class Hotel { [SimpleField(IsKey = true, IsFilterable = true)] public string HotelId { get; set; } [SearchableField(IsSortable = true)] public string HotelName { get; set; } [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)] public string Description { get; set; } [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)] [JsonPropertyName("Description_fr")] public string DescriptionFr { get; set; } [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public string Category { get; set; } [SearchableField(IsFilterable = true, IsFacetable = true)] public string[] Tags { get; set; } [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public bool? ParkingIncluded { get; set; } [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public DateTimeOffset? LastRenovationDate { get; set; } [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public double? Rating { get; set; } [SearchableField] public Address Address { get; set; } } }
Maak een nieuw bestand met de naam Address.cs en plak de volgende code om de structuur van een adresdocument te definiëren.
using Azure.Search.Documents.Indexes; namespace AzureSearch.Quickstart { public partial class Address { [SearchableField(IsFilterable = true)] public string StreetAddress { get; set; } [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public string City { get; set; } [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public string StateProvince { get; set; } [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public string PostalCode { get; set; } [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)] public string Country { get; set; } } }
Maak een nieuw bestand met de naam Hotel.Methods.cs en plak de volgende code om een
ToString()
onderdrukking voor deHotel
klasse te definiëren.using System; using System.Text; namespace AzureSearch.Quickstart { public partial class Hotel { public override string ToString() { var builder = new StringBuilder(); if (!String.IsNullOrEmpty(HotelId)) { builder.AppendFormat("HotelId: {0}\n", HotelId); } if (!String.IsNullOrEmpty(HotelName)) { builder.AppendFormat("Name: {0}\n", HotelName); } if (!String.IsNullOrEmpty(Description)) { builder.AppendFormat("Description: {0}\n", Description); } if (!String.IsNullOrEmpty(DescriptionFr)) { builder.AppendFormat("Description (French): {0}\n", DescriptionFr); } if (!String.IsNullOrEmpty(Category)) { builder.AppendFormat("Category: {0}\n", Category); } if (Tags != null && Tags.Length > 0) { builder.AppendFormat("Tags: [ {0} ]\n", String.Join(", ", Tags)); } if (ParkingIncluded.HasValue) { builder.AppendFormat("Parking included: {0}\n", ParkingIncluded.Value ? "yes" : "no"); } if (LastRenovationDate.HasValue) { builder.AppendFormat("Last renovated on: {0}\n", LastRenovationDate); } if (Rating.HasValue) { builder.AppendFormat("Rating: {0}\n", Rating); } if (Address != null && !Address.IsEmpty) { builder.AppendFormat("Address: \n{0}\n", Address.ToString()); } return builder.ToString(); } } }
Maak een nieuw bestand met de naam Address.Methods.cs en plak de volgende code om een
ToString()
onderdrukking voor deAddress
klasse te definiëren.using System; using System.Text; using System.Text.Json.Serialization; namespace AzureSearch.Quickstart { public partial class Address { public override string ToString() { var builder = new StringBuilder(); if (!IsEmpty) { builder.AppendFormat("{0}\n{1}, {2} {3}\n{4}", StreetAddress, City, StateProvince, PostalCode, Country); } return builder.ToString(); } [JsonIgnore] public bool IsEmpty => String.IsNullOrEmpty(StreetAddress) && String.IsNullOrEmpty(City) && String.IsNullOrEmpty(StateProvince) && String.IsNullOrEmpty(PostalCode) && String.IsNullOrEmpty(Country); } }
Bouw en voer de toepassing uit met de volgende opdracht:
dotnet run
De uitvoer bevat berichten van Console.WriteLine, met toevoeging van query-informatie en -resultaten.
Uitleg van de code
In de vorige secties hebt u een nieuwe consoletoepassing gemaakt en de Azure AI Search-clientbibliotheek geïnstalleerd. U hebt code toegevoegd om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U hebt het programma uitgevoerd om de resultaten in de console te zien.
In deze sectie wordt de code uitgelegd die u hebt toegevoegd aan de consoletoepassing.
Een zoekclient maken
In Program.cs hebt u twee clients gemaakt:
- SearchIndexClient maakt de index.
- SearchClient laadt en voert query's uit op een bestaande index.
Beide clients hebben het eindpunt van de zoekservice en referenties nodig die eerder zijn beschreven in de sectie resourcegegevens .
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het DefaultAzureCredential
object vervangen door een AzureKeyCredential
object.
Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
static void Main(string[] args)
{
// Your search service endpoint
Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
// Use the recommended keyless credential instead of the AzureKeyCredential credential.
DefaultAzureCredential credential = new();
//AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");
// Create a SearchIndexClient to send create/delete index commands
SearchIndexClient searchIndexClient = new SearchIndexClient(serviceEndpoint, credential);
// Create a SearchClient to load and query documents
string indexName = "hotels-quickstart";
SearchClient searchClient = new SearchClient(serviceEndpoint, indexName, credential);
// REDACTED FOR BREVITY . . .
}
Een index maken
Deze quickstart bouwt een Hotels-index die u laadt met hotelgegevens en voert query's uit op. In deze stap definieert u de velden in de index. Elke definitie bevat een naam, gegevenstype en kenmerken die bepalen hoe het veld wordt gebruikt.
In dit voorbeeld worden synchrone methoden van de bibliotheek Azure.Search.Documents gebruikt voor eenvoud en leesbaarheid. Voor productiescenario's moet u echter asynchrone methoden gebruiken om uw app op een schaalbare en responsieve manier te laten werken. U kunt bijvoorbeeld CreateIndexAsync gebruiken in plaats van CreateIndex.
De structuren definiëren
U hebt twee helperklassen gemaakt, Hotel.cs en Address.cs, om de structuur van een hoteldocument en het bijbehorende adres te definiëren. De Hotel
klasse bevat velden voor een hotel-id, naam, beschrijving, categorie, tags, parkeren, renovatiedatum, classificatie en adres. De Address
klasse bevat velden voor adres, plaats, provincie, postcode en land/regio.
In de clientbibliotheek Azure.Search.Documents kunt u SearchableField en SimpleField gebruiken om velddefinities te stroomlijnen. Beide zijn afgeleiden van een SearchField en kunnen uw code vereenvoudigen:
SimpleField
kan elk gegevenstype zijn, is altijd niet doorzoekbaar (genegeerd voor zoekquery's in volledige tekst) en kan worden opgehaald (niet verborgen). Andere kenmerken zijn standaard uitgeschakeld, maar kunnen wel worden ingeschakeld. U kunt eenSimpleField
gebruiken voor document-id's of velden die alleen worden gebruikt in filters, facetten of scoreprofielen. Als dat het geval is, moet u ervoor zorgen dat u alle kenmerken toepast die nodig zijn voor het scenario, zoalsIsKey = true
voor een document-id. Zie SimpleFieldAttribute.cs in broncode voor meer informatie.SearchableField
moet een tekenreeks zijn die altijd kan worden doorzocht en opgehaald. Andere kenmerken zijn standaard uitgeschakeld, maar kunnen wel worden ingeschakeld. Omdat dit veldtype kan worden doorzocht, worden synoniemen en het volledige gamma van analyse-eigenschappen ondersteund. Zie SearchableFieldAttribute.cs in broncode voor meer informatie.
Ongeacht of u de basis-API van SearchField
of een van de hulpmodellen gebruikt, u moet filter-, facet- en sorteerkenmerken expliciet inschakelen. IsFilterable, IsSortable en IsFacetable moeten bijvoorbeeld expliciet worden toegeschreven, zoals wordt weergegeven in het vorige voorbeeld.
De zoekindex maken
In Program.cs maakt u een SearchIndex-object en roept u vervolgens de methode CreateIndex aan om de index in uw zoekservice uit te drukken. De index bevat ook een SearchSuggester om automatisch aanvullen in te schakelen voor de opgegeven velden.
// Create hotels-quickstart index
private static void CreateIndex(string indexName, SearchIndexClient searchIndexClient)
{
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));
var definition = new SearchIndex(indexName, searchFields);
var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" });
definition.Suggesters.Add(suggester);
searchIndexClient.CreateOrUpdateIndex(definition);
}
Documenten laden
Azure AI Search zoekt naar inhoud die is opgeslagen in de service. In deze stap laadt u JSON-documenten die voldoen aan de hotelindex die u hebt gemaakt.
In Azure AI Search zijn zoekdocumenten gegevensstructuren die zowel invoer zijn voor het indexeren als uitvoeren van query's. Als u de documenten hebt verkregen via een externe gegevensbron, bestaat de documentinvoer mogelijk uit rijen in een database, blobs in Blob Storage of JSON-documenten op een schijf. In dit voorbeeld nemen we de korte route en gaan we JSON-documenten voor vier hotels in de code zelf insluiten.
Bij het uploaden van documenten moet u een IndexDocumentsBatch-object gebruiken. Een IndexDocumentsBatch
object bevat een verzameling acties, die elk een document en een eigenschap bevatten die Azure AI Search vertelt welke actie moet worden uitgevoerd (uploaden, samenvoegen, verwijderen en samenvoegen).
In Program.cs maakt u een matrix met documenten en indexacties en geeft u de matrix vervolgens door aan IndexDocumentsBatch
. De volgende documenten voldoen aan de index hotels-quickstart, zoals gedefinieerd door de hotelklasse.
// Upload documents in a single Upload request.
private static void UploadDocuments(SearchClient searchClient)
{
IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "1",
HotelName = "Stay-Kay City Hotel",
Description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
Category = "Boutique",
Tags = new[] { "pool", "air conditioning", "concierge" },
ParkingIncluded = false,
LastRenovationDate = new DateTimeOffset(1970, 1, 18, 0, 0, 0, TimeSpan.Zero),
Rating = 3.6,
Address = new Address()
{
StreetAddress = "677 5th Ave",
City = "New York",
StateProvince = "NY",
PostalCode = "10022",
Country = "USA"
}
}),
// REDACTED FOR BREVITY
}
Nadat u het IndexDocumentsBatch-object hebt geïnitialiseerd, kunt u het naar de index sturen door IndexDocuments aan te roepen op uw SearchClient-object.
U laadt documenten met SearchClient in Main()
, maar voor de bewerking zijn ook beheerdersrechten vereist voor de service, die doorgaans is gekoppeld aan SearchIndexClient. Een manier om deze bewerking in te stellen, is door SearchClient door SearchIndexClient
te halen (searchIndexClient
in dit voorbeeld).
SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);
// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);
Omdat we een console-app hebben waarmee alle opdrachten opeenvolgend worden uitgevoerd, voegen we een wachttijd van 2 seconden toe tussen indexering en query's.
// Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
Console.WriteLine("Waiting for indexing...\n");
System.Threading.Thread.Sleep(2000);
De vertraging van 2 seconden compenseert de indexering (die asynchroon is), zodat alle documenten kunnen worden geïndexeerd voordat de query's worden uitgevoerd. Codering in een vertraging is doorgaans alleen nodig in demo's, testen en voorbeeldtoepassingen.
Een index doorzoeken
U kunt queryresultaten ophalen zodra het eerste document wordt geïndexeerd, maar wacht met het daadwerkelijk testen van uw index totdat alle documenten zijn geïndexeerd.
In deze sectie worden twee functies toegevoegd: querylogica en resultaten. Gebruik voor query's de methode Search. Deze methode gebruikt zoektekst (de querytekenreeks) en andere opties.
De SearchResults-klasse vertegenwoordigt de resultaten.
In Program.cs drukt de WriteDocuments
methode zoekresultaten af op de console.
// Write search results to console
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.GetResults())
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
private static void WriteDocuments(AutocompleteResults autoResults)
{
foreach (AutocompleteItem result in autoResults.Results)
{
Console.WriteLine(result.Text);
}
Console.WriteLine();
}
Queryvoorbeeld 1
De RunQueries
methode voert query's uit en retourneert resultaten. Resultaten zijn Hotel-objecten. In dit voorbeeld ziet u de handtekening en de eerste query van de methode. Deze query demonstreert de Select
parameter waarmee u het resultaat kunt opstellen met behulp van geselecteerde velden uit het document.
// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient searchClient)
{
SearchOptions options;
SearchResults<Hotel> response;
// Query 1
Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
options = new SearchOptions()
{
IncludeTotalCount = true,
Filter = "",
OrderBy = { "" }
};
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Address/City");
response = searchClient.Search<Hotel>("*", options);
WriteDocuments(response);
// REDACTED FOR BREVITY
}
Queryvoorbeeld 2
In de tweede query zoekt u op een term, voegt u een filter toe waarmee documenten worden geselecteerd waarin Waardering groter is dan 4 en vervolgens in aflopende volgorde op Classificatie sorteren. Een filter is een Booleaanse uitdrukking die wordt geëvalueerd over IsFilterable-velden in een index. Filterquery's bevatten waarden of sluiten ze uit. Er is dus geen relevantiescore gekoppeld aan een filterquery.
// Query 2
Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
options = new SearchOptions()
{
Filter = "Rating gt 4",
OrderBy = { "Rating desc" }
};
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Rating");
response = searchClient.Search<Hotel>("hotels", options);
WriteDocuments(response);
Queryvoorbeeld 3
De derde query laat zien searchFields
, dat wordt gebruikt om een zoekbewerking voor volledige tekst te bepalen voor specifieke velden.
// Query 3
Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n");
options = new SearchOptions()
{
SearchFields = { "Tags" }
};
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Tags");
response = searchClient.Search<Hotel>("pool", options);
WriteDocuments(response);
Queryvoorbeeld 4
De vierde query laat zien facets
, die kan worden gebruikt om een facetnavigatiestructuur te structuren.
// Query 4
Console.WriteLine("Query #4: Facet on 'Category'...\n");
options = new SearchOptions()
{
Filter = ""
};
options.Facets.Add("Category");
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Category");
response = searchClient.Search<Hotel>("*", options);
WriteDocuments(response);
Queryvoorbeeld 5
In de vijfde query retourneert u een specifiek document. Een documentzoekactie is een typisch antwoord op OnClick
gebeurtenissen in een resultatenset.
// Query 5
Console.WriteLine("Query #5: Look up a specific document...\n");
Response<Hotel> lookupResponse;
lookupResponse = searchClient.GetDocument<Hotel>("3");
Console.WriteLine(lookupResponse.Value.HotelId);
Queryvoorbeeld 6
De laatste query toont de syntaxis voor automatisch aanvullen en simuleert een gedeeltelijke gebruikersinvoer van sa die wordt omgezet in twee mogelijke overeenkomsten in de bronvelden die zijn gekoppeld aan de suggestie die u in de index hebt gedefinieerd.
// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
var autoresponse = searchClient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);
Samenvatting van query's
De vorige query's tonen meerdere manieren om termen in een query te koppelen: zoekopdracht in volledige tekst, filters en automatisch aanvullen.
Zoekopdrachten in volledige tekst en filters worden uitgevoerd met behulp van de methode SearchClient.Search. Een zoekquery kan worden doorgegeven in de searchText
-tekenreeks, terwijl een filterexpressie kan worden doorgegeven in de eigenschap Filter van de klasse Search Options. Als u wilt filteren zonder te zoeken, geeft u "*"
door voor de searchText
-parameter van de Zoekmethode. Als u wilt zoeken zonder te filteren, laat u de Filter
eigenschap uitgeschakeld of geeft u een SearchOptions
instantie helemaal niet door.
Meer informatie over het gebruik van de clientbibliotheek Azure.Search.Documents om een zoekindex te maken, laden en er query's op uit te voeren met voorbeeldgegevens voor zoeken in volledige tekst. Zoeken in volledige tekst maakt gebruik van Apache Lucene voor indexering en query's en een BM25-classificatie-algoritme voor scoreresultaten.
In deze quickstart wordt een kleine index voor hotels-quickstart gemaakt en opgevraagd die gegevens over vier hotels bevat.
Tip
U kunt de broncode downloaden om te beginnen met een voltooid project of deze stappen volgen om uw eigen project te maken.
Vereisten
- Een actief Azure-abonnement - Een gratis abonnement maken
- Een Azure AI-Search-service. Maak een service als u er nog geen hebt. U kunt een gratis laag gebruiken voor deze quickstart.
Vereisten voor Microsoft Entra-id
Voor de aanbevolen sleutelloze verificatie met Microsoft Entra-id moet u het volgende doen:
- Installeer de Azure CLI die wordt gebruikt voor sleutelloze verificatie met Microsoft Entra-id.
- Wijs zowel de rollen als de
Search Service Contributor
Search Index Data Contributor
rollen toe aan uw gebruikersaccount. U kunt rollen toewijzen in Azure Portal onder Toegangsbeheer (IAM)>Roltoewijzing toevoegen. Zie Verbinding maken met Azure AI Search met behulp van rollen voor meer informatie.
Resourcegegevens ophalen
U moet de volgende informatie ophalen om uw toepassing te verifiëren met uw Azure AI-Search-service:
Naam van de variabele | Weergegeven als |
---|---|
SEARCH_API_ENDPOINT |
Deze waarde vindt u in Azure Portal. Selecteer uw zoekservice en selecteer vervolgens in het linkermenu Overzicht. De URL-waarde onder Essentials is het eindpunt dat u nodig hebt. Een eindpunt ziet er bijvoorbeeld uit als https://mydemo.search.windows.net . |
Meer informatie over sleutelloze verificatie en het instellen van omgevingsvariabelen.
Instellingen
Het voorbeeld in deze quickstart werkt met de Java Runtime. Installeer een Java Development Kit zoals Azul Zulu OpenJDK. De Microsoft Build van OpenJDK of uw favoriete JDK moet ook werken.
Installeer Apache Maven. Voer vervolgens uit
mvn -v
om de installatie te bevestigen.Maak een nieuw
pom.xml
bestand in de hoofdmap van uw project en kopieer de volgende code erin:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>azure.search.sample</groupId> <artifactId>azuresearchquickstart</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>com.azure</groupId> <artifactId>azure-search-documents</artifactId> <version>11.7.3</version> </dependency> <dependency> <groupId>com.azure</groupId> <artifactId>azure-core</artifactId> <version>1.53.0</version> </dependency> <dependency> <groupId>com.azure</groupId> <artifactId>azure-identity</artifactId> <version>1.15.1</version> </dependency> </dependencies> </project>
Installeer de afhankelijkheden, waaronder de Azure AI Search-clientbibliotheek (Azure.Search.Documents) voor Java- en Azure Identity-clientbibliotheek voor Java met:
mvn clean dependency:copy-dependencies
Meld u met de volgende opdracht aan bij Azure voor de aanbevolen sleutelloze verificatie met Microsoft Entra ID:
az login
Een zoekindex maken, laden en er query's op uitvoeren
In de vorige sectie voor het instellen hebt u de Azure AI Search-clientbibliotheek en andere afhankelijkheden geïnstalleerd.
In deze sectie voegt u code toe om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U voert het programma uit om de resultaten in de console te zien. Zie de uitleg van de codesectie voor een gedetailleerde uitleg van de code .
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het DefaultAzureCredential
object vervangen door een AzureKeyCredential
object.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
Maak een nieuw bestand met de naam App.java en plak de volgende code in App.java:
import java.util.Arrays; import java.util.ArrayList; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.LocalDateTime; import java.time.LocalDate; import java.time.LocalTime; import com.azure.core.util.Configuration; import com.azure.core.util.Context; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.search.documents.SearchClient; import com.azure.search.documents.SearchClientBuilder; import com.azure.search.documents.indexes.SearchIndexClient; import com.azure.search.documents.indexes.SearchIndexClientBuilder; import com.azure.search.documents.indexes.models.IndexDocumentsBatch; import com.azure.search.documents.models.SearchOptions; import com.azure.search.documents.indexes.models.SearchIndex; import com.azure.search.documents.indexes.models.SearchSuggester; import com.azure.search.documents.util.AutocompletePagedIterable; import com.azure.search.documents.util.SearchPagedIterable; public class App { public static void main(String[] args) { // Your search service endpoint "https://<Put your search service NAME here>.search.windows.net/"; // Use the recommended keyless credential instead of the AzureKeyCredential credential. DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build(); //AzureKeyCredential credential = new AzureKeyCredential("<Your search service admin key>"); // Create a SearchIndexClient to send create/delete index commands SearchIndexClient searchIndexClient = new SearchIndexClientBuilder() .endpoint(searchServiceEndpoint) .credential(credential) .buildClient(); // Create a SearchClient to load and query documents String indexName = "hotels-quickstart-java"; SearchClient searchClient = new SearchClientBuilder() .endpoint(searchServiceEndpoint) .credential(credential) .indexName(indexName) .buildClient(); // Create Search Index for Hotel model searchIndexClient.createOrUpdateIndex( new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null)) .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName")))); // Upload sample hotel documents to the Search Index uploadDocuments(searchClient); // Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only) System.out.println("Waiting for indexing...\n"); try { Thread.sleep(2000); } catch (InterruptedException e) { } // Call the RunQueries method to invoke a series of queries System.out.println("Starting queries...\n"); RunQueries(searchClient); // End the program System.out.println("Complete.\n"); } // Upload documents in a single Upload request. private static void uploadDocuments(SearchClient searchClient) { var hotelList = new ArrayList<Hotel>(); var hotel = new Hotel(); hotel.hotelId = "1"; hotel.hotelName = "Stay-Kay City Hotel"; hotel.description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities."; hotel.descriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique."; hotel.category = "Boutique"; hotel.tags = new String[] { "pool", "air conditioning", "concierge" }; hotel.parkingIncluded = false; hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC); hotel.rating = 3.6; hotel.address = new Address(); hotel.address.streetAddress = "677 5th Ave"; hotel.address.city = "New York"; hotel.address.stateProvince = "NY"; hotel.address.postalCode = "10022"; hotel.address.country = "USA"; hotelList.add(hotel); hotel = new Hotel(); hotel.hotelId = "2"; hotel.hotelName = "Old Century Hotel"; hotel.description = "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts."; hotel.descriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne."; hotel.category = "Boutique"; hotel.tags = new String[] { "pool", "free wifi", "concierge" }; hotel.parkingIncluded = false; hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1979, 2, 18), LocalTime.of(0, 0)), ZoneOffset.UTC); hotel.rating = 3.60; hotel.address = new Address(); hotel.address.streetAddress = "140 University Town Center Dr"; hotel.address.city = "Sarasota"; hotel.address.stateProvince = "FL"; hotel.address.postalCode = "34243"; hotel.address.country = "USA"; hotelList.add(hotel); hotel = new Hotel(); hotel.hotelId = "3"; hotel.hotelName = "Gastronomic Landscape Hotel"; hotel.description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services."; hotel.descriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne."; hotel.category = "Resort and Spa"; hotel.tags = new String[] { "air conditioning", "bar", "continental breakfast" }; hotel.parkingIncluded = true; hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2015, 9, 20), LocalTime.of(0, 0)), ZoneOffset.UTC); hotel.rating = 4.80; hotel.address = new Address(); hotel.address.streetAddress = "3393 Peachtree Rd"; hotel.address.city = "Atlanta"; hotel.address.stateProvince = "GA"; hotel.address.postalCode = "30326"; hotel.address.country = "USA"; hotelList.add(hotel); hotel = new Hotel(); hotel.hotelId = "4"; hotel.hotelName = "Sublime Palace Hotel"; hotel.description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace."; hotel.descriptionFr = "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour."; hotel.category = "Boutique"; hotel.tags = new String[] { "concierge", "view", "24-hour front desk service" }; hotel.parkingIncluded = true; hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1960, 2, 06), LocalTime.of(0, 0)), ZoneOffset.UTC); hotel.rating = 4.60; hotel.address = new Address(); hotel.address.streetAddress = "7400 San Pedro Ave"; hotel.address.city = "San Antonio"; hotel.address.stateProvince = "TX"; hotel.address.postalCode = "78216"; hotel.address.country = "USA"; hotelList.add(hotel); var batch = new IndexDocumentsBatch<Hotel>(); batch.addMergeOrUploadActions(hotelList); try { searchClient.indexDocuments(batch); } catch (Exception e) { e.printStackTrace(); // If for some reason any documents are dropped during indexing, you can compensate by delaying and // retrying. This simple demo just logs failure and continues System.err.println("Failed to index some of the documents"); } } // Write search results to console private static void WriteSearchResults(SearchPagedIterable searchResults) { searchResults.iterator().forEachRemaining(result -> { Hotel hotel = result.getDocument(Hotel.class); System.out.println(hotel); }); System.out.println(); } // Write autocomplete results to console private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults) { autocompleteResults.iterator().forEachRemaining(result -> { String text = result.getText(); System.out.println(text); }); System.out.println(); } // Run queries, use WriteDocuments to print output private static void RunQueries(SearchClient searchClient) { // Query 1 System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n"); SearchOptions options = new SearchOptions(); options.setIncludeTotalCount(true); options.setFilter(""); options.setOrderBy(""); options.setSelect("HotelId", "HotelName", "Address/City"); WriteSearchResults(searchClient.search("*", options, Context.NONE)); // Query 2 System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n"); options = new SearchOptions(); options.setFilter("Rating gt 4"); options.setOrderBy("Rating desc"); options.setSelect("HotelId", "HotelName", "Rating"); WriteSearchResults(searchClient.search("hotels", options, Context.NONE)); // Query 3 System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n"); options = new SearchOptions(); options.setSearchFields("Tags"); options.setSelect("HotelId", "HotelName", "Tags"); WriteSearchResults(searchClient.search("pool", options, Context.NONE)); // Query 4 System.out.println("Query #4: Facet on 'Category'...\n"); options = new SearchOptions(); options.setFilter(""); options.setFacets("Category"); options.setSelect("HotelId", "HotelName", "Category"); WriteSearchResults(searchClient.search("*", options, Context.NONE)); // Query 5 System.out.println("Query #5: Look up a specific document...\n"); Hotel lookupResponse = searchClient.getDocument("3", Hotel.class); System.out.println(lookupResponse.hotelId); System.out.println(); // Query 6 System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n"); WriteAutocompleteResults(searchClient.autocomplete("s", "sg")); } }
Maak een nieuw bestand met de naam Hotel.java en plak de volgende code in Hotel.java:
import com.azure.search.documents.indexes.SearchableField; import com.azure.search.documents.indexes.SimpleField; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonInclude.Include; import java.time.OffsetDateTime; /** * Model class representing a hotel. */ @JsonInclude(Include.NON_NULL) public class Hotel { /** * Hotel ID */ @JsonProperty("HotelId") @SimpleField(isKey = true) public String hotelId; /** * Hotel name */ @JsonProperty("HotelName") @SearchableField(isSortable = true) public String hotelName; /** * Description */ @JsonProperty("Description") @SearchableField(analyzerName = "en.microsoft") public String description; /** * French description */ @JsonProperty("DescriptionFr") @SearchableField(analyzerName = "fr.lucene") public String descriptionFr; /** * Category */ @JsonProperty("Category") @SearchableField(isFilterable = true, isSortable = true, isFacetable = true) public String category; /** * Tags */ @JsonProperty("Tags") @SearchableField(isFilterable = true, isFacetable = true) public String[] tags; /** * Whether parking is included */ @JsonProperty("ParkingIncluded") @SimpleField(isFilterable = true, isSortable = true, isFacetable = true) public Boolean parkingIncluded; /** * Last renovation time */ @JsonProperty("LastRenovationDate") @SimpleField(isFilterable = true, isSortable = true, isFacetable = true) public OffsetDateTime lastRenovationDate; /** * Rating */ @JsonProperty("Rating") @SimpleField(isFilterable = true, isSortable = true, isFacetable = true) public Double rating; /** * Address */ @JsonProperty("Address") public Address address; @Override public String toString() { try { return new ObjectMapper().writeValueAsString(this); } catch (JsonProcessingException e) { e.printStackTrace(); return ""; } } }
Maak een nieuw bestand met de naam Address.java en plak de volgende code in Address.java:
import com.azure.search.documents.indexes.SearchableField; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonInclude.Include; /** * Model class representing an address. */ @JsonInclude(Include.NON_NULL) public class Address { /** * Street address */ @JsonProperty("StreetAddress") @SearchableField public String streetAddress; /** * City */ @JsonProperty("City") @SearchableField(isFilterable = true, isSortable = true, isFacetable = true) public String city; /** * State or province */ @JsonProperty("StateProvince") @SearchableField(isFilterable = true, isSortable = true, isFacetable = true) public String stateProvince; /** * Postal code */ @JsonProperty("PostalCode") @SearchableField(isFilterable = true, isSortable = true, isFacetable = true) public String postalCode; /** * Country */ @JsonProperty("Country") @SearchableField(isFilterable = true, isSortable = true, isFacetable = true) public String country; }
Voer de nieuwe consoletoepassing uit:
javac Address.java App.java Hotel.java -cp ".;target\dependency\*" java -cp ".;target\dependency\*" App
Uitleg van de code
In de vorige secties hebt u een nieuwe consoletoepassing gemaakt en de Azure AI Search-clientbibliotheek geïnstalleerd. U hebt code toegevoegd om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U hebt het programma uitgevoerd om de resultaten in de console te zien.
In deze sectie wordt de code uitgelegd die u hebt toegevoegd aan de consoletoepassing.
Een zoekclient maken
In App.java hebt u twee clients gemaakt:
- SearchIndexClient maakt de index.
- SearchClient laadt en voert query's uit op een bestaande index.
Beide clients hebben het eindpunt van de zoekservice en referenties nodig die eerder zijn beschreven in de sectie resourcegegevens.
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het DefaultAzureCredential
object vervangen door een AzureKeyCredential
object.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
public static void main(String[] args) {
// Your search service endpoint
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
// Use the recommended keyless credential instead of the AzureKeyCredential credential.
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
//AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");
// Create a SearchIndexClient to send create/delete index commands
SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
.endpoint(searchServiceEndpoint)
.credential(credential)
.buildClient();
// Create a SearchClient to load and query documents
String indexName = "hotels-quickstart-java";
SearchClient searchClient = new SearchClientBuilder()
.endpoint(searchServiceEndpoint)
.credential(credential)
.indexName(indexName)
.buildClient();
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
// REDACTED FOR BREVITY . . .
}
Een index maken
Deze quickstart bouwt een Hotels-index die u laadt met hotelgegevens en voert query's uit op. In deze stap definieert u de velden in de index. Elke definitie bevat een naam, gegevenstype en kenmerken die bepalen hoe het veld wordt gebruikt.
In dit voorbeeld worden synchrone methoden van de bibliotheek Azure.Search.Documents gebruikt voor eenvoud en leesbaarheid. Voor productiescenario's moet u echter asynchrone methoden gebruiken om uw app op een schaalbare en responsieve manier te laten werken. U kunt bijvoorbeeld CreateIndexAsync gebruiken in plaats van CreateIndex.
De structuren definiëren
U hebt twee helperklassen gemaakt, Hotel.java en Address.java, om de structuur van een hoteldocument en het bijbehorende adres te definiëren. De klasse Hotel bevat velden voor een hotel-id, naam, beschrijving, categorie, tags, parkeren, renovatiedatum, beoordeling en adres. De klasse Adres bevat velden voor straatadres, plaats, provincie, postcode en land/regio.
In de clientbibliotheek van Azure.Search.Documents kunt u velddefinities stroomlijnen met behulp van de velden SearchableField en SimpleField.
-
SimpleField
kan elk gegevenstype zijn, is altijd niet doorzoekbaar (genegeerd voor zoekquery's in volledige tekst) en kan worden opgehaald (niet verborgen). Andere kenmerken zijn standaard uitgeschakeld, maar kunnen wel worden ingeschakeld. U kunt een SimpleField gebruiken voor document-id's of velden die alleen worden gebruikt in filters, facetten of scoreprofielen. Als dit het geval is, moet u alle kenmerken toepassen die nodig zijn voor het scenario, zoals IsKey = true voor een document-id. -
SearchableField
moet een tekenreeks zijn die altijd kan worden doorzocht en opgehaald. Andere kenmerken zijn standaard uitgeschakeld, maar kunnen wel worden ingeschakeld. Omdat dit veldtype kan worden doorzocht, worden synoniemen en het volledige gamma van analyse-eigenschappen ondersteund.
Ongeacht of u de basis-API van SearchField
of een van de hulpmodellen gebruikt, u moet filter-, facet- en sorteerkenmerken expliciet inschakelen. Bijvoorbeeld, isFilterable
isSortable
en isFacetable
moet expliciet worden toegeschreven, zoals wordt weergegeven in de vorige steekproef.
De zoekindex maken
In App.java
maakt u een SearchIndex
object in de main
methode en roept u de createOrUpdateIndex
methode aan om de index in uw zoekservice te maken. De index bevat ook een SearchSuggester
functie om automatisch aanvullen in te schakelen voor de opgegeven velden.
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
Documenten laden
Azure AI Search zoekt naar inhoud die is opgeslagen in de service. In deze stap laadt u JSON-documenten die voldoen aan de hotelindex die u hebt gemaakt.
In Azure AI Search zijn zoekdocumenten gegevensstructuren die zowel invoer zijn voor het indexeren als uitvoeren van query's. Als u de documenten hebt verkregen via een externe gegevensbron, bestaat de documentinvoer mogelijk uit rijen in een database, blobs in Blob Storage of JSON-documenten op een schijf. In dit voorbeeld nemen we de korte route en gaan we JSON-documenten voor vier hotels in de code zelf insluiten.
Bij het uploaden van documenten moet u een IndexDocumentsBatch-object gebruiken. Een IndexDocumentsBatch
object bevat een verzameling IndexActions, die elk een document en een eigenschap bevatten die Azure AI Search vertelt welke actie moet worden uitgevoerd (uploaden, samenvoegen, verwijderen en samenvoegen).
In App.java
maakt u documenten en indexacties en geeft u ze door aan IndexDocumentsBatch
. De volgende documenten voldoen aan de index hotels-quickstart, zoals gedefinieerd door de hotelklasse.
private static void uploadDocuments(SearchClient searchClient)
{
var hotelList = new ArrayList<Hotel>();
var hotel = new Hotel();
hotel.hotelId = "1";
hotel.hotelName = "Stay-Kay City Hotel";
hotel.description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.";
hotel.descriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.";
hotel.category = "Boutique";
hotel.tags = new String[] { "pool", "air conditioning", "concierge" };
hotel.parkingIncluded = false;
hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
hotel.rating = 3.6;
hotel.address = new Address();
hotel.address.streetAddress = "677 5th Ave";
hotel.address.city = "New York";
hotel.address.stateProvince = "NY";
hotel.address.postalCode = "10022";
hotel.address.country = "USA";
hotelList.add(hotel);
// REDACTED FOR BREVITY
var batch = new IndexDocumentsBatch<Hotel>();
batch.addMergeOrUploadActions(hotelList);
try
{
searchClient.indexDocuments(batch);
}
catch (Exception e)
{
e.printStackTrace();
// If for some reason any documents are dropped during indexing, you can compensate by delaying and
// retrying. This simple demo just logs failure and continues
System.err.println("Failed to index some of the documents");
}
}
Nadat u het IndexDocumentsBatch
object hebt geïnitialiseerd, kunt u het naar de index verzenden door indexDocuments op uw SearchClient
object aan te roepen.
U laadt documenten met SearchClient in main()
, maar voor de bewerking zijn ook beheerdersrechten vereist voor de service, die doorgaans is gekoppeld aan SearchIndexClient. Een manier om deze bewerking in te stellen, is door SearchClient door SearchIndexClient
te halen (searchIndexClient
in dit voorbeeld).
uploadDocuments(searchClient);
Omdat we een console-app hebben waarmee alle opdrachten opeenvolgend worden uitgevoerd, voegen we een wachttijd van 2 seconden toe tussen indexering en query's.
// Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
System.out.println("Waiting for indexing...\n");
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
}
De vertraging van 2 seconden compenseert de indexering (die asynchroon is), zodat alle documenten kunnen worden geïndexeerd voordat de query's worden uitgevoerd. Codering in een vertraging is doorgaans alleen nodig in demo's, testen en voorbeeldtoepassingen.
Een index doorzoeken
U kunt queryresultaten ophalen zodra het eerste document wordt geïndexeerd, maar wacht met het daadwerkelijk testen van uw index totdat alle documenten zijn geïndexeerd.
In deze sectie worden twee onderdelen van functionaliteit toegevoegd: querylogica en resultaten. Gebruik voor query's de methode Search. Deze methode gebruikt zoektekst (de querytekenreeks) en andere opties.
In App.java
de WriteDocuments
methode worden zoekresultaten naar de console afgedrukt.
// Write search results to console
private static void WriteSearchResults(SearchPagedIterable searchResults)
{
searchResults.iterator().forEachRemaining(result ->
{
Hotel hotel = result.getDocument(Hotel.class);
System.out.println(hotel);
});
System.out.println();
}
// Write autocomplete results to console
private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
{
autocompleteResults.iterator().forEachRemaining(result ->
{
String text = result.getText();
System.out.println(text);
});
System.out.println();
}
Queryvoorbeeld 1
De RunQueries
methode voert query's uit en retourneert resultaten. Resultaten zijn Hotel-objecten. In dit voorbeeld ziet u de handtekening en de eerste query van de methode. Deze query demonstreert de Select
parameter waarmee u het resultaat kunt opstellen met behulp van geselecteerde velden uit het document.
// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient searchClient)
{
// Query 1
System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
SearchOptions options = new SearchOptions();
options.setIncludeTotalCount(true);
options.setFilter("");
options.setOrderBy("");
options.setSelect("HotelId", "HotelName", "Address/City");
WriteSearchResults(searchClient.search("*", options, Context.NONE));
}
Queryvoorbeeld 2
In de tweede query zoekt u op een term, voegt u een filter toe waarmee documenten worden geselecteerd waarin Waardering groter is dan 4 en vervolgens in aflopende volgorde op Classificatie sorteren. Filter is een Booleaanse expressie die wordt geëvalueerd op isFilterable
velden in een index. Filterquery's bevatten waarden of sluiten ze uit. Er is dus geen relevantiescore gekoppeld aan een filterquery.
// Query 2
System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
options = new SearchOptions();
options.setFilter("Rating gt 4");
options.setOrderBy("Rating desc");
options.setSelect("HotelId", "HotelName", "Rating");
WriteSearchResults(searchClient.search("hotels", options, Context.NONE));
Queryvoorbeeld 3
De derde query laat zien searchFields
, dat wordt gebruikt om een zoekbewerking voor volledige tekst te bepalen voor specifieke velden.
// Query 3
System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");
options = new SearchOptions();
options.setSearchFields("Tags");
options.setSelect("HotelId", "HotelName", "Tags");
WriteSearchResults(searchClient.search("pool", options, Context.NONE));
Queryvoorbeeld 4
De vierde query laat zien facets
, die kan worden gebruikt om een facetnavigatiestructuur te structuren.
// Query 4
System.out.println("Query #4: Facet on 'Category'...\n");
options = new SearchOptions();
options.setFilter("");
options.setFacets("Category");
options.setSelect("HotelId", "HotelName", "Category");
WriteSearchResults(searchClient.search("*", options, Context.NONE));
Queryvoorbeeld 5
In de vijfde query retourneert u een specifiek document.
// Query 5
System.out.println("Query #5: Look up a specific document...\n");
Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
System.out.println(lookupResponse.hotelId);
System.out.println();
Queryvoorbeeld 6
De laatste query toont de syntaxis voor automatisch aanvullen, waarbij een gedeeltelijke gebruikersinvoer van s wordt gesimsimeerd die wordt omgezet in twee mogelijke overeenkomsten in de sourceFields
gekoppelde aan de suggestie die u in de index hebt gedefinieerd.
// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
Samenvatting van query's
De vorige query's tonen meerdere manieren om termen in een query te koppelen: zoekopdracht in volledige tekst, filters en automatisch aanvullen.
Zoekopdrachten en filters voor volledige tekst worden uitgevoerd met behulp van de zoekmethode SearchClient.search . Een zoekquery kan worden doorgegeven in de searchText
tekenreeks, terwijl een filterexpressie kan worden doorgegeven in de filter
eigenschap van de klasse SearchOptions . Als u wilt filteren zonder te zoeken, geeft u '*' door voor de searchText
parameter van de search
methode. Als u wilt zoeken zonder te filteren, laat u de filter
eigenschap uitgeschakeld of geeft u een SearchOptions
instantie helemaal niet door.
Meer informatie over het gebruik van de clientbibliotheek Azure.Search.Documents om een zoekindex te maken, laden en er query's op uit te voeren met voorbeeldgegevens voor zoeken in volledige tekst. Zoeken in volledige tekst maakt gebruik van Apache Lucene voor indexering en query's en een BM25-classificatie-algoritme voor scoreresultaten.
In deze quickstart wordt een kleine index voor hotels-quickstart gemaakt en opgevraagd die gegevens over vier hotels bevat.
Tip
U kunt de broncode downloaden om te beginnen met een voltooid project of deze stappen volgen om uw eigen project te maken.
Vereisten
- Een actief Azure-abonnement - Een gratis abonnement maken
- Een Azure AI-Search-service. Maak een service als u er nog geen hebt. U kunt een gratis laag gebruiken voor deze quickstart.
Vereisten voor Microsoft Entra-id
Voor de aanbevolen sleutelloze verificatie met Microsoft Entra-id moet u het volgende doen:
- Installeer de Azure CLI die wordt gebruikt voor sleutelloze verificatie met Microsoft Entra-id.
- Wijs zowel de rollen als de
Search Service Contributor
Search Index Data Contributor
rollen toe aan uw gebruikersaccount. U kunt rollen toewijzen in Azure Portal onder Toegangsbeheer (IAM)>Roltoewijzing toevoegen. Zie Verbinding maken met Azure AI Search met behulp van rollen voor meer informatie.
Resourcegegevens ophalen
U moet de volgende informatie ophalen om uw toepassing te verifiëren met uw Azure AI-Search-service:
Naam van de variabele | Weergegeven als |
---|---|
SEARCH_API_ENDPOINT |
Deze waarde vindt u in Azure Portal. Selecteer uw zoekservice en selecteer vervolgens in het linkermenu Overzicht. De URL-waarde onder Essentials is het eindpunt dat u nodig hebt. Een eindpunt ziet er bijvoorbeeld uit als https://mydemo.search.windows.net . |
Meer informatie over sleutelloze verificatie en het instellen van omgevingsvariabelen.
Instellingen
Maak een nieuwe map
full-text-quickstart
die de toepassing bevat en open Visual Studio Code in die map met de volgende opdracht:mkdir full-text-quickstart && cd full-text-quickstart
Maak de
package.json
opdracht met de volgende opdracht:npm init -y
Installeer de Azure AI Search-clientbibliotheek (Azure.Search.Documents) voor JavaScript met:
npm install @azure/search-documents
Installeer voor de aanbevolen verificatie zonder wachtwoord de Azure Identity-clientbibliotheek met:
npm install @azure/identity
Een zoekindex maken, laden en er query's op uitvoeren
In de vorige sectie voor het instellen hebt u de Azure AI Search-clientbibliotheek en andere afhankelijkheden geïnstalleerd.
In deze sectie voegt u code toe om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U voert het programma uit om de resultaten in de console te zien. Zie de uitleg van de codesectie voor een gedetailleerde uitleg van de code .
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het DefaultAzureCredential
object vervangen door een AzureKeyCredential
object.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
Maak een nieuw bestand met de naam index.js en plak de volgende code in index.js:
// Import from the @azure/search-documents library import { SearchIndexClient, odata } from "@azure/search-documents"; // Import from the Azure Identity library import { DefaultAzureCredential } from "@azure/identity"; // Importing the hotels sample data import hotelData from './hotels.json' assert { type: "json" }; // Load the .env file if it exists import * as dotenv from "dotenv"; dotenv.config(); // Defining the index definition const indexDefinition = { "name": "hotels-quickstart", "fields": [ { "name": "HotelId", "type": "Edm.String", "key": true, "filterable": true }, { "name": "HotelName", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": true, "facetable": false }, { "name": "Description", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzerName": "en.lucene" }, { "name": "Description_fr", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzerName": "fr.lucene" }, { "name": "Category", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "Tags", "type": "Collection(Edm.String)", "searchable": true, "filterable": true, "sortable": false, "facetable": true }, { "name": "ParkingIncluded", "type": "Edm.Boolean", "filterable": true, "sortable": true, "facetable": true }, { "name": "LastRenovationDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "facetable": true }, { "name": "Rating", "type": "Edm.Double", "filterable": true, "sortable": true, "facetable": true }, { "name": "Address", "type": "Edm.ComplexType", "fields": [ { "name": "StreetAddress", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false, "searchable": true }, { "name": "City", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "StateProvince", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "PostalCode", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "Country", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true } ] } ], "suggesters": [ { "name": "sg", "searchMode": "analyzingInfixMatching", "sourceFields": [ "HotelName" ] } ] }; async function main() { // Your search service endpoint const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/"; // Use the recommended keyless credential instead of the AzureKeyCredential credential. const credential = new DefaultAzureCredential(); //const credential = new AzureKeyCredential(Your search service admin key); // Create a SearchIndexClient to send create/delete index commands const searchIndexClient = new SearchIndexClient(searchServiceEndpoint, credential); // Creating a search client to upload documents and issue queries const indexName = "hotels-quickstart"; const searchClient = searchIndexClient.getSearchClient(indexName); console.log('Checking if index exists...'); await deleteIndexIfExists(searchIndexClient, indexName); console.log('Creating index...'); let index = await searchIndexClient.createIndex(indexDefinition); console.log(`Index named ${index.name} has been created.`); console.log('Uploading documents...'); let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']); console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `); // waiting one second for indexing to complete (for demo purposes only) sleep(1000); console.log('Querying the index...'); console.log(); await sendQueries(searchClient); } async function deleteIndexIfExists(searchIndexClient, indexName) { try { await searchIndexClient.deleteIndex(indexName); console.log('Deleting index...'); } catch { console.log('Index does not exist yet.'); } } async function sendQueries(searchClient) { // Query 1 console.log('Query #1 - search everything:'); let searchOptions = { includeTotalCount: true, select: ["HotelId", "HotelName", "Rating"] }; let searchResults = await searchClient.search("*", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(`Result count: ${searchResults.count}`); console.log(); // Query 2 console.log('Query #2 - search with filter, orderBy, and select:'); let state = 'FL'; searchOptions = { filter: odata `Address/StateProvince eq ${state}`, orderBy: ["Rating desc"], select: ["HotelId", "HotelName", "Rating"] }; searchResults = await searchClient.search("wifi", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(); // Query 3 console.log('Query #3 - limit searchFields:'); searchOptions = { select: ["HotelId", "HotelName", "Rating"], searchFields: ["HotelName"] }; searchResults = await searchClient.search("sublime cliff", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(); // Query 4 console.log('Query #4 - limit searchFields and use facets:'); searchOptions = { facets: ["Category"], select: ["HotelId", "HotelName", "Rating"], searchFields: ["HotelName"] }; searchResults = await searchClient.search("*", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(); // Query 5 console.log('Query #5 - Lookup document:'); let documentResult = await searchClient.getDocument('3'); console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`); console.log(); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } main().catch((err) => { console.error("The sample encountered an error:", err); });
Maak een bestand met de naam hotels.json en plak de volgende code in hotels.json:
{ "value": [ { "HotelId": "1", "HotelName": "Secret Point Motel", "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.", "Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.", "Category": "Boutique", "Tags": ["pool", "air conditioning", "concierge"], "ParkingIncluded": false, "LastRenovationDate": "1970-01-18T00:00:00Z", "Rating": 3.6, "Address": { "StreetAddress": "677 5th Ave", "City": "New York", "StateProvince": "NY", "PostalCode": "10022" } }, { "HotelId": "2", "HotelName": "Twin Dome Motel", "Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.", "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.", "Category": "Boutique", "Tags": ["pool", "free wifi", "concierge"], "ParkingIncluded": "false", "LastRenovationDate": "1979-02-18T00:00:00Z", "Rating": 3.6, "Address": { "StreetAddress": "140 University Town Center Dr", "City": "Sarasota", "StateProvince": "FL", "PostalCode": "34243" } }, { "HotelId": "3", "HotelName": "Triple Landscape Hotel", "Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.", "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.", "Category": "Resort and Spa", "Tags": ["air conditioning", "bar", "continental breakfast"], "ParkingIncluded": "true", "LastRenovationDate": "2015-09-20T00:00:00Z", "Rating": 4.8, "Address": { "StreetAddress": "3393 Peachtree Rd", "City": "Atlanta", "StateProvince": "GA", "PostalCode": "30326" } }, { "HotelId": "4", "HotelName": "Sublime Cliff Hotel", "Description": "Sublime Cliff Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace.", "Description_fr": "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.", "Category": "Boutique", "Tags": ["concierge", "view", "24-hour front desk service"], "ParkingIncluded": true, "LastRenovationDate": "1960-02-06T00:00:00Z", "Rating": 4.6, "Address": { "StreetAddress": "7400 San Pedro Ave", "City": "San Antonio", "StateProvince": "TX", "PostalCode": "78216" } } ] }
Maak een bestand met de naam hotels_quickstart_index.json en plak de volgende code in hotels_quickstart_index.json:
{ "name": "hotels-quickstart", "fields": [ { "name": "HotelId", "type": "Edm.String", "key": true, "filterable": true }, { "name": "HotelName", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": true, "facetable": false }, { "name": "Description", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzerName": "en.lucene" }, { "name": "Description_fr", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzerName": "fr.lucene" }, { "name": "Category", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "Tags", "type": "Collection(Edm.String)", "searchable": true, "filterable": true, "sortable": false, "facetable": true }, { "name": "ParkingIncluded", "type": "Edm.Boolean", "filterable": true, "sortable": true, "facetable": true }, { "name": "LastRenovationDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "facetable": true }, { "name": "Rating", "type": "Edm.Double", "filterable": true, "sortable": true, "facetable": true }, { "name": "Address", "type": "Edm.ComplexType", "fields": [ { "name": "StreetAddress", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false, "searchable": true }, { "name": "City", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "StateProvince", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "PostalCode", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "Country", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true } ] } ], "suggesters": [ { "name": "sg", "searchMode": "analyzingInfixMatching", "sourceFields": [ "HotelName" ] } ] }
Meld u aan bij Azure met de volgende opdracht:
az login
Voer de JavaScript-code uit met de volgende opdracht:
node index.js
Uitleg van de code
Index maken
Het hotels_quickstart_index.json-bestand definieert hoe Azure AI Search werkt met de documenten die u in de volgende stap laadt. Elk veld wordt geïdentificeerd door een name
en heeft een opgegeven type
. Elk veld heeft ook een reeks indexkenmerken die aangeven of Azure AI Search kan zoeken, filteren, sorteren en facet op het veld. De meeste velden zijn eenvoudige gegevenstypen, maar een aantal, zoals AddressType
, is van een complex type waarmee u uitgebreide gegevensstructuren in uw index kunt maken. Meer informatie over ondersteunde gegevenstypen en indexkenmerken die worden beschreven in Create Index (REST).
Als u de indexdefinitie hebt geïmplementeerd, wilt u hotels_quickstart_index. json boven in het bestand index.js importeren, zodat de hoofdfunctie de indexdefinitie kan openen.
const indexDefinition = require('./hotels_quickstart_index.json');
In de hoofdfunctie maken we vervolgens een SearchIndexClient
, die wordt gebruikt voor het maken en beheren van indexen voor Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
De index moet worden verwijderd als deze al bestaat. Deze bewerking is een veelvoorkomende procedure voor test-/democode.
Dit doet u door een eenvoudige functie te definiëren die probeert om de index te verwijderen.
async function deleteIndexIfExists(indexClient, indexName) {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Als u de functie wilt uitvoeren, halen we de indexnaam op uit de indexdefinitie en geven we de indexName
door met de indexClient
aan de functie deleteIndexIfExists()
.
const indexName = indexDefinition["name"];
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
Daarna kunt u de index maken met behulp van de methode createIndex()
.
console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);
console.log(`Index named ${index.name} has been created.`);
Documenten laden
In Azure AI Search zijn documenten gegevensstructuren die zowel invoer zijn voor het indexeren als uitvoeren van query's. U kunt dergelijke gegevens naar de index pushen of een Indexeerfunctie gebruiken. In dat geval kunnen we de documenten via een programma naar de index pushen.
De documentinvoer bestaat mogelijk uit rijen in een database, blobs in Blob Storage of, zoals in dit voorbeeld, JSON-documenten op een schijf. Net als bij wat we hebben gedaan met de indexDefinition
, moeten we ook importeren hotels.json
bovenaan index.js , zodat de gegevens kunnen worden geopend in onze hoofdfunctie.
const hotelData = require('./hotels.json');
Als u gegevens in de zoekindex wilt indexeren, moet u nu een SearchClient
maken. Terwijl de SearchIndexClient
wordt gebruikt voor het maken en beheren van een index, wordt de SearchClient
gebruikt voor het uploaden van documenten en het uitvoeren van query's op de index.
Er zijn twee manieren om een SearchClient
maken. De eerste optie is het maken van een geheel nieuwe SearchClient
:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
U kunt ook de methode getSearchClient()
van de SearchIndexClient
gebruiken om de SearchClient
te maken:
const searchClient = indexClient.getSearchClient(indexName);
Nu de client is gedefinieerd, uploadt u de documenten in de zoekindex. In dit geval gebruiken we de mergeOrUploadDocuments()
methode, waarmee de documenten worden geüpload of samengevoegd met een bestaand document als er al een document met dezelfde sleutel bestaat.
console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Een index doorzoeken
Als er een index is gemaakt en documenten zijn geüpload, bent u klaar om query's naar de index te verzenden. In deze sectie verzenden we vijf verschillende query's naar de zoekindex om verschillende onderdelen van de queryfunctionaliteit te demonstreren die voor u beschikbaar zijn.
De query's worden als volgt geschreven in een sendQueries()
functie die we in de hoofdfunctie aanroepen:
await sendQueries(searchClient);
Query's worden verzonden met behulp van de methode search()
van searchClient
. De eerste parameter is de zoektekst en de tweede parameter specificeert zoekopties.
Queryvoorbeeld 1
De eerste query zoekt *
, wat gelijk is aan ‘zoeken in alle’ en selecteert drie van de velden in de index. Het is een best practice om alleen de velden te select
die u nodig hebt, omdat het ophalen van overbodige gegevens een latentie kan veroorzaken bij uw query's.
De searchOptions
voor deze query is includeTotalCount
ook ingesteld op true
, waarmee het aantal gevonden overeenkomende resultaten wordt geretourneerd.
async function sendQueries(searchClient) {
console.log('Query #1 - search everything:');
let searchOptions = {
includeTotalCount: true,
select: ["HotelId", "HotelName", "Rating"]
};
let searchResults = await searchClient.search("*", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
console.log(`Result count: ${searchResults.count}`);
// remaining queries go here
}
De resterende query's die hieronder worden beschreven, moeten ook worden toegevoegd aan de functie sendQueries()
. Ze zijn hier gescheiden voor leesbaarheid.
Queryvoorbeeld 2
In de volgende query geven we de zoekterm "wifi"
op en nemen we ook een filter op om alleen resultaten te retourneren waarvan de status gelijk is aan 'FL'
. Resultaten worden ook gerangschikt op de Rating
van het hotel.
console.log('Query #2 - Search with filter, orderBy, and select:');
let state = 'FL';
searchOptions = {
filter: odata`Address/StateProvince eq ${state}`,
orderBy: ["Rating desc"],
select: ["HotelId", "HotelName", "Rating"]
};
searchResults = await searchClient.search("wifi", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Queryvoorbeeld 3
Vervolgens kan de zoekopdracht worden beperkt tot één doorzoekbaar veld met behulp van de parameter searchFields
. Deze methode is een uitstekende optie om uw query efficiënter te maken als u weet dat u alleen geïnteresseerd bent in overeenkomsten in bepaalde velden.
console.log('Query #3 - Limit searchFields:');
searchOptions = {
select: ["HotelId", "HotelName", "Rating"],
searchFields: ["HotelName"]
};
searchResults = await searchClient.search("Sublime Palace", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
console.log();
Queryvoorbeeld 4
Een andere veelgebruikte optie die in een query moet worden opgenomen, is facets
. Met facetten kunt u filters maken op uw gebruikersinterface, zodat gebruikers eenvoudig kunnen zien welke waarden ze kunnen filteren.
console.log('Query #4 - Use facets:');
searchOptions = {
facets: ["Category"],
select: ["HotelId", "HotelName", "Rating"],
searchFields: ["HotelName"]
};
searchResults = await searchClient.search("*", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Queryvoorbeeld 5
De laatste query maakt gebruik van de methode getDocument()
van de searchClient
. Zo kunt u een document efficiënt ophalen met de bijbehorende sleutel.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Samenvatting van query's
De vorige query's tonen meerdere manieren om termen in een query te koppelen: zoekopdracht in volledige tekst, filters en automatisch aanvullen.
Zoekopdrachten en filters voor volledige tekst worden uitgevoerd met behulp van de searchClient.search
methode. Een zoekquery kan worden doorgegeven in de searchText
tekenreeks, terwijl een filterexpressie kan worden doorgegeven in de filter
eigenschap van de SearchOptions
klasse. Als u wilt filteren zonder te zoeken, geeft u '*' door voor de searchText
parameter van de search
methode. Als u wilt zoeken zonder te filteren, laat u de filter
eigenschap uitgeschakeld of geeft u een SearchOptions
instantie helemaal niet door.
Meer informatie over het gebruik van de clientbibliotheek Azure.Search.Documents om een zoekindex te maken, laden en er query's op uit te voeren met voorbeeldgegevens voor zoeken in volledige tekst. Zoeken in volledige tekst maakt gebruik van Apache Lucene voor indexering en query's en een BM25-classificatie-algoritme voor scoreresultaten.
In deze quickstart wordt een kleine index voor hotels-quickstart gemaakt en opgevraagd die gegevens over vier hotels bevat.
Tip
U kunt een voltooid notebook downloaden en uitvoeren.
Vereisten
- Een actief Azure-abonnement - Een gratis abonnement maken
- Een Azure AI-Search-service. Maak een service als u er nog geen hebt. U kunt een gratis laag gebruiken voor deze quickstart.
- Visual Studio Code met de Python-extensie of een equivalente IDE, met Python 3.10 of hoger. Als u geen geschikte versie van Python hebt geïnstalleerd, kunt u de instructies volgen in de zelfstudie over VS Code Python.
Vereisten voor Microsoft Entra-id
Voor de aanbevolen sleutelloze verificatie met Microsoft Entra-id moet u het volgende doen:
- Installeer de Azure CLI die wordt gebruikt voor sleutelloze verificatie met Microsoft Entra-id.
- Wijs zowel de rollen als de
Search Service Contributor
Search Index Data Contributor
rollen toe aan uw gebruikersaccount. U kunt rollen toewijzen in Azure Portal onder Toegangsbeheer (IAM)>Roltoewijzing toevoegen. Zie Verbinding maken met Azure AI Search met behulp van rollen voor meer informatie.
Resourcegegevens ophalen
U moet de volgende informatie ophalen om uw toepassing te verifiëren met uw Azure AI-Search-service:
Naam van de variabele | Weergegeven als |
---|---|
SEARCH_API_ENDPOINT |
Deze waarde vindt u in Azure Portal. Selecteer uw zoekservice en selecteer vervolgens in het linkermenu Overzicht. De URL-waarde onder Essentials is het eindpunt dat u nodig hebt. Een eindpunt ziet er bijvoorbeeld uit als https://mydemo.search.windows.net . |
Meer informatie over sleutelloze verificatie en het instellen van omgevingsvariabelen.
Uw omgeving instellen
U voert de voorbeeldcode uit in een Jupyter-notebook. U moet dus uw omgeving instellen om Jupyter-notebooks uit te voeren.
Download of kopieer het voorbeeldnotebook vanuit GitHub.
Open het notebook in Visual Studio Code.
Maak een nieuwe Python-omgeving om de pakketten te installeren die u nodig hebt voor deze zelfstudie.
Belangrijk
Installeer geen pakketten in uw globale Python-installatie. U moet altijd een virtuele of conda-omgeving gebruiken bij het installeren van Python-pakketten, anders kunt u uw globale installatie van Python verbreken.
Het kan even duren voordat het is ingesteld. Als u problemen ondervindt, raadpleegt u Python-omgevingen in VS Code.
Installeer Jupyter-notebooks en de IPython Kernel voor Jupyter-notebooks als u deze nog niet hebt.
pip install jupyter pip install ipykernel python -m ipykernel install --user --name=.venv
Selecteer de notebook-kernel.
- Selecteer Kernel in de rechterbovenhoek van het notitieblok.
- Als u deze in de lijst ziet
.venv
, selecteert u deze. Als u deze niet ziet, selecteert u Een andere Kernel>Python-omgevingen>.venv
selecteren.
Een zoekindex maken, laden en er query's op uitvoeren
In deze sectie voegt u code toe om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U voert het programma uit om de resultaten in de console te zien. Zie de uitleg van de codesectie voor een gedetailleerde uitleg van de code .
Zorg ervoor dat het notebook is geopend in de
.venv
kernel, zoals beschreven in de vorige sectie.Voer de eerste codecel uit om de vereiste pakketten te installeren, inclusief azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet ! pip install azure-identity --quiet ! pip install python-dotenv --quiet
Vervang de inhoud van de tweede codecel door de volgende code, afhankelijk van uw verificatiemethode.
Notitie
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het
DefaultAzureCredential
object vervangen door eenAzureKeyCredential
object.from azure.core.credentials import AzureKeyCredential from azure.identity import DefaultAzureCredential, AzureAuthorityHosts search_endpoint: str = "https://<Put your search service NAME here>.search.windows.net/" authority = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD credential = DefaultAzureCredential(authority=authority) index_name: str = "hotels-quickstart-python"
Verwijder de volgende twee regels uit de cel Een indexcode maken. Referenties zijn al ingesteld in de vorige codecel.
from azure.core.credentials import AzureKeyCredential credential = AzureKeyCredential(search_api_key)
Voer de cel Een indexcode maken uit om een zoekindex te maken.
Voer de resterende codecellen opeenvolgend uit om documenten te laden en query's uit te voeren.
Uitleg van de code
Een index maken
SearchIndexClient
wordt gebruikt voor het maken en beheren van indexen voor Azure AI Search. Elk veld wordt geïdentificeerd door een name
en heeft een opgegeven type
.
Elk veld heeft ook een reeks indexkenmerken die aangeven of Azure AI Search kan zoeken, filteren, sorteren en facet op het veld. De meeste velden zijn eenvoudige gegevenstypen, maar een aantal, zoals AddressType
, is van een complex type waarmee u uitgebreide gegevensstructuren in uw index kunt maken. Meer informatie over ondersteunde gegevenstypen en indexkenmerken die worden beschreven in Create Index (REST).
Een nettolading van documenten maken en documenten uploaden
Gebruik een indexactie voor het bewerkingstype, zoals uploaden of samenvoegen en uploaden. Documenten zijn afkomstig van het Voorbeeld HotelsData op GitHub.
Een index doorzoeken
U kunt queryresultaten ophalen zodra het eerste document wordt geïndexeerd, maar wacht met het daadwerkelijk testen van uw index totdat alle documenten zijn geïndexeerd.
Gebruik de zoekmethode van de klasse search.client.
De voorbeeldquery's in het notebook zijn:
- Basisquery: voert een lege zoekopdracht (
search=*
) uit, retourneert een ongerankeerde lijst (zoekscore = 1,0) van willekeurige documenten. Omdat er geen criteria zijn, worden alle documenten opgenomen in de resultaten. - Termquery: voegt hele termen toe aan de zoekexpressie ('wifi'). Met deze query wordt opgegeven dat de resultaten alleen die velden bevatten in de
select
-instructie. Het beperken van de velden die worden geretourneerd, minimaliseert de hoeveelheid gegevens die via de kabel wordt verzonden en vermindert de zoeklatentie. - Gefilterde query: Voeg een filterexpressie toe, waarbij alleen hotels met een classificatie groter dan vier worden geretourneerd, gesorteerd in aflopende volgorde.
- Bereikveld: Toevoegen
search_fields
aan bereikquery-uitvoering aan specifieke velden. - Facetten: facetten genereren voor positieve overeenkomsten die in de zoekresultaten worden gevonden. Er zijn geen nulovereenkomsten. Als zoekresultaten de term wifi niet bevatten, wordt wifi niet weergegeven in de facetnavigatiestructuur.
- Een document opzoeken: een document retourneren op basis van de sleutel. Deze bewerking is handig als u drill through wilt opgeven wanneer een gebruiker een item in een zoekresultaat selecteert.
- Automatisch aanvullen: geef potentiële overeenkomsten op als de gebruiker in het zoekvak typt. Automatisch aanvullen maakt gebruik van een suggestie (
sg
) om te weten welke velden mogelijke overeenkomsten bevatten voor suggestieaanvragen. In deze quickstart zijnTags
deze velden , .Address/City
Address/Country
Als u automatisch aanvullen wilt simuleren, geeft u de letters sa door als een gedeeltelijke tekenreeks. Met de methode voor automatisch aanvullen van SearchClient worden mogelijke overeenkomende termen geretourneerd.
De index verwijderen
Als u klaar bent met deze index, kunt u deze verwijderen door de codecel Opschonen uit te voeren. Als u onnodige indexen verwijdert, wordt ruimte vrijgemaakt voor het doorlopen van meer quickstarts en zelfstudies.
Meer informatie over het gebruik van de clientbibliotheek Azure.Search.Documents om een zoekindex te maken, laden en er query's op uit te voeren met voorbeeldgegevens voor zoeken in volledige tekst. Zoeken in volledige tekst maakt gebruik van Apache Lucene voor indexering en query's en een BM25-classificatie-algoritme voor scoreresultaten.
In deze quickstart wordt een kleine index voor hotels-quickstart gemaakt en opgevraagd die gegevens over vier hotels bevat.
Tip
U kunt de broncode downloaden om te beginnen met een voltooid project of deze stappen volgen om uw eigen project te maken.
Vereisten
- Een actief Azure-abonnement - Een gratis abonnement maken
- Een Azure AI-Search-service. Maak een service als u er nog geen hebt. U kunt een gratis laag gebruiken voor deze quickstart.
Vereisten voor Microsoft Entra-id
Voor de aanbevolen sleutelloze verificatie met Microsoft Entra-id moet u het volgende doen:
- Installeer de Azure CLI die wordt gebruikt voor sleutelloze verificatie met Microsoft Entra-id.
- Wijs zowel de rollen als de
Search Service Contributor
Search Index Data Contributor
rollen toe aan uw gebruikersaccount. U kunt rollen toewijzen in Azure Portal onder Toegangsbeheer (IAM)>Roltoewijzing toevoegen. Zie Verbinding maken met Azure AI Search met behulp van rollen voor meer informatie.
Resourcegegevens ophalen
U moet de volgende informatie ophalen om uw toepassing te verifiëren met uw Azure AI-Search-service:
Naam van de variabele | Weergegeven als |
---|---|
SEARCH_API_ENDPOINT |
Deze waarde vindt u in Azure Portal. Selecteer uw zoekservice en selecteer vervolgens in het linkermenu Overzicht. De URL-waarde onder Essentials is het eindpunt dat u nodig hebt. Een eindpunt ziet er bijvoorbeeld uit als https://mydemo.search.windows.net . |
Meer informatie over sleutelloze verificatie en het instellen van omgevingsvariabelen.
Instellingen
Maak een nieuwe map
full-text-quickstart
die de toepassing bevat en open Visual Studio Code in die map met de volgende opdracht:mkdir full-text-quickstart && cd full-text-quickstart
Maak de
package.json
opdracht met de volgende opdracht:npm init -y
Werk de
package.json
app bij naar ECMAScript met de volgende opdracht:npm pkg set type=module
Installeer de Azure AI Search-clientbibliotheek (Azure.Search.Documents) voor JavaScript met:
npm install @azure/search-documents
Installeer voor de aanbevolen verificatie zonder wachtwoord de Azure Identity-clientbibliotheek met:
npm install @azure/identity
Een zoekindex maken, laden en er query's op uitvoeren
In de vorige sectie voor het instellen hebt u de Azure AI Search-clientbibliotheek en andere afhankelijkheden geïnstalleerd.
In deze sectie voegt u code toe om een zoekindex te maken, deze te laden met documenten en query's uit te voeren. U voert het programma uit om de resultaten in de console te zien. Zie de uitleg van de codesectie voor een gedetailleerde uitleg van de code .
De voorbeeldcode in deze quickstart maakt gebruik van Microsoft Entra-id voor de aanbevolen sleutelloze verificatie. Als u liever een API-sleutel gebruikt, kunt u het DefaultAzureCredential
object vervangen door een AzureKeyCredential
object.
const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
Maak een nieuw bestand met de naam index.ts en plak de volgende code in index.ts:
// Import from the @azure/search-documents library import { SearchIndexClient, SearchClient, SearchFieldDataType, AzureKeyCredential, odata, SearchIndex } from "@azure/search-documents"; // Import from the Azure Identity library import { DefaultAzureCredential } from "@azure/identity"; // Importing the hotels sample data import hotelData from './hotels.json' assert { type: "json" }; // Load the .env file if it exists import * as dotenv from "dotenv"; dotenv.config(); // Defining the index definition const indexDefinition: SearchIndex = { "name": "hotels-quickstart", "fields": [ { "name": "HotelId", "type": "Edm.String" as SearchFieldDataType, "key": true, "filterable": true }, { "name": "HotelName", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": false, "sortable": true, "facetable": false }, { "name": "Description", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzerName": "en.lucene" }, { "name": "Description_fr", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzerName": "fr.lucene" }, { "name": "Category", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "Tags", "type": "Collection(Edm.String)", "searchable": true, "filterable": true, "sortable": false, "facetable": true }, { "name": "ParkingIncluded", "type": "Edm.Boolean", "filterable": true, "sortable": true, "facetable": true }, { "name": "LastRenovationDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "facetable": true }, { "name": "Rating", "type": "Edm.Double", "filterable": true, "sortable": true, "facetable": true }, { "name": "Address", "type": "Edm.ComplexType", "fields": [ { "name": "StreetAddress", "type": "Edm.String" as SearchFieldDataType, "filterable": false, "sortable": false, "facetable": false, "searchable": true }, { "name": "City", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "StateProvince", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "PostalCode", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": true, "sortable": true, "facetable": true }, { "name": "Country", "type": "Edm.String" as SearchFieldDataType, "searchable": true, "filterable": true, "sortable": true, "facetable": true } ] } ], "suggesters": [ { "name": "sg", "searchMode": "analyzingInfixMatching", "sourceFields": [ "HotelName" ] } ] }; async function main() { // Your search service endpoint const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/"; // Use the recommended keyless credential instead of the AzureKeyCredential credential. const credential = new DefaultAzureCredential(); //const credential = new AzureKeyCredential(Your search service admin key); // Create a SearchIndexClient to send create/delete index commands const searchIndexClient: SearchIndexClient = new SearchIndexClient( searchServiceEndpoint, credential ); // Creating a search client to upload documents and issue queries const indexName: string = "hotels-quickstart"; const searchClient: SearchClient<any> = searchIndexClient.getSearchClient(indexName); console.log('Checking if index exists...'); await deleteIndexIfExists(searchIndexClient, indexName); console.log('Creating index...'); let index: SearchIndex = await searchIndexClient.createIndex(indexDefinition); console.log(`Index named ${index.name} has been created.`); console.log('Uploading documents...'); let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']); console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `); // waiting one second for indexing to complete (for demo purposes only) sleep(1000); console.log('Querying the index...'); console.log(); await sendQueries(searchClient); } async function deleteIndexIfExists(searchIndexClient: SearchIndexClient, indexName: string) { try { await searchIndexClient.deleteIndex(indexName); console.log('Deleting index...'); } catch { console.log('Index does not exist yet.'); } } async function sendQueries(searchClient: SearchClient<any>) { // Query 1 console.log('Query #1 - search everything:'); let searchOptions: any = { includeTotalCount: true, select: ["HotelId", "HotelName", "Rating"] }; let searchResults = await searchClient.search("*", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(`Result count: ${searchResults.count}`); console.log(); // Query 2 console.log('Query #2 - search with filter, orderBy, and select:'); let state = 'FL'; searchOptions = { filter: odata`Address/StateProvince eq ${state}`, orderBy: ["Rating desc"], select: ["HotelId", "HotelName", "Rating"] }; searchResults = await searchClient.search("wifi", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(); // Query 3 console.log('Query #3 - limit searchFields:'); searchOptions = { select: ["HotelId", "HotelName", "Rating"], searchFields: ["HotelName"] }; searchResults = await searchClient.search("sublime cliff", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(); // Query 4 console.log('Query #4 - limit searchFields and use facets:'); searchOptions = { facets: ["Category"], select: ["HotelId", "HotelName", "Rating"], searchFields: ["HotelName"] }; searchResults = await searchClient.search("*", searchOptions); for await (const result of searchResults.results) { console.log(`${JSON.stringify(result.document)}`); } console.log(); // Query 5 console.log('Query #5 - Lookup document:'); let documentResult = await searchClient.getDocument('3'); console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`); console.log(); } function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } main().catch((err) => { console.error("The sample encountered an error:", err); });
Maak een bestand met de naam hotels.json en plak de volgende code in hotels.json:
{ "value": [ { "HotelId": "1", "HotelName": "Secret Point Motel", "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.", "Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.", "Category": "Boutique", "Tags": ["pool", "air conditioning", "concierge"], "ParkingIncluded": false, "LastRenovationDate": "1970-01-18T00:00:00Z", "Rating": 3.6, "Address": { "StreetAddress": "677 5th Ave", "City": "New York", "StateProvince": "NY", "PostalCode": "10022" } }, { "HotelId": "2", "HotelName": "Twin Dome Motel", "Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.", "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.", "Category": "Boutique", "Tags": ["pool", "free wifi", "concierge"], "ParkingIncluded": "false", "LastRenovationDate": "1979-02-18T00:00:00Z", "Rating": 3.6, "Address": { "StreetAddress": "140 University Town Center Dr", "City": "Sarasota", "StateProvince": "FL", "PostalCode": "34243" } }, { "HotelId": "3", "HotelName": "Triple Landscape Hotel", "Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.", "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.", "Category": "Resort and Spa", "Tags": ["air conditioning", "bar", "continental breakfast"], "ParkingIncluded": "true", "LastRenovationDate": "2015-09-20T00:00:00Z", "Rating": 4.8, "Address": { "StreetAddress": "3393 Peachtree Rd", "City": "Atlanta", "StateProvince": "GA", "PostalCode": "30326" } }, { "HotelId": "4", "HotelName": "Sublime Cliff Hotel", "Description": "Sublime Cliff Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace.", "Description_fr": "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.", "Category": "Boutique", "Tags": ["concierge", "view", "24-hour front desk service"], "ParkingIncluded": true, "LastRenovationDate": "1960-02-06T00:00:00Z", "Rating": 4.6, "Address": { "StreetAddress": "7400 San Pedro Ave", "City": "San Antonio", "StateProvince": "TX", "PostalCode": "78216" } } ] }
Maak het
tsconfig.json
bestand om de TypeScript-code te transpileren en kopieer de volgende code voor ECMAScript.{ "compilerOptions": { "module": "NodeNext", "target": "ES2022", // Supports top-level await "moduleResolution": "NodeNext", "skipLibCheck": true, // Avoid type errors from node_modules "strict": true // Enable strict type-checking options }, "include": ["*.ts"] }
Transpile van TypeScript naar JavaScript.
tsc
Meld u aan bij Azure met de volgende opdracht:
az login
Voer de JavaScript-code uit met de volgende opdracht:
node index.js
Uitleg van de code
Index maken
Maak een bestand hotels_quickstart_index.json. Dit bestand definieert hoe Azure AI Search werkt met de documenten die u in de volgende stap laadt. Elk veld wordt geïdentificeerd door een name
en heeft een opgegeven type
. Elk veld heeft ook een reeks indexkenmerken die aangeven of Azure AI Search kan zoeken, filteren, sorteren en facet op het veld. De meeste velden zijn eenvoudige gegevenstypen, maar een aantal, zoals AddressType
, is van een complex type waarmee u uitgebreide gegevensstructuren in uw index kunt maken. Meer informatie over ondersteunde gegevenstypen en indexkenmerken die worden beschreven in Create Index (REST).
We willen hotels_quickstart_index.json importeren, zodat de hoofdfunctie toegang heeft tot de indexdefinitie.
import indexDefinition from './hotels_quickstart_index.json';
interface HotelIndexDefinition {
name: string;
fields: SimpleField[] | ComplexField[];
suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;
In de hoofdfunctie maken we vervolgens een SearchIndexClient
, die wordt gebruikt voor het maken en beheren van indexen voor Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
De index moet worden verwijderd als deze al bestaat. Deze bewerking is een veelvoorkomende procedure voor test-/democode.
Dit doet u door een eenvoudige functie te definiëren die probeert om de index te verwijderen.
async function deleteIndexIfExists(indexClient: SearchIndexClient, indexName: string): Promise<void> {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Als u de functie wilt uitvoeren, halen we de indexnaam op uit de indexdefinitie en geven we de indexName
door met de indexClient
aan de functie deleteIndexIfExists()
.
// Getting the name of the index from the index definition
const indexName: string = hotelIndexDefinition.name;
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
Daarna kunt u de index maken met behulp van de methode createIndex()
.
console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);
console.log(`Index named ${index.name} has been created.`);
Documenten laden
In Azure AI Search zijn documenten gegevensstructuren die zowel invoer zijn voor het indexeren als uitvoeren van query's. U kunt dergelijke gegevens naar de index pushen of een Indexeerfunctie gebruiken. In dat geval kunnen we de documenten via een programma naar de index pushen.
De documentinvoer bestaat mogelijk uit rijen in een database, blobs in Blob Storage of, zoals in dit voorbeeld, JSON-documenten op een schijf. U kunt hotels.json downloaden of uw eigen hotels.json-bestand maken met de volgende inhoud:
Net als bij indexDefinition moeten we ook bovenaan index.ts importeren hotels.json
, zodat de gegevens kunnen worden geopend in onze hoofdfunctie.
import hotelData from './hotels.json';
interface Hotel {
HotelId: string;
HotelName: string;
Description: string;
Description_fr: string;
Category: string;
Tags: string[];
ParkingIncluded: string | boolean;
LastRenovationDate: string;
Rating: number;
Address: {
StreetAddress: string;
City: string;
StateProvince: string;
PostalCode: string;
};
};
const hotels: Hotel[] = hotelData["value"];
Als u gegevens in de zoekindex wilt indexeren, moet u nu een SearchClient
maken. Terwijl de SearchIndexClient
wordt gebruikt voor het maken en beheren van een index, wordt de SearchClient
gebruikt voor het uploaden van documenten en het uitvoeren van query's op de index.
Er zijn twee manieren om een SearchClient
maken. De eerste optie is het maken van een geheel nieuwe SearchClient
:
const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));
U kunt ook de methode getSearchClient()
van de SearchIndexClient
gebruiken om de SearchClient
te maken:
const searchClient = indexClient.getSearchClient<Hotel>(indexName);
Nu de client is gedefinieerd, uploadt u de documenten in de zoekindex. In dit geval gebruiken we de mergeOrUploadDocuments()
methode, waarmee de documenten worden geüpload of samengevoegd met een bestaand document als er al een document met dezelfde sleutel bestaat. Controleer vervolgens of de bewerking is geslaagd omdat ten minste het eerste document bestaat.
console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Voer het programma opnieuw uit met tsc && node index.ts
. U ziet nu een iets andere reeks berichten dan in stap 1. Nu bestaat de index wel en u zou een bericht moeten zien over het verwijderen van de index voordat de nieuwe index wordt gemaakt en hierin gegevens worden geplaatst.
Voordat we de query's in de volgende stap uitvoeren, definieert u een functie om het programma één seconde te laten wachten. Dit is alleen bedoeld voor test- en demonstratiedoeleinden om te controleren of de indexering is voltooid en of de documenten beschikbaar zijn in de index voor onze query's.
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Als u het programma één seconde wilt laten wachten, roept u de sleep
functie aan:
sleep(1000);
Een index doorzoeken
Als er een index is gemaakt en documenten zijn geüpload, bent u klaar om query's naar de index te verzenden. In deze sectie verzenden we vijf verschillende query's naar de zoekindex om verschillende onderdelen van de queryfunctionaliteit te demonstreren die voor u beschikbaar zijn.
De query's worden als volgt geschreven in een sendQueries()
functie die we in de hoofdfunctie aanroepen:
await sendQueries(searchClient);
Query's worden verzonden met behulp van de methode search()
van searchClient
. De eerste parameter is de zoektekst en de tweede parameter specificeert zoekopties.
Queryvoorbeeld 1
De eerste query zoekt *
, wat gelijk is aan ‘zoeken in alle’ en selecteert drie van de velden in de index. Het is een best practice om alleen de velden te select
die u nodig hebt, omdat het ophalen van overbodige gegevens een latentie kan veroorzaken bij uw query's.
De searchOptions
voor deze query heeft ook includeTotalCount
ingesteld op true
, waardoor het aantal gevonden resultaten wat overeenkomt wordt geretourneerd.
async function sendQueries(
searchClient: SearchClient<Hotel>
): Promise<void> {
// Query 1
console.log('Query #1 - search everything:');
const selectFields: SearchFieldArray<Hotel> = [
"HotelId",
"HotelName",
"Rating",
];
const searchOptions1 = {
includeTotalCount: true,
select: selectFields
};
let searchResults = await searchClient.search("*", searchOptions1);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
console.log(`Result count: ${searchResults.count}`);
// remaining queries go here
}
De resterende query's die hieronder worden beschreven, moeten ook worden toegevoegd aan de functie sendQueries()
. Ze zijn hier gescheiden voor leesbaarheid.
Queryvoorbeeld 2
In de volgende query geven we de zoekterm "wifi"
op en nemen we ook een filter op om alleen resultaten te retourneren waarvan de status gelijk is aan 'FL'
. Resultaten worden ook gerangschikt op de Rating
van het hotel.
console.log('Query #2 - search with filter, orderBy, and select:');
let state = 'FL';
const searchOptions2 = {
filter: odata`Address/StateProvince eq ${state}`,
orderBy: ["Rating desc"],
select: selectFields
};
searchResults = await searchClient.search("wifi", searchOptions2);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Queryvoorbeeld 3
Vervolgens kan de zoekopdracht worden beperkt tot één doorzoekbaar veld met behulp van de parameter searchFields
. Deze methode is een uitstekende optie om uw query efficiënter te maken als u weet dat u alleen geïnteresseerd bent in overeenkomsten in bepaalde velden.
console.log('Query #3 - limit searchFields:');
const searchOptions3 = {
select: selectFields,
searchFields: ["HotelName"] as const
};
searchResults = await searchClient.search("Sublime Palace", searchOptions3);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Queryvoorbeeld 4
Een andere veelgebruikte optie die in een query moet worden opgenomen, is facets
. Met facetten kunt u zelfgestuurd inzoomen op de resultaten in uw gebruikersinterface. De resultaten van facetten kunnen worden omgezet in selectievakjes in het resultaatvenster.
console.log('Query #4 - limit searchFields and use facets:');
const searchOptions4 = {
facets: ["Category"],
select: selectFields,
searchFields: ["HotelName"] as const
};
searchResults = await searchClient.search("*", searchOptions4);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Queryvoorbeeld 5
De laatste query maakt gebruik van de methode getDocument()
van de searchClient
. Zo kunt u een document efficiënt ophalen met de bijbehorende sleutel.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Samenvatting van query's
De vorige query's tonen meerdere manieren om termen in een query te koppelen: zoekopdracht in volledige tekst, filters en automatisch aanvullen.
Zoekopdrachten en filters voor volledige tekst worden uitgevoerd met behulp van de searchClient.search
methode. Een zoekquery kan worden doorgegeven in de searchText
tekenreeks, terwijl een filterexpressie kan worden doorgegeven in de filter
eigenschap van de SearchOptions
klasse. Als u wilt filteren zonder te zoeken, geeft u '*' door voor de searchText
parameter van de search
methode. Als u wilt zoeken zonder te filteren, laat u de filter
eigenschap uitgeschakeld of geeft u een SearchOptions
instantie helemaal niet door.
Resources opschonen
Wanneer u in uw eigen abonnement werkt, is het een goed idee om aan het einde van een project te bepalen of u de gemaakte resources nog nodig hebt. Resources die actief blijven, kunnen u geld kosten. U kunt resources afzonderlijk verwijderen, maar u kunt ook de resourcegroep verwijderen als u de volledige resourceset wilt verwijderen.
U kunt resources vinden en beheren in Azure Portal met behulp van de koppeling Alle resources of resourcegroepen in het linkernavigatiedeelvenster.
Als u een gratis service gebruikt, moet u er rekening mee houden dat u beperkt bent tot drie indexen, indexeerfuncties en gegevensbronnen. U kunt afzonderlijke items in Azure Portal verwijderen om onder de limiet te blijven.
Volgende stap
In deze quickstart hebt u een set taken doorlopen om een index te maken, deze te laden met documenten en query's uit te voeren. In verschillende fasen hebben we korte routes genomen om de code te vereenvoudigen voor leesbaarheid en begrijpelijkheid. Nu u bekend bent met de basisconcepten, kunt u een zelfstudie uitproberen waarmee de Azure AI Search-API's in een web-app worden aangeroepen.