Snabbstart: Fulltextsökning med hjälp av Azure SDK:er
Lär dig hur du använder klientbiblioteket Azure.Search.Documents för att skapa, läsa in och köra frågor mot ett sökindex med hjälp av exempeldata för fulltextsökning. Fulltextsökning använder Apache Lucene för indexering och frågor samt en BM25-rangordningsalgoritm för bedömning av resultat.
Den här snabbstarten skapar och frågar ett litet hotell-snabbstartsindex som innehåller data om fyra hotell.
Dricks
Du kan ladda ned källkoden för att börja med ett färdigt projekt eller följa dessa steg för att skapa en egen.
Förutsättningar
- En aktiv Azure-prenumeration – Skapa en kostnadsfritt
- En Azure AI-tjänsten Search. Skapa en tjänst om du inte har någon. Du kan använda en kostnadsfri nivå för den här snabbstarten.
Krav för Microsoft Entra-ID
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID måste du:
- Installera Azure CLI som används för nyckellös autentisering med Microsoft Entra-ID.
- Tilldela både rollerna
Search Service Contributor
ochSearch Index Data Contributor
till ditt användarkonto. Du kan tilldela roller i Azure Portal under Åtkomstkontroll (IAM)>Lägg till rolltilldelning. Mer information finns i Ansluta till Azure AI Search med hjälp av roller.
Hämta resursinformation
Du måste hämta följande information för att autentisera ditt program med din Azure AI-tjänsten Search:
Variabelnamn | Värde |
---|---|
SEARCH_API_ENDPOINT |
Det här värdet finns i Azure Portal. Välj söktjänsten och välj sedan Översikt på den vänstra menyn.
Url-värdet under Essentials är den slutpunkt som du behöver. Här följer ett exempel på hur en slutpunkt kan se ut: https://mydemo.search.windows.net . |
Läs mer om nyckellös autentisering och inställning av miljövariabler.
Konfigurera
Skapa en ny mapp
full-text-quickstart
som ska innehålla programmet och öppna Visual Studio Code i mappen med följande kommando:mkdir full-text-quickstart && cd full-text-quickstart
Skapa ett nytt konsolprogram med följande kommando:
dotnet new console
Installera Azure AI Search-klientbiblioteket (Azure.Search.Documents) för .NET med:
dotnet add package Azure.Search.Documents
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID installerar du Azure.Identity-paketet med:
dotnet add package Azure.Identity
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID loggar du in på Azure med följande kommando:
az login
Skapa, läsa in och fråga ett sökindex
I föregående konfigurationsavsnitt skapade du ett nytt konsolprogram och installerade Azure AI Search-klientbiblioteket.
I det här avsnittet lägger du till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du kör programmet för att se resultatet i konsolen. En detaljerad förklaring av koden finns i avsnittet förklara koden .
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet DefaultAzureCredential
med ett AzureKeyCredential
objekt.
Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
I Program.cs klistrar du in följande kod. Redigera variablerna
serviceName
ochapiKey
med söktjänstens namn och administratörs-API-nyckel.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(); } } }
I samma mapp skapar du en ny fil med namnet Hotel.cs och klistrar in följande kod. Den här koden definierar strukturen för ett hotelldokument.
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; } } }
Skapa en ny fil med namnet Hotel.cs och klistra in följande kod för att definiera strukturen för ett hotelldokument. Attribut i fältet avgör hur det används i ett program. Attributet måste till exempel
IsFilterable
tilldelas till varje fält som stöder ett filteruttryck.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; } } }
Skapa en ny fil med namnet Address.cs och klistra in följande kod för att definiera strukturen för ett adressdokument.
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; } } }
Skapa en ny fil med namnet Hotel.Methods.cs och klistra in följande kod för att definiera en
ToString()
åsidosättning förHotel
klassen.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(); } } }
Skapa en ny fil med namnet Address.Methods.cs och klistra in följande kod för att definiera en
ToString()
åsidosättning förAddress
klassen.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); } }
Skapa och kör programmet med följande kommando:
dotnet run
Utdata innehåller meddelanden från Console.WriteLine, med tillägg av frågeinformation och resultat.
Förklara koden
I föregående avsnitt skapade du ett nytt konsolprogram och installerade Azure AI Search-klientbiblioteket. Du har lagt till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du körde programmet för att se resultatet i konsolen.
I det här avsnittet förklarar vi koden som du lade till i konsolprogrammet.
Skapa en sökklient
I Program.cs skapade du två klienter:
- SearchIndexClient skapar indexet.
- SearchClient läser in och frågar ett befintligt index.
Båda klienterna behöver slutpunkten för söktjänsten och autentiseringsuppgifterna som beskrevs tidigare i avsnittet resursinformation .
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet DefaultAzureCredential
med ett AzureKeyCredential
objekt.
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 . . .
}
Skapa ett index
Den här snabbstarten skapar ett Hotell-index som du läser in med hotelldata och kör frågor mot. I det här steget definierar du fälten i indexet. Varje fältdefinition innehåller ett namn, en datatyp och attribut som avgör hur fältet används.
I det här exemplet används synkrona metoder i biblioteket Azure.Search.Documents för enkelhet och läsbarhet. För produktionsscenarier bör du dock använda asynkrona metoder för att hålla appen skalbar och dynamisk. Du skulle till exempel använda CreateIndexAsync i stället för CreateIndex.
Definiera strukturerna
Du skapade två hjälpklasser, Hotel.cs och Address.cs, för att definiera strukturen för ett hotelldokument och dess adress. Klassen Hotel
innehåller fält för ett hotell-ID, namn, beskrivning, kategori, taggar, parkering, renoveringsdatum, betyg och adress. Klassen Address
innehåller fält för gatuadress, ort, delstat/provins, postnummer och land/region.
I azure.search.documents-klientbiblioteket kan du använda SearchableField och SimpleField för att effektivisera fältdefinitioner. Båda är derivat av ett SearchField och kan förenkla koden:
SimpleField
kan vara vilken datatyp som helst, är alltid inte sökbar (ignoreras för fulltextsökningsfrågor) och kan hämtas (inte dold). Andra attribut är inaktiverade som standard, men kan aktiveras. Du kan använda ettSimpleField
för dokument-ID eller fält som endast används i filter, fasetter eller bedömningsprofiler. I så fall bör du tillämpa eventuella attribut som är nödvändiga för scenariot, till exempelIsKey = true
för ett dokument-ID. Mer information finns i SimpleFieldAttribute.cs i källkoden.SearchableField
måste vara en sträng och är alltid sökbar och hämtningsbar. Andra attribut är inaktiverade som standard, men kan aktiveras. Eftersom den här fälttypen är sökbar stöder den synonymer och det fullständiga komplementet av analysverktygsegenskaper. Mer information finns i SearchableFieldAttribute.cs i källkoden.
Oavsett om du använder det grundläggande SearchField
API:et eller någon av hjälpmodellerna måste du uttryckligen aktivera attribut för filter, fasetter och sortering. Till exempel måste IsFilterable, IsSortable och IsFacetable uttryckligen tillskrivas, som du ser i föregående exempel.
Skapa sökindexet
I Program.cs skapar du ett SearchIndex-objekt och anropar sedan metoden CreateIndex för att uttrycka indexet i söktjänsten. Indexet innehåller också en SearchSuggester för att aktivera automatisk komplettering i de angivna fälten.
// 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);
}
Läsa in dokument
Azure AI Search söker efter innehåll som lagras i tjänsten. I det här steget läser du in JSON-dokument som överensstämmer med hotellindexet du skapade.
I Azure AI Search är sökdokument datastrukturer som både är indata för indexering och utdata från frågor. Som hämtats från en extern datakälla kan dokumentindata vara rader i en databas, blobar i Blob Storage eller JSON-dokument på disk. I det här exemplet tar vi en genväg och bäddar in JSON-dokument för fyra hotell i själva koden.
När du laddar upp dokument måste du använda ett IndexDocumentsBatch-objekt . Ett IndexDocumentsBatch
objekt innehåller en samling åtgärder, som var och en innehåller ett dokument och en egenskap som talar om för Azure AI Search vilken åtgärd som ska utföras (ladda upp, slå samman, ta bort och slå sammanEllerUpload).
I Program.cs skapar du en matris med dokument och indexåtgärder och skickar sedan matrisen till IndexDocumentsBatch
. Följande dokument överensstämmer med indexet hotels-quickstart enligt definitionen i hotellklassen.
// 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
}
När du har initierat Objektet IndexDocumentsBatch kan du skicka det till indexet genom att anropa IndexDocuments på ditt SearchClient-objekt .
Du läser in dokument med SearchClient i Main()
, men åtgärden kräver även administratörsbehörighet för tjänsten, som vanligtvis är associerad med SearchIndexClient. Ett sätt att konfigurera den här åtgärden är att hämta SearchClient genom SearchIndexClient
(searchIndexClient
i det här exemplet).
SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);
// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);
Eftersom vi har en konsolapp som kör alla kommandon sekventiellt lägger vi till en väntetid på 2 sekunder mellan indexering och frågor.
// 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);
Fördröjningen på 2 sekunder kompenserar för indexering, vilket är asynkront, så att alla dokument kan indexeras innan frågorna körs. Kodning i en fördröjning är vanligtvis bara nödvändigt i demonstrationer, tester och exempelprogram.
Sök i ett index
Du kan få frågeresultat så snart det första dokumentet indexeras, men den faktiska testningen av indexet bör vänta tills alla dokument har indexerats.
Det här avsnittet lägger till två funktioner: frågelogik och resultat. För frågor använder du sökmetoden . Den här metoden tar söktext (frågesträngen) och andra alternativ.
Klassen SearchResults representerar resultatet.
I Program.csWriteDocuments
skriver metoden ut sökresultat till konsolen.
// 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();
}
Frågeexempel 1
Metoden RunQueries
kör frågor och returnerar resultat. Resultatet är Hotellobjekt. Det här exemplet visar metodsignaturen och den första frågan. Den här frågan visar parametern Select
som gör att du kan skapa resultatet med hjälp av valda fält från dokumentet.
// 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
}
Frågeexempel 2
I den andra frågan söker du efter en term, lägger till ett filter som väljer dokument där Omdöme är större än 4 och sorterar sedan efter Klassificering i fallande ordning. Filter är ett booleskt uttryck som utvärderas över IsFilterable-fält i ett index. Filtrera frågor antingen inkludera eller exkludera värden. Därför finns det ingen relevanspoäng associerad med en filterfråga.
// 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);
Frågeexempel 3
Den tredje frågan visar searchFields
, som används för att begränsa en fulltextsökningsåtgärd till specifika fält.
// 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);
Frågeexempel 4
Den fjärde frågan visar facets
, som kan användas för att strukturera en fasetterad navigeringsstruktur.
// 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);
Frågeexempel 5
I den femte frågan returnerar du ett specifikt dokument. Ett dokumentuppslag är ett typiskt svar på OnClick
händelsen i en resultatuppsättning.
// 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);
Frågeexempel 6
Den sista frågan visar syntaxen för automatisk komplettering, som simulerar en partiell användarinmatning av sa som matchar två möjliga matchningar i de sourceFields som är associerade med den förslagsanvändare som du definierade i indexet.
// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
var autoresponse = searchClient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);
Sammanfattning av frågor
Föregående frågor visar flera sätt att matcha termer i en fråga: fulltextsökning, filter och automatisk komplettering.
Fulltextsökning och -filter utförs med metoden SearchClient.Search . En sökfråga kan skickas i strängensearchText
, medan ett filteruttryck kan skickas i filteregenskapen för klassen SearchOptions. Om du vill filtrera utan att söka skickar du "*"
bara efter parametern searchText
för sökmetoden . Om du vill söka utan filtrering lämnar du Filter
egenskapen oetig eller skickar inte någon SearchOptions
instans alls.
Lär dig hur du använder klientbiblioteket Azure.Search.Documents för att skapa, läsa in och köra frågor mot ett sökindex med hjälp av exempeldata för fulltextsökning. Fulltextsökning använder Apache Lucene för indexering och frågor samt en BM25-rangordningsalgoritm för bedömning av resultat.
Den här snabbstarten skapar och frågar ett litet hotell-snabbstartsindex som innehåller data om fyra hotell.
Dricks
Du kan ladda ned källkoden för att börja med ett färdigt projekt eller följa dessa steg för att skapa en egen.
Förutsättningar
- En aktiv Azure-prenumeration – Skapa en kostnadsfritt
- En Azure AI-tjänsten Search. Skapa en tjänst om du inte har någon. Du kan använda en kostnadsfri nivå för den här snabbstarten.
Krav för Microsoft Entra-ID
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID måste du:
- Installera Azure CLI som används för nyckellös autentisering med Microsoft Entra-ID.
- Tilldela både rollerna
Search Service Contributor
ochSearch Index Data Contributor
till ditt användarkonto. Du kan tilldela roller i Azure Portal under Åtkomstkontroll (IAM)>Lägg till rolltilldelning. Mer information finns i Ansluta till Azure AI Search med hjälp av roller.
Hämta resursinformation
Du måste hämta följande information för att autentisera ditt program med din Azure AI-tjänsten Search:
Variabelnamn | Värde |
---|---|
SEARCH_API_ENDPOINT |
Det här värdet finns i Azure Portal. Välj söktjänsten och välj sedan Översikt på den vänstra menyn.
Url-värdet under Essentials är den slutpunkt som du behöver. Här följer ett exempel på hur en slutpunkt kan se ut: https://mydemo.search.windows.net . |
Läs mer om nyckellös autentisering och inställning av miljövariabler.
Konfigurera
Exemplet i den här snabbstarten fungerar med Java Runtime. Installera ett Java Development Kit, till exempel Azul Zulu OpenJDK. Microsoft Build of OpenJDK eller din önskade JDK bör också fungera.
Installera Apache Maven. Kör
mvn -v
sedan för att bekräfta att installationen har slutförts.Skapa en ny
pom.xml
fil i roten av projektet och kopiera följande kod till den:<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>
Installera beroendena, inklusive Azure AI Search-klientbiblioteket (Azure.Search.Documents) för Java- och Azure Identity-klientbiblioteket för Java med:
mvn clean dependency:copy-dependencies
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID loggar du in på Azure med följande kommando:
az login
Skapa, läsa in och fråga ett sökindex
I föregående konfigurationsavsnitt installerade du Azure AI Search-klientbiblioteket och andra beroenden.
I det här avsnittet lägger du till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du kör programmet för att se resultatet i konsolen. En detaljerad förklaring av koden finns i avsnittet förklara koden .
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet DefaultAzureCredential
med ett AzureKeyCredential
objekt.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
Skapa en ny fil med namnet App.java och klistra in följande kod i 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")); } }
Skapa en ny fil med namnet Hotel.java och klistra in följande kod i 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 ""; } } }
Skapa en ny fil med namnet Address.java och klistra in följande kod i 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; }
Kör det nya konsolprogrammet:
javac Address.java App.java Hotel.java -cp ".;target\dependency\*" java -cp ".;target\dependency\*" App
Förklara koden
I föregående avsnitt skapade du ett nytt konsolprogram och installerade Azure AI Search-klientbiblioteket. Du har lagt till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du körde programmet för att se resultatet i konsolen.
I det här avsnittet förklarar vi koden som du lade till i konsolprogrammet.
Skapa en sökklient
I App.java skapade du två klienter:
- SearchIndexClient skapar indexet.
- SearchClient läser in och frågar ett befintligt index.
Båda klienterna behöver slutpunkten för söktjänsten och autentiseringsuppgifterna som beskrevs tidigare i avsnittet resursinformation.
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet DefaultAzureCredential
med ett AzureKeyCredential
objekt.
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 . . .
}
Skapa ett index
Den här snabbstarten skapar ett Hotell-index som du läser in med hotelldata och kör frågor mot. I det här steget definierar du fälten i indexet. Varje fältdefinition innehåller ett namn, en datatyp och attribut som avgör hur fältet används.
I det här exemplet används synkrona metoder i biblioteket Azure.Search.Documents för enkelhet och läsbarhet. För produktionsscenarier bör du dock använda asynkrona metoder för att hålla appen skalbar och dynamisk. Du skulle till exempel använda CreateIndexAsync i stället för CreateIndex.
Definiera strukturerna
Du skapade två hjälpklasser, Hotel.java och Address.java, för att definiera strukturen för ett hotelldokument och dess adress. Hotellklassen innehåller fält för ett hotell-ID, namn, beskrivning, kategori, taggar, parkering, renoveringsdatum, betyg och adress. Klassen Address innehåller fält för gatuadress, ort, delstat/provins, postnummer och land/region.
I azure.search.documents-klientbiblioteket kan du använda SearchableField och SimpleField för att effektivisera fältdefinitioner.
-
SimpleField
kan vara vilken datatyp som helst, är alltid inte sökbar (ignoreras för fulltextsökningsfrågor) och kan hämtas (inte dold). Andra attribut är inaktiverade som standard, men kan aktiveras. Du kan använda simplefield för dokument-ID:er eller fält som endast används i filter, fasetter eller bedömningsprofiler. I så fall måste du tillämpa eventuella attribut som är nödvändiga för scenariot, till exempel IsKey = true för ett dokument-ID. -
SearchableField
måste vara en sträng och är alltid sökbar och hämtningsbar. Andra attribut är inaktiverade som standard, men kan aktiveras. Eftersom den här fälttypen är sökbar stöder den synonymer och det fullständiga komplementet av analysverktygsegenskaper.
Oavsett om du använder det grundläggande SearchField
API:et eller någon av hjälpmodellerna måste du uttryckligen aktivera attribut för filter, fasetter och sortering. Till exempel isFilterable
, isSortable
, och isFacetable
måste uttryckligen tillskrivas, som du ser i föregående exempel.
Skapa sökindexet
I App.java
skapar du ett SearchIndex
objekt i main
metoden och anropar createOrUpdateIndex
sedan metoden för att skapa indexet i söktjänsten. Indexet innehåller även en SearchSuggester
för att aktivera automatisk komplettering på de angivna fälten.
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
Läsa in dokument
Azure AI Search söker efter innehåll som lagras i tjänsten. I det här steget läser du in JSON-dokument som överensstämmer med hotellindexet du skapade.
I Azure AI Search är sökdokument datastrukturer som både är indata för indexering och utdata från frågor. Som hämtats från en extern datakälla kan dokumentindata vara rader i en databas, blobar i Blob Storage eller JSON-dokument på disk. I det här exemplet tar vi en genväg och bäddar in JSON-dokument för fyra hotell i själva koden.
När du laddar upp dokument måste du använda ett IndexDocumentsBatch-objekt . Ett IndexDocumentsBatch
objekt innehåller en samling IndexActions, som var och en innehåller ett dokument och en egenskap som talar om för Azure AI Search vilken åtgärd som ska utföras (ladda upp, slå samman, ta bort och mergeOrUpload).
I App.java
skapar du dokument och indexåtgärder och skickar dem sedan till IndexDocumentsBatch
. Följande dokument överensstämmer med indexet hotels-quickstart enligt definitionen i hotellklassen.
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");
}
}
När du har initierat IndexDocumentsBatch
objektet kan du skicka det till indexet genom att anropa indexDocuments för objektet SearchClient
.
Du läser in dokument med SearchClient i main()
, men åtgärden kräver även administratörsbehörighet för tjänsten, som vanligtvis är associerad med SearchIndexClient. Ett sätt att konfigurera den här åtgärden är att hämta SearchClient genom SearchIndexClient
(searchIndexClient
i det här exemplet).
uploadDocuments(searchClient);
Eftersom vi har en konsolapp som kör alla kommandon sekventiellt lägger vi till en väntetid på 2 sekunder mellan indexering och frågor.
// 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)
{
}
Fördröjningen på 2 sekunder kompenserar för indexering, vilket är asynkront, så att alla dokument kan indexeras innan frågorna körs. Kodning i en fördröjning är vanligtvis bara nödvändigt i demonstrationer, tester och exempelprogram.
Sök i ett index
Du kan få frågeresultat så snart det första dokumentet indexeras, men den faktiska testningen av indexet bör vänta tills alla dokument har indexerats.
Det här avsnittet lägger till två funktioner: frågelogik och resultat. För frågor använder du sökmetoden. Den här metoden tar söktext (frågesträngen) och andra alternativ.
WriteDocuments
I App.java
skriver metoden ut sökresultat till konsolen.
// 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();
}
Frågeexempel 1
Metoden RunQueries
kör frågor och returnerar resultat. Resultatet är Hotellobjekt. Det här exemplet visar metodsignaturen och den första frågan. Den här frågan visar parametern Select
som gör att du kan skapa resultatet med hjälp av valda fält från dokumentet.
// 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));
}
Frågeexempel 2
I den andra frågan söker du efter en term, lägger till ett filter som väljer dokument där Omdöme är större än 4 och sorterar sedan efter Klassificering i fallande ordning. Filter är ett booleskt uttryck som utvärderas över isFilterable
fält i ett index. Filtrera frågor antingen inkludera eller exkludera värden. Därför finns det ingen relevanspoäng associerad med en filterfråga.
// 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));
Frågeexempel 3
Den tredje frågan visar searchFields
, som används för att begränsa en fulltextsökningsåtgärd till specifika fält.
// 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));
Frågeexempel 4
Den fjärde frågan visar facets
, som kan användas för att strukturera en fasetterad navigeringsstruktur.
// 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));
Frågeexempel 5
I den femte frågan returnerar du ett specifikt dokument.
// 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();
Frågeexempel 6
Den sista frågan visar syntaxen för automatisk komplettering, vilket simulerar en partiell användarinmatning av s som matchar två möjliga matchningar i den sourceFields
associerade med den förslagsanvändare som du definierade i indexet.
// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
Sammanfattning av frågor
Föregående frågor visar flera sätt att matcha termer i en fråga: fulltextsökning, filter och automatisk komplettering.
Fulltextsökning och -filter utförs med metoden SearchClient.search . En sökfråga kan skickas i strängen searchText
, medan ett filteruttryck kan skickas i filter
egenskapen för klassen SearchOptions . Om du vill filtrera utan att söka skickar du bara "*" för parametern searchText
för search
metoden. Om du vill söka utan filtrering lämnar du filter
egenskapen oetig eller skickar inte någon SearchOptions
instans alls.
Lär dig hur du använder klientbiblioteket Azure.Search.Documents för att skapa, läsa in och köra frågor mot ett sökindex med hjälp av exempeldata för fulltextsökning. Fulltextsökning använder Apache Lucene för indexering och frågor samt en BM25-rangordningsalgoritm för bedömning av resultat.
Den här snabbstarten skapar och frågar ett litet hotell-snabbstartsindex som innehåller data om fyra hotell.
Dricks
Du kan ladda ned källkoden för att börja med ett färdigt projekt eller följa dessa steg för att skapa en egen.
Förutsättningar
- En aktiv Azure-prenumeration – Skapa en kostnadsfritt
- En Azure AI-tjänsten Search. Skapa en tjänst om du inte har någon. Du kan använda en kostnadsfri nivå för den här snabbstarten.
Krav för Microsoft Entra-ID
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID måste du:
- Installera Azure CLI som används för nyckellös autentisering med Microsoft Entra-ID.
- Tilldela både rollerna
Search Service Contributor
ochSearch Index Data Contributor
till ditt användarkonto. Du kan tilldela roller i Azure Portal under Åtkomstkontroll (IAM)>Lägg till rolltilldelning. Mer information finns i Ansluta till Azure AI Search med hjälp av roller.
Hämta resursinformation
Du måste hämta följande information för att autentisera ditt program med din Azure AI-tjänsten Search:
Variabelnamn | Värde |
---|---|
SEARCH_API_ENDPOINT |
Det här värdet finns i Azure Portal. Välj söktjänsten och välj sedan Översikt på den vänstra menyn.
Url-värdet under Essentials är den slutpunkt som du behöver. Här följer ett exempel på hur en slutpunkt kan se ut: https://mydemo.search.windows.net . |
Läs mer om nyckellös autentisering och inställning av miljövariabler.
Konfigurera
Skapa en ny mapp
full-text-quickstart
som ska innehålla programmet och öppna Visual Studio Code i mappen med följande kommando:mkdir full-text-quickstart && cd full-text-quickstart
package.json
Skapa med följande kommando:npm init -y
Installera Azure AI Search-klientbiblioteket (Azure.Search.Documents) för JavaScript med:
npm install @azure/search-documents
För den rekommenderade lösenordslösa autentiseringen installerar du Azure Identity-klientbiblioteket med:
npm install @azure/identity
Skapa, läsa in och fråga ett sökindex
I föregående konfigurationsavsnitt installerade du Azure AI Search-klientbiblioteket och andra beroenden.
I det här avsnittet lägger du till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du kör programmet för att se resultatet i konsolen. En detaljerad förklaring av koden finns i avsnittet förklara koden .
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet DefaultAzureCredential
med ett AzureKeyCredential
objekt.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
Skapa en ny fil med namnet index.js och klistra in följande kod i 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); });
Skapa en fil med namnet hotels.json och klistra in följande kod i 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" } } ] }
Skapa en fil med namnet hotels_quickstart_index.json och klistra in följande kod i 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" ] } ] }
Logga in på Azure med följande kommando:
az login
Kör JavaScript-koden med följande kommando:
node index.js
Förklara koden
Skapa index
Filen hotels_quickstart_index.json definierar hur Azure AI Search fungerar med de dokument som du läser in i nästa steg. Varje fält identifieras av en name
och har en angiven type
. Varje fält har också en serie indexattribut som anger om Azure AI Search kan söka, filtrera, sortera och fasettera på fältet. De flesta av fälten är enkla datatyper, men vissa, som AddressType
är komplexa typer som gör att du kan skapa omfattande datastrukturer i ditt index. Du kan läsa mer om datatyper som stöds och indexattribut som beskrivs i Skapa index (REST).
Med vår indexdefinition på plats vill vi importera hotels_quickstart_index.json överst i index.js så att huvudfunktionen kan komma åt indexdefinitionen.
const indexDefinition = require('./hotels_quickstart_index.json');
I huvudfunktionen skapar vi sedan en SearchIndexClient
, som används för att skapa och hantera index för Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Sedan vill vi ta bort indexet om det redan finns. Den här åtgärden är en vanlig metod för test-/demokod.
Det gör vi genom att definiera en enkel funktion som försöker ta bort indexet.
async function deleteIndexIfExists(indexClient, indexName) {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
För att köra funktionen extraherar vi indexnamnet från indexdefinitionen och skickar indexName
med indexClient
till deleteIndexIfExists()
funktionen.
const indexName = indexDefinition["name"];
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
Efter det är vi redo att skapa indexet med createIndex()
-metoden.
console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);
console.log(`Index named ${index.name} has been created.`);
Läsa in dokument
I Azure AI Search är dokument datastrukturer som både är indata för indexering och utdata från frågor. Du kan skicka sådana data till indexet eller använda en indexerare. I det här fallet skickar vi programmatiskt dokumenten till indexet.
Dokumentindata kan vara rader i en databas, blobar i Blob Storage eller, som i det här exemplet, JSON-dokument på disk. På samma sätt som vi gjorde med indexDefinition
måste vi också importera hotels.json
överst i index.js så att data kan nås i vår huvudfunktion.
const hotelData = require('./hotels.json');
För att indexera data i sökindexet måste vi nu skapa en SearchClient
.
SearchIndexClient
Används för att skapa och hantera ett index, SearchClient
men används för att ladda upp dokument och köra frågor mot indexet.
Det finns två sätt att skapa en SearchClient
. Det första alternativet är att skapa en SearchClient
från grunden:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
Du kan också använda getSearchClient()
metoden SearchIndexClient
för för att skapa SearchClient
:
const searchClient = indexClient.getSearchClient(indexName);
Nu när klienten har definierats laddar du upp dokumenten till sökindexet. I det här fallet använder mergeOrUploadDocuments()
vi metoden som laddar upp dokumenten eller sammanfogar dem med ett befintligt dokument om det redan finns ett dokument med samma nyckel.
console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Sök i ett index
När ett index har skapats och dokument har laddats upp är du redo att skicka frågor till indexet. I det här avsnittet skickar vi fem olika frågor till sökindexet för att demonstrera olika frågefunktioner som är tillgängliga för dig.
Frågorna skrivs i en sendQueries()
funktion som vi anropar i huvudfunktionen enligt följande:
await sendQueries(searchClient);
Frågor skickas med metoden search()
searchClient
. Den första parametern är söktexten och den andra parametern anger sökalternativ.
Frågeexempel 1
Den första frågan söker efter *
, vilket motsvarar att söka efter allt och väljer tre av fälten i indexet. Det är en bra idé att bara select
använda de fält du behöver eftersom onödiga data kan ge svarstid till dina frågor.
För searchOptions
den här frågan har includeTotalCount
också angetts till true
, som returnerar antalet matchande resultat som hittades.
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 återstående frågorna som beskrivs nedan bör också läggas till i sendQueries()
funktionen. De är separerade här för läsbarhet.
Frågeexempel 2
I nästa fråga anger vi söktermen "wifi"
och inkluderar även ett filter för att endast returnera resultat där tillståndet är lika med 'FL'
. Resultaten beställs också av hotellets Rating
.
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)}`);
}
Frågeexempel 3
Därefter begränsas sökningen till ett enda sökbart fält med hjälp av parametern searchFields
. Den här metoden är ett bra alternativ för att göra frågan mer effektiv om du vet att du bara är intresserad av matchningar i vissa fält.
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();
Frågeexempel 4
Ett annat vanligt alternativ att ta med i en fråga är facets
. Med fasetter kan du skapa filter i användargränssnittet för att göra det enkelt för användarna att veta vilka värden de kan filtrera ned till.
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)}`);
}
Frågeexempel 5
Den sista frågan använder getDocument()
metoden för searchClient
. På så sätt kan du effektivt hämta ett dokument med dess nyckel.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Sammanfattning av frågor
Föregående frågor visar flera sätt att matcha termer i en fråga: fulltextsökning, filter och automatisk komplettering.
Fulltextsökning och -filter utförs med hjälp av searchClient.search
metoden . En sökfråga kan skickas i strängen searchText
, medan ett filteruttryck kan skickas i filter
klassens SearchOptions
egenskap. Om du vill filtrera utan att söka skickar du bara "*" för parametern searchText
för search
metoden. Om du vill söka utan filtrering lämnar du filter
egenskapen oetig eller skickar inte någon SearchOptions
instans alls.
Lär dig hur du använder klientbiblioteket Azure.Search.Documents för att skapa, läsa in och köra frågor mot ett sökindex med hjälp av exempeldata för fulltextsökning. Fulltextsökning använder Apache Lucene för indexering och frågor samt en BM25-rangordningsalgoritm för bedömning av resultat.
Den här snabbstarten skapar och frågar ett litet hotell-snabbstartsindex som innehåller data om fyra hotell.
Dricks
Du kan ladda ned och köra en färdig notebook-fil.
Förutsättningar
- En aktiv Azure-prenumeration – Skapa en kostnadsfritt
- En Azure AI-tjänsten Search. Skapa en tjänst om du inte har någon. Du kan använda en kostnadsfri nivå för den här snabbstarten.
- Visual Studio Code med Python-tillägget eller motsvarande IDE med Python 3.10 eller senare. Om du inte har en lämplig version av Python installerad kan du följa anvisningarna i VS Code Python-självstudien.
Krav för Microsoft Entra-ID
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID måste du:
- Installera Azure CLI som används för nyckellös autentisering med Microsoft Entra-ID.
- Tilldela både rollerna
Search Service Contributor
ochSearch Index Data Contributor
till ditt användarkonto. Du kan tilldela roller i Azure Portal under Åtkomstkontroll (IAM)>Lägg till rolltilldelning. Mer information finns i Ansluta till Azure AI Search med hjälp av roller.
Hämta resursinformation
Du måste hämta följande information för att autentisera ditt program med din Azure AI-tjänsten Search:
Variabelnamn | Värde |
---|---|
SEARCH_API_ENDPOINT |
Det här värdet finns i Azure Portal. Välj söktjänsten och välj sedan Översikt på den vänstra menyn.
Url-värdet under Essentials är den slutpunkt som du behöver. Här följer ett exempel på hur en slutpunkt kan se ut: https://mydemo.search.windows.net . |
Läs mer om nyckellös autentisering och inställning av miljövariabler.
Konfigurera din miljö
Du kör exempelkoden i en Jupyter-anteckningsbok. Därför måste du konfigurera din miljö för att köra Jupyter Notebooks.
Ladda ned eller kopiera exempelanteckningsboken från GitHub.
Öppna anteckningsboken i Visual Studio Code.
Skapa en ny Python-miljö som ska användas för att installera de paket du behöver för den här självstudien.
Viktigt!
Installera inte paket i din globala Python-installation. Du bör alltid använda en virtuell miljö eller conda-miljö när du installerar Python-paket, annars kan du avbryta din globala installation av Python.
Det kan ta en minut att konfigurera. Om du stöter på problem kan du läsa Python-miljöer i VS Code.
Installera Jupyter Notebooks och IPython Kernel for Jupyter Notebooks om du inte redan har dem.
pip install jupyter pip install ipykernel python -m ipykernel install --user --name=.venv
Välj notebook-kerneln.
- I det övre högra hörnet i notebook-filen väljer du Välj kernel.
- Om du ser
.venv
i listan väljer du den. Om du inte ser den väljer du Välj en annan Kernel>Python-miljö.>.venv
Skapa, läsa in och fråga ett sökindex
I det här avsnittet lägger du till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du kör programmet för att se resultatet i konsolen. En detaljerad förklaring av koden finns i avsnittet förklara koden .
Kontrollera att anteckningsboken är öppen i kerneln
.venv
enligt beskrivningen i föregående avsnitt.Kör den första kodcellen för att installera de paket som krävs, inklusive azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet ! pip install azure-identity --quiet ! pip install python-dotenv --quiet
Ersätt innehållet i den andra kodcellen med följande kod beroende på din autentiseringsmetod.
Kommentar
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet
DefaultAzureCredential
med ettAzureKeyCredential
objekt.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"
Ta bort följande två rader från cellen Skapa en indexkod . Autentiseringsuppgifterna har redan angetts i den tidigare kodcellen.
from azure.core.credentials import AzureKeyCredential credential = AzureKeyCredential(search_api_key)
Kör cellen Skapa en indexkod för att skapa ett sökindex.
Kör de återstående kodcellerna sekventiellt för att läsa in dokument och köra frågor.
Förklara koden
Skapa ett index
SearchIndexClient
används för att skapa och hantera index för Azure AI Search. Varje fält identifieras av en name
och har en angiven type
.
Varje fält har också en serie indexattribut som anger om Azure AI Search kan söka, filtrera, sortera och fasettera på fältet. De flesta av fälten är enkla datatyper, men vissa, som AddressType
är komplexa typer som gör att du kan skapa omfattande datastrukturer i ditt index. Du kan läsa mer om datatyper som stöds och indexattribut som beskrivs i Skapa index (REST).
Skapa en dokumentnyttolast och ladda upp dokument
Använd en indexåtgärd för åtgärdstypen, till exempel uppladdning eller sammanslagning och uppladdning. Dokument kommer från Exemplet HotelsData på GitHub.
Sök i ett index
Du kan få frågeresultat så snart det första dokumentet indexeras, men den faktiska testningen av indexet bör vänta tills alla dokument har indexerats.
Använd sökmetoden för klassen search.client.
Exempelfrågorna i notebook-filen är:
- Grundläggande fråga: Kör en tom sökning (
search=*
), som returnerar en orankad lista (sökpoäng = 1,0) godtyckliga dokument. Eftersom det inte finns några kriterier inkluderas alla dokument i resultatet. - Termfråga: Lägger till hela termer i sökuttrycket ("wifi"). Den här frågan anger att resultaten endast innehåller de fälten i -instruktionen
select
. Om du begränsar fälten som kommer tillbaka minimeras mängden data som skickas tillbaka över tråden och minskar sökfördröjningen. - Filtrerad fråga: Lägg till ett filteruttryck som bara returnerar de hotell med ett omdöme som är större än fyra, sorterat i fallande ordning.
- Fältomfång: Lägg till
search_fields
i omfånget för frågekörning till specifika fält. - Fasetter: Generera fasetter för positiva matchningar som finns i sökresultat. Det finns inga noll matchningar. Om sökresultaten inte innehåller termen wifi visas inte wifi i den fasetterade navigeringsstrukturen.
- Slå upp ett dokument: Returnera ett dokument baserat på dess nyckel. Den här åtgärden är användbar om du vill visa detaljerad information när en användare väljer ett objekt i ett sökresultat.
- Komplettera automatiskt: Ange potentiella matchningar när användaren skriver i sökrutan. Autocomplete använder en suggester (
sg
) för att veta vilka fält som innehåller potentiella matchningar för förslagsbegäranden. I den här snabbstarten ärTags
fälten ,Address/City
,Address/Country
. Om du vill simulera automatisk komplettering skickar du bokstäverna sa som en partiell sträng. Metoden för automatisk komplettering av SearchClient skickar tillbaka potentiella termmatchningar.
Ta bort indexet
Om du är klar med det här indexet kan du ta bort det genom att köra cellen Rensa kod. Om du tar bort onödiga index frigörs utrymme för att gå igenom fler snabbstarter och självstudier.
Lär dig hur du använder klientbiblioteket Azure.Search.Documents för att skapa, läsa in och köra frågor mot ett sökindex med hjälp av exempeldata för fulltextsökning. Fulltextsökning använder Apache Lucene för indexering och frågor samt en BM25-rangordningsalgoritm för bedömning av resultat.
Den här snabbstarten skapar och frågar ett litet hotell-snabbstartsindex som innehåller data om fyra hotell.
Dricks
Du kan ladda ned källkoden för att börja med ett färdigt projekt eller följa dessa steg för att skapa en egen.
Förutsättningar
- En aktiv Azure-prenumeration – Skapa en kostnadsfritt
- En Azure AI-tjänsten Search. Skapa en tjänst om du inte har någon. Du kan använda en kostnadsfri nivå för den här snabbstarten.
Krav för Microsoft Entra-ID
För den rekommenderade nyckellösa autentiseringen med Microsoft Entra-ID måste du:
- Installera Azure CLI som används för nyckellös autentisering med Microsoft Entra-ID.
- Tilldela både rollerna
Search Service Contributor
ochSearch Index Data Contributor
till ditt användarkonto. Du kan tilldela roller i Azure Portal under Åtkomstkontroll (IAM)>Lägg till rolltilldelning. Mer information finns i Ansluta till Azure AI Search med hjälp av roller.
Hämta resursinformation
Du måste hämta följande information för att autentisera ditt program med din Azure AI-tjänsten Search:
Variabelnamn | Värde |
---|---|
SEARCH_API_ENDPOINT |
Det här värdet finns i Azure Portal. Välj söktjänsten och välj sedan Översikt på den vänstra menyn.
Url-värdet under Essentials är den slutpunkt som du behöver. Här följer ett exempel på hur en slutpunkt kan se ut: https://mydemo.search.windows.net . |
Läs mer om nyckellös autentisering och inställning av miljövariabler.
Konfigurera
Skapa en ny mapp
full-text-quickstart
som ska innehålla programmet och öppna Visual Studio Code i mappen med följande kommando:mkdir full-text-quickstart && cd full-text-quickstart
package.json
Skapa med följande kommando:npm init -y
package.json
Uppdatera till ECMAScript med följande kommando:npm pkg set type=module
Installera Azure AI Search-klientbiblioteket (Azure.Search.Documents) för JavaScript med:
npm install @azure/search-documents
För den rekommenderade lösenordslösa autentiseringen installerar du Azure Identity-klientbiblioteket med:
npm install @azure/identity
Skapa, läsa in och fråga ett sökindex
I föregående konfigurationsavsnitt installerade du Azure AI Search-klientbiblioteket och andra beroenden.
I det här avsnittet lägger du till kod för att skapa ett sökindex, läsa in det med dokument och köra frågor. Du kör programmet för att se resultatet i konsolen. En detaljerad förklaring av koden finns i avsnittet förklara koden .
Exempelkoden i den här snabbstarten använder Microsoft Entra-ID för rekommenderad nyckellös autentisering. Om du föredrar att använda en API-nyckel kan du ersätta objektet DefaultAzureCredential
med ett AzureKeyCredential
objekt.
const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
Skapa en ny fil med namnet index.ts och klistra in följande kod i 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); });
Skapa en fil med namnet hotels.json och klistra in följande kod i 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" } } ] }
tsconfig.json
Skapa filen för att transpilera TypeScript-koden och kopiera följande kod för 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"] }
Transpilera från TypeScript till JavaScript.
tsc
Logga in på Azure med följande kommando:
az login
Kör JavaScript-koden med följande kommando:
node index.js
Förklara koden
Skapa index
Skapa en fil hotels_quickstart_index.json. Den här filen definierar hur Azure AI Search fungerar med de dokument som du läser in i nästa steg. Varje fält identifieras av en name
och har en angiven type
. Varje fält har också en serie indexattribut som anger om Azure AI Search kan söka, filtrera, sortera och fasettera på fältet. De flesta av fälten är enkla datatyper, men vissa, som AddressType
är komplexa typer som gör att du kan skapa omfattande datastrukturer i ditt index. Du kan läsa mer om datatyper som stöds och indexattribut som beskrivs i Skapa index (REST).
Vi vill importera hotels_quickstart_index.json så att huvudfunktionen kan komma åt indexdefinitionen.
import indexDefinition from './hotels_quickstart_index.json';
interface HotelIndexDefinition {
name: string;
fields: SimpleField[] | ComplexField[];
suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;
I huvudfunktionen skapar vi sedan en SearchIndexClient
, som används för att skapa och hantera index för Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Sedan vill vi ta bort indexet om det redan finns. Den här åtgärden är en vanlig metod för test-/demokod.
Det gör vi genom att definiera en enkel funktion som försöker ta bort indexet.
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.');
}
}
För att köra funktionen extraherar vi indexnamnet från indexdefinitionen och skickar indexName
med indexClient
till deleteIndexIfExists()
funktionen.
// 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);
Efter det är vi redo att skapa indexet med createIndex()
-metoden.
console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);
console.log(`Index named ${index.name} has been created.`);
Läsa in dokument
I Azure AI Search är dokument datastrukturer som både är indata för indexering och utdata från frågor. Du kan skicka sådana data till indexet eller använda en indexerare. I det här fallet skickar vi programmatiskt dokumenten till indexet.
Dokumentindata kan vara rader i en databas, blobar i Blob Storage eller, som i det här exemplet, JSON-dokument på disk. Du kan antingen ladda ned hotels.json eller skapa en egen hotels.json fil med följande innehåll:
På samma sätt som vi gjorde med indexDefinition måste vi också importera hotels.json
överst i index.ts så att data kan nås i vår huvudfunktion.
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"];
För att indexera data i sökindexet måste vi nu skapa en SearchClient
.
SearchIndexClient
Används för att skapa och hantera ett index, SearchClient
men används för att ladda upp dokument och köra frågor mot indexet.
Det finns två sätt att skapa en SearchClient
. Det första alternativet är att skapa en SearchClient
från grunden:
const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));
Du kan också använda getSearchClient()
metoden SearchIndexClient
för för att skapa SearchClient
:
const searchClient = indexClient.getSearchClient<Hotel>(indexName);
Nu när klienten har definierats laddar du upp dokumenten till sökindexet. I det här fallet använder mergeOrUploadDocuments()
vi metoden som laddar upp dokumenten eller sammanfogar dem med ett befintligt dokument om det redan finns ett dokument med samma nyckel. Kontrollera sedan att åtgärden lyckades eftersom åtminstone det första dokumentet finns.
console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Kör programmet igen med tsc && node index.ts
. Du bör se en något annorlunda uppsättning meddelanden än de du såg i steg 1. Den här gången finns indexet och du bör se ett meddelande om att ta bort det innan appen skapar det nya indexet och publicerar data till det.
Innan vi kör frågorna i nästa steg definierar du en funktion så att programmet väntar en sekund. Detta görs bara i test-/demosyfte för att säkerställa att indexeringen är klar och att dokumenten är tillgängliga i indexet för våra frågor.
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Om du vill att programmet ska vänta en sekund anropar du sleep
funktionen:
sleep(1000);
Sök i ett index
När ett index har skapats och dokument har laddats upp är du redo att skicka frågor till indexet. I det här avsnittet skickar vi fem olika frågor till sökindexet för att demonstrera olika frågefunktioner som är tillgängliga för dig.
Frågorna skrivs i en sendQueries()
funktion som vi anropar i huvudfunktionen enligt följande:
await sendQueries(searchClient);
Frågor skickas med metoden search()
searchClient
. Den första parametern är söktexten och den andra parametern anger sökalternativ.
Frågeexempel 1
Den första frågan söker efter *
, vilket motsvarar att söka efter allt och väljer tre av fälten i indexet. Det är en bra idé att bara select
använda de fält du behöver eftersom onödiga data kan ge svarstid till dina frågor.
För searchOptions
den här frågan har includeTotalCount
också angetts till true
, vilket returnerar antalet matchande resultat som hittades.
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 återstående frågorna som beskrivs nedan bör också läggas till i sendQueries()
funktionen. De är separerade här för läsbarhet.
Frågeexempel 2
I nästa fråga anger vi söktermen "wifi"
och inkluderar även ett filter för att endast returnera resultat där tillståndet är lika med 'FL'
. Resultaten beställs också av hotellets Rating
.
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)}`);
}
Frågeexempel 3
Därefter begränsas sökningen till ett enda sökbart fält med hjälp av parametern searchFields
. Den här metoden är ett bra alternativ för att göra frågan mer effektiv om du vet att du bara är intresserad av matchningar i vissa fält.
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)}`);
}
Frågeexempel 4
Ett annat vanligt alternativ att ta med i en fråga är facets
. Med fasetter kan du tillhandahålla självriktad detaljvisning från resultatet i användargränssnittet. Fasetteringsresultatet kan omvandlas till kryssrutor i resultatfönstret.
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)}`);
}
Frågeexempel 5
Den sista frågan använder getDocument()
metoden för searchClient
. På så sätt kan du effektivt hämta ett dokument med dess nyckel.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Sammanfattning av frågor
Föregående frågor visar flera sätt att matcha termer i en fråga: fulltextsökning, filter och automatisk komplettering.
Fulltextsökning och -filter utförs med hjälp av searchClient.search
metoden . En sökfråga kan skickas i strängen searchText
, medan ett filteruttryck kan skickas i filter
klassens SearchOptions
egenskap. Om du vill filtrera utan att söka skickar du bara "*" för parametern searchText
för search
metoden. Om du vill söka utan filtrering lämnar du filter
egenskapen oetig eller skickar inte någon SearchOptions
instans alls.
Rensa resurser
När du arbetar i din egen prenumeration kan det dock vara klokt att i slutet av ett projekt kontrollera om du fortfarande behöver de resurser som du skapade. Resurser som fortsätter att köras kostar pengar. Du kan ta bort enstaka resurser eller hela resursgruppen om du vill ta bort alla resurser.
Du kan hitta och hantera resurser i Azure Portal med hjälp av länken Alla resurser eller Resursgrupper i det vänstra navigeringsfönstret.
Om du använder en kostnadsfri tjänst ska du komma ihåg att du är begränsad till tre index, indexerare och datakällor. Du kan ta bort enskilda objekt i Azure Portal för att hålla dig under gränsen.
Gå vidare
I den här snabbstarten arbetade du med en uppsättning uppgifter för att skapa ett index, läsa in det med dokument och köra frågor. I olika steg tog vi genvägar för att förenkla koden för läsbarhet och förståelse. Nu när du är bekant med de grundläggande begreppen kan du prova en självstudiekurs som anropar API:erna för Azure AI Search i en webbapp.