Rychlý start: Fulltextové vyhledávání pomocí sad SDK Azure
Naučte se používat klientskou knihovnu Azure.Search.Documents k vytvoření, načtení a dotazování indexu vyhledávání pomocí ukázkových dat pro fulltextové vyhledávání. Fulltextové vyhledávání používá Apache Lucene k indexování a dotazům a algoritmus hodnocení BM25 pro vyhodnocování výsledků.
V tomto rychlém startu se vytvoří a dotazuje malý index rychlého startu obsahující data o čtyřech hotelech.
Tip
Zdrojový kód si můžete stáhnout, abyste mohli začít s hotovým projektem, nebo si podle těchto kroků vytvořte vlastní.
Požadavky
- Aktivní předplatné Azure – Vytvořte si ho zdarma
- Azure AI Search. Pokud službu nemáte, vytvořte ji. Pro účely tohoto rychlého startu můžete použít úroveň Free.
Požadavky pro Microsoft Entra ID
Pro doporučené ověřování bez klíčů s ID Microsoft Entra musíte:
- Nainstalujte Azure CLI, které se používá pro ověřování bez klíčů pomocí ID Microsoft Entra.
- Přiřaďte uživatelskému
Search Service Contributor
účtu obě role iSearch Index Data Contributor
role. Role můžete přiřadit na webu Azure Portal v části Řízení přístupu (IAM)>Přidat přiřazení role. Další informace najdete v tématu Připojení k Azure AI Search pomocí rolí.
Načtení informací o prostředcích
Abyste mohli aplikaci ověřit pomocí Search Azure AI, musíte načíst následující informace:
Název proměnné | Hodnota |
---|---|
SEARCH_API_ENDPOINT |
Tuto hodnotu najdete na webu Azure Portal. Vyberte vyhledávací službu a pak v nabídce vlevo vyberte Přehled. Hodnota url v části Essentials je koncový bod, který potřebujete. Příkladem koncového bodu může být https://mydemo.search.windows.net . |
Přečtěte si další informace o ověřování bez klíčů a nastavení proměnných prostředí.
Nastavení
Vytvořte novou složku
full-text-quickstart
, která bude obsahovat aplikaci, a otevřete v této složce Visual Studio Code pomocí následujícího příkazu:mkdir full-text-quickstart && cd full-text-quickstart
Vytvořte novou konzolovou aplikaci pomocí následujícího příkazu:
dotnet new console
Nainstalujte klientskou knihovnu Azure AI Search (Azure.Search.Documents) pro .NET pomocí:
dotnet add package Azure.Search.Documents
Pro doporučené ověřování bez klíčů s ID Microsoft Entra nainstalujte balíček Azure.Identity pomocí:
dotnet add package Azure.Identity
Pro doporučené ověřování bez klíčů pomocí ID Microsoft Entra se přihlaste k Azure pomocí následujícího příkazu:
az login
Vytvoření, načtení a dotazování indexu vyhledávání
V předchozí části nastavení jste vytvořili novou konzolovou aplikaci a nainstalovali klientskou knihovnu Azure AI Search.
V této části přidáte kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spuštěním programu zobrazíte výsledky v konzole. Podrobné vysvětlení kódu najdete v části s vysvětlením kódu .
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit DefaultAzureCredential
objektem AzureKeyCredential
.
Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
Do Program.cs vložte následující kód.
serviceName
Upravte proměnnéapiKey
pomocí názvu vyhledávací služby a klíče rozhraní API správce.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(); } } }
Ve stejné složce vytvořte nový soubor s názvem Hotel.cs a vložte následující kód. Tento kód definuje strukturu hotelového dokumentu.
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; } } }
Vytvořte nový soubor s názvem Hotel.cs a vložte následující kód, který definuje strukturu hotelového dokumentu. Atributy v poli určují, jak se používá v aplikaci. Atribut musí být například
IsFilterable
přiřazen ke každému poli, které podporuje výraz filtru.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; } } }
Vytvořte nový soubor s názvem Address.cs a vložte následující kód, který definuje strukturu dokumentu adresy.
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; } } }
Vytvořte nový soubor s názvem Hotel.Methods.cs a vložte následující kód, který definuje přepsání
ToString()
proHotel
třídu.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(); } } }
Vytvořte nový soubor s názvem Address.Methods.cs a vložte následující kód, který definuje přepsání
ToString()
proAddress
třídu.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); } }
Sestavte a spusťte aplikaci pomocí následujícího příkazu:
dotnet run
Výstup obsahuje zprávy z Console.WriteLine s přidáním informací o dotazu a výsledků.
Vysvětlení kódu
V předchozích částech jste vytvořili novou konzolovou aplikaci a nainstalovali klientskou knihovnu Azure AI Search. Přidali jste kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spustili jste program, abyste zobrazili výsledky v konzole.
V této části vysvětlujeme kód, který jste přidali do konzolové aplikace.
Vytvoření vyhledávacího klienta
V Program.cs jste vytvořili dva klienty:
- SearchIndexClient vytvoří index.
- SearchClient načte a dotazuje existující index.
Oba klienti potřebují koncový bod vyhledávací služby a přihlašovací údaje popsané výše v části s informacemi o prostředcích.
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit DefaultAzureCredential
objektem AzureKeyCredential
.
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 . . .
}
Vytvoření indexu
V tomto rychlém startu vytvoříte index hotelů, na který načtete data hotelů, a spustíte dotazy. V tomto kroku definujete pole v indexu. Každá definice pole obsahuje název, datový typ a atributy, které určují způsob použití pole.
V tomto příkladu se pro jednoduchost a čitelnost používají synchronní metody knihovny Azure.Search.Documents . V produkčních scénářích byste ale měli použít asynchronní metody, které umožňují udržovat aplikaci škálovatelnou a responzivní. Místo CreateIndex byste například použili CreateIndexAsync.
Definování struktur
Vytvořili jste dvě pomocné třídy, Hotel.cs a Address.cs, abyste definovali strukturu hotelového dokumentu a jeho adresy. Třída Hotel
obsahuje pole pro ID hotelu, název, popis, kategorii, značky, parkování, datum renovace, hodnocení a adresu. Třída Address
obsahuje pole pro adresu ulice, město, stát/kraj, PSČ a zemi/oblast.
V klientské knihovně Azure.Search.Documents můžete k zjednodušení definic polí použít SearchableField a SimpleField . Oba jsou deriváty vyhledávacího pole a můžou potenciálně zjednodušit váš kód:
SimpleField
může být libovolný datový typ, je vždy nehledatelný (ignorován pro dotazy fulltextového vyhledávání) a je možné je načíst (není skrytý). Ostatní atributy jsou ve výchozím nastavení vypnuté, ale je možné je povolit. Můžete použítSimpleField
pro ID dokumentu nebo pole použitá pouze v filtrech, omezujících vlastností nebo v bodovacích profilech. Pokud ano, nezapomeňte použít všechny atributy, které jsou nezbytné pro daný scénář, napříkladIsKey = true
pro ID dokumentu. Další informace najdete v tématu SimpleFieldAttribute.cs ve zdrojovém kódu.SearchableField
musí být řetězec a je vždy prohledávatelný a načístelný. Ostatní atributy jsou ve výchozím nastavení vypnuté, ale je možné je povolit. Vzhledem k tomu, že tento typ pole je prohledávatelný, podporuje synonyma a úplný doplněk vlastností analyzátoru. Další informace najdete v SearchableFieldAttribute.cs ve zdrojovém kódu.
Bez ohledu na to, jestli používáte základní SearchField
rozhraní API nebo jeden z pomocných modelů, musíte explicitně povolit atributy filtru, omezující vlastnosti a řazení. Například IsFilterable, IsSortable a IsFacetable musí být explicitně přiřazeny, jak je znázorněno v předchozí ukázce.
Vytvoření indexu vyhledávání
V Program.cs vytvoříte SearchIndex objekt a pak zavoláte CreateIndex metoda vyjádřit index ve vyhledávací službě. Index obsahuje také SearchSuggester , který povolí automatické dokončování v zadaných polích.
// 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);
}
Nahrání dokumentů
Azure AI Search vyhledá obsah uložený ve službě. V tomto kroku načtete dokumenty JSON, které odpovídají indexu hotelu, který jste vytvořili.
Ve službě Azure AI Search jsou vyhledávací dokumenty datové struktury, které jsou vstupy indexování a výstupy z dotazů. Jak je získáno z externího zdroje dat, vstupy dokumentů můžou být řádky v databázi, objekty blob v úložišti objektů blob nebo dokumenty JSON na disku. V tomto příkladu přebíráme zástupce a vkládáme dokumenty JSON pro čtyři hotely v samotném kódu.
Při nahrávání dokumentů musíte použít objekt IndexDocumentsBatch . Objekt IndexDocumentsBatch
obsahuje kolekci akcí, z nichž každý obsahuje dokument a vlastnost, která službě Azure AI Search říká, jakou akci provést (nahrání, sloučení, odstranění a mergeOrUpload).
V Program.cs vytvoříte pole dokumentů a akcí indexu a pak předáte pole do IndexDocumentsBatch
. Následující dokumenty odpovídají indexu pro rychlý start hotelů, jak je definováno hotelovou třídou.
// 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
}
Jakmile inicializujete IndexDocumentsBatch objekt, můžete jej odeslat do indexu voláním IndexDocuments na objekt SearchClient .
Dokumenty načítáte pomocí SearchClient in Main()
, ale operace také vyžaduje oprávnění správce služby, která je obvykle přidružena k SearchIndexClient. Jedním zezpůsobůch SearchIndexClient
searchIndexClient
SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);
// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);
Vzhledem k tomu, že máme konzolovou aplikaci, která spouští všechny příkazy postupně, přidáme 2sekundovou dobu čekání mezi indexováním a dotazy.
// 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);
Dvousekundové zpoždění kompenzuje indexování, což je asynchronní, aby se všechny dokumenty mohly indexovat před spuštěním dotazů. Kódování ve zpoždění je obvykle nezbytné pouze v ukázkách, testech a ukázkových aplikacích.
Prohledání indexu
Výsledky dotazu můžete získat hned po indexování prvního dokumentu, ale skutečné testování indexu by mělo počkat na indexování všech dokumentů.
Tato část přidává dvě funkce: logiku dotazu a výsledky. Pro dotazy použijte metodu Search . Tato metoda přebírá hledaný text (řetězec dotazu) a další možnosti.
SearchResults třída představuje výsledky.
V Program.csWriteDocuments
metoda vytiskne výsledky hledání do konzoly.
// 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();
}
Příklad dotazu 1
Metoda RunQueries
provádí dotazy a vrací výsledky. Výsledky jsou objekty Hotelu. Tato ukázka ukazuje podpis metody a první dotaz. Tento dotaz ukazuje Select
parametr, který umožňuje vytvořit výsledek pomocí vybraných polí z dokumentu.
// 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
}
Příklad dotazu 2
Ve druhém dotazu vyhledejte termín, přidejte filtr, který vybere dokumenty, ve kterých je hodnocení větší než 4, a potom řadit podle hodnocení v sestupném pořadí. Filtr je logický výraz, který se vyhodnocuje přes pole IsFilterable v indexu. Dotazy filtrování zahrnují nebo vylučují hodnoty. Proto neexistuje žádné skóre relevance přidružené k dotazu filtru.
// 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);
Příklad dotazu 3
Třetí dotaz ukazuje searchFields
, který slouží k určení rozsahu operace fulltextového vyhledávání na konkrétní pole.
// 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);
Příklad dotazu 4
Čtvrtý dotaz ukazuje facets
, který lze použít ke strukturování fasetové navigační struktury.
// 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);
Příklad dotazu 5
V pátém dotazu vraťte konkrétní dokument. Vyhledání dokumentu je typická odpověď na OnClick
událost v sadě výsledků.
// 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);
Příklad dotazu 6
Poslední dotaz zobrazí syntaxi automatického dokončování, která simuluje částečný vstup uživatele sa , který se přeloží na dvě možné shody ve zdrojových polích přidružených k sugestivnímu modulu, který jste definovali v indexu.
// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
var autoresponse = searchClient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);
Souhrn dotazů
Předchozí dotazy zobrazují v dotazu několik způsobů párování termínů: fulltextové vyhledávání, filtry a automatické dokončování.
Fulltextové vyhledávání a filtry se provádějí pomocí metody SearchClient.Search . Vyhledávací dotaz lze předat v řetězcisearchText
, zatímco výraz filtru lze předat ve vlastnosti Filter třídy SearchOptions. Pokud chcete filtrovat bez hledání, stačí předat "*"
searchText
parametr metody Search . Pokud chcete hledat bez filtrování, nechejte Filter
vlastnost nenasazenou nebo vůbec nepředávejte SearchOptions
instanci.
Naučte se používat klientskou knihovnu Azure.Search.Documents k vytvoření, načtení a dotazování indexu vyhledávání pomocí ukázkových dat pro fulltextové vyhledávání. Fulltextové vyhledávání používá Apache Lucene k indexování a dotazům a algoritmus hodnocení BM25 pro vyhodnocování výsledků.
V tomto rychlém startu se vytvoří a dotazuje malý index rychlého startu obsahující data o čtyřech hotelech.
Tip
Zdrojový kód si můžete stáhnout, abyste mohli začít s hotovým projektem, nebo si podle těchto kroků vytvořte vlastní.
Požadavky
- Aktivní předplatné Azure – Vytvořte si ho zdarma
- Azure AI Search. Pokud službu nemáte, vytvořte ji. Pro účely tohoto rychlého startu můžete použít úroveň Free.
Požadavky pro Microsoft Entra ID
Pro doporučené ověřování bez klíčů s ID Microsoft Entra musíte:
- Nainstalujte Azure CLI, které se používá pro ověřování bez klíčů pomocí ID Microsoft Entra.
- Přiřaďte uživatelskému
Search Service Contributor
účtu obě role iSearch Index Data Contributor
role. Role můžete přiřadit na webu Azure Portal v části Řízení přístupu (IAM)>Přidat přiřazení role. Další informace najdete v tématu Připojení k Azure AI Search pomocí rolí.
Načtení informací o prostředcích
Abyste mohli aplikaci ověřit pomocí Search Azure AI, musíte načíst následující informace:
Název proměnné | Hodnota |
---|---|
SEARCH_API_ENDPOINT |
Tuto hodnotu najdete na webu Azure Portal. Vyberte vyhledávací službu a pak v nabídce vlevo vyberte Přehled. Hodnota url v části Essentials je koncový bod, který potřebujete. Příkladem koncového bodu může být https://mydemo.search.windows.net . |
Přečtěte si další informace o ověřování bez klíčů a nastavení proměnných prostředí.
Nastavení
Ukázka v tomto rychlém startu funguje s modulem Runtime Java. Nainstalujte sadu Java Development Kit, jako je Azul Zulu OpenJDK. Měl by fungovat také microsoft build OpenJDK nebo upřednostňovaná sada JDK.
Nainstalujte Apache Maven. Pak spusťte a potvrďte
mvn -v
úspěšnou instalaci.V kořenovém adresáři projektu vytvořte nový
pom.xml
soubor a zkopírujte do něj následující kód:<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>
Nainstalujte závislosti včetně klientské knihovny Azure AI Search (Azure.Search.Documents) pro klientskou knihovnu Java a Azure Identity pro Javu pomocí:
mvn clean dependency:copy-dependencies
Pro doporučené ověřování bez klíčů pomocí ID Microsoft Entra se přihlaste k Azure pomocí následujícího příkazu:
az login
Vytvoření, načtení a dotazování indexu vyhledávání
V předchozí části nastavení jste nainstalovali klientskou knihovnu Azure AI Search a další závislosti.
V této části přidáte kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spuštěním programu zobrazíte výsledky v konzole. Podrobné vysvětlení kódu najdete v části s vysvětlením kódu .
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit DefaultAzureCredential
objektem AzureKeyCredential
.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
Vytvořte nový soubor s názvem App.java a vložte do App.java následující kód:
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")); } }
Vytvořte nový soubor s názvem Hotel.java a vložte do Hotel.java následující kód:
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 ""; } } }
Vytvořte nový soubor s názvem Address.java a vložte do Address.java následující kód:
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; }
Spusťte novou konzolovou aplikaci:
javac Address.java App.java Hotel.java -cp ".;target\dependency\*" java -cp ".;target\dependency\*" App
Vysvětlení kódu
V předchozích částech jste vytvořili novou konzolovou aplikaci a nainstalovali klientskou knihovnu Azure AI Search. Přidali jste kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spustili jste program, abyste zobrazili výsledky v konzole.
V této části vysvětlujeme kód, který jste přidali do konzolové aplikace.
Vytvoření vyhledávacího klienta
V App.java jste vytvořili dva klienty:
- SearchIndexClient vytvoří index.
- SearchClient načte a dotazuje existující index.
Oba klienti potřebují koncový bod vyhledávací služby a přihlašovací údaje popsané výše v části s informacemi o prostředcích.
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit DefaultAzureCredential
objektem AzureKeyCredential
.
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 . . .
}
Vytvoření indexu
V tomto rychlém startu vytvoříte index hotelů, na který načtete data hotelů, a spustíte dotazy. V tomto kroku definujete pole v indexu. Každá definice pole obsahuje název, datový typ a atributy, které určují způsob použití pole.
V tomto příkladu se pro jednoduchost a čitelnost používají synchronní metody knihovny Azure.Search.Documents . V produkčních scénářích byste ale měli použít asynchronní metody, které umožňují udržovat aplikaci škálovatelnou a responzivní. Místo CreateIndex byste například použili CreateIndexAsync.
Definování struktur
Vytvořili jste dvě pomocné třídy, Hotel.java a Address.java, abyste definovali strukturu hotelového dokumentu a jeho adresy. Třída hotelu obsahuje pole pro ID hotelu, název, popis, kategorii, značky, parkování, datum renovace, hodnocení a adresu. Třída Adresa obsahuje pole pro adresu ulice, město, stát/kraj, PSČ a zemi/oblast.
V klientské knihovně Azure.Search.Documents můžete k zjednodušení definic polí použít SearchableField a SimpleField .
-
SimpleField
může být libovolný datový typ, je vždy nehledatelný (ignorován pro dotazy fulltextového vyhledávání) a je možné je načíst (není skrytý). Ostatní atributy jsou ve výchozím nastavení vypnuté, ale je možné je povolit. Pro ID dokumentu nebo pole použitá pouze v filtrech, fazetách nebo v bodovacích profilech můžete použít SimpleField. Pokud ano, nezapomeňte použít všechny atributy, které jsou nezbytné pro scénář, například IsKey = true pro ID dokumentu. -
SearchableField
musí být řetězec a je vždy prohledávatelný a načístelný. Ostatní atributy jsou ve výchozím nastavení vypnuté, ale je možné je povolit. Vzhledem k tomu, že tento typ pole je prohledávatelný, podporuje synonyma a úplný doplněk vlastností analyzátoru.
Bez ohledu na to, jestli používáte základní SearchField
rozhraní API nebo jeden z pomocných modelů, musíte explicitně povolit atributy filtru, omezující vlastnosti a řazení. Například isFilterable
, isSortable
a isFacetable
musí být explicitně atribut, jak je znázorněno v předchozí ukázce.
Vytvoření indexu vyhledávání
V App.java
aplikaci vytvoříte SearchIndex
objekt v main
metodě a potom zavoláte metodu createOrUpdateIndex
pro vytvoření indexu ve vyhledávací službě. Index obsahuje SearchSuggester
také povolení automatického dokončování u zadaných polí.
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
Nahrání dokumentů
Azure AI Search vyhledá obsah uložený ve službě. V tomto kroku načtete dokumenty JSON, které odpovídají indexu hotelu, který jste vytvořili.
Ve službě Azure AI Search jsou vyhledávací dokumenty datové struktury, které jsou vstupy indexování a výstupy z dotazů. Jak je získáno z externího zdroje dat, vstupy dokumentů můžou být řádky v databázi, objekty blob v úložišti objektů blob nebo dokumenty JSON na disku. V tomto příkladu přebíráme zástupce a vkládáme dokumenty JSON pro čtyři hotely v samotném kódu.
Při nahrávání dokumentů musíte použít objekt IndexDocumentsBatch . Objekt IndexDocumentsBatch
obsahuje kolekci IndexActions, z nichž každý obsahuje dokument a vlastnost, která službě Azure AI Search říká, jakou akci provést (nahrání, sloučení, odstranění a mergeOrUpload).
V App.java
aplikaci vytvoříte dokumenty a akce indexu a pak je IndexDocumentsBatch
předáte . Následující dokumenty odpovídají indexu pro rychlý start hotelů, jak je definováno hotelovou třídou.
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");
}
}
Jakmile objekt inicializujete IndexDocumentsBatch
, můžete ho odeslat do indexu voláním indexDocuments ve vašem SearchClient
objektu.
Dokumenty načítáte pomocí SearchClient in main()
, ale operace také vyžaduje oprávnění správce služby, která je obvykle přidružena k SearchIndexClient. Jedním zezpůsobůch SearchIndexClient
searchIndexClient
uploadDocuments(searchClient);
Vzhledem k tomu, že máme konzolovou aplikaci, která spouští všechny příkazy postupně, přidáme 2sekundovou dobu čekání mezi indexováním a dotazy.
// 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)
{
}
Dvousekundové zpoždění kompenzuje indexování, což je asynchronní, aby se všechny dokumenty mohly indexovat před spuštěním dotazů. Kódování ve zpoždění je obvykle nezbytné pouze v ukázkách, testech a ukázkových aplikacích.
Prohledání indexu
Výsledky dotazu můžete získat hned po indexování prvního dokumentu, ale skutečné testování indexu by mělo počkat na indexování všech dokumentů.
Tato část přidá dvě části funkčnosti: logiku dotazu a výsledky. Pro dotazy použijte metodu Search. Tato metoda přebírá hledaný text (řetězec dotazu) a další možnosti.
Metoda App.java
vypíše WriteDocuments
výsledky hledání do konzoly.
// 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();
}
Příklad dotazu 1
Metoda RunQueries
provádí dotazy a vrací výsledky. Výsledky jsou objekty Hotelu. Tato ukázka ukazuje podpis metody a první dotaz. Tento dotaz ukazuje Select
parametr, který umožňuje vytvořit výsledek pomocí vybraných polí z dokumentu.
// 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));
}
Příklad dotazu 2
Ve druhém dotazu vyhledejte termín, přidejte filtr, který vybere dokumenty, ve kterých je hodnocení větší než 4, a potom řadit podle hodnocení v sestupném pořadí. Filtr je logický výraz, který se vyhodnocuje přes isFilterable
pole v indexu. Dotazy filtrování zahrnují nebo vylučují hodnoty. Proto neexistuje žádné skóre relevance přidružené k dotazu filtru.
// 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));
Příklad dotazu 3
Třetí dotaz ukazuje searchFields
, který slouží k určení rozsahu operace fulltextového vyhledávání na konkrétní pole.
// 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));
Příklad dotazu 4
Čtvrtý dotaz ukazuje facets
, který lze použít ke strukturování fasetové navigační struktury.
// 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));
Příklad dotazu 5
V pátém dotazu vraťte konkrétní 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();
Příklad dotazu 6
Poslední dotaz ukazuje syntaxi automatického dokončování, která simuluje částečný uživatelský vstup s , který se přeloží na dvě možné shody v sourceFields
přidruženém s návrhu, který jste definovali v indexu.
// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
Souhrn dotazů
Předchozí dotazy zobrazují v dotazu několik způsobů párování termínů: fulltextové vyhledávání, filtry a automatické dokončování.
Fulltextové vyhledávání a filtry se provádějí pomocí metody SearchClient.search . Vyhledávací dotaz lze předat v searchText
řetězci, zatímco výraz filtru lze předat ve filter
vlastnosti SearchOptions třídy. Pokud chcete filtrovat bez hledání, stačí předat searchText
parametr search
metody "*". Pokud chcete hledat bez filtrování, nechejte filter
vlastnost nenasazenou nebo vůbec nepředávejte SearchOptions
instanci.
Naučte se používat klientskou knihovnu Azure.Search.Documents k vytvoření, načtení a dotazování indexu vyhledávání pomocí ukázkových dat pro fulltextové vyhledávání. Fulltextové vyhledávání používá Apache Lucene k indexování a dotazům a algoritmus hodnocení BM25 pro vyhodnocování výsledků.
V tomto rychlém startu se vytvoří a dotazuje malý index rychlého startu obsahující data o čtyřech hotelech.
Tip
Zdrojový kód si můžete stáhnout, abyste mohli začít s hotovým projektem, nebo si podle těchto kroků vytvořte vlastní.
Požadavky
- Aktivní předplatné Azure – Vytvořte si ho zdarma
- Azure AI Search. Pokud službu nemáte, vytvořte ji. Pro účely tohoto rychlého startu můžete použít úroveň Free.
Požadavky pro Microsoft Entra ID
Pro doporučené ověřování bez klíčů s ID Microsoft Entra musíte:
- Nainstalujte Azure CLI, které se používá pro ověřování bez klíčů pomocí ID Microsoft Entra.
- Přiřaďte uživatelskému
Search Service Contributor
účtu obě role iSearch Index Data Contributor
role. Role můžete přiřadit na webu Azure Portal v části Řízení přístupu (IAM)>Přidat přiřazení role. Další informace najdete v tématu Připojení k Azure AI Search pomocí rolí.
Načtení informací o prostředcích
Abyste mohli aplikaci ověřit pomocí Search Azure AI, musíte načíst následující informace:
Název proměnné | Hodnota |
---|---|
SEARCH_API_ENDPOINT |
Tuto hodnotu najdete na webu Azure Portal. Vyberte vyhledávací službu a pak v nabídce vlevo vyberte Přehled. Hodnota url v části Essentials je koncový bod, který potřebujete. Příkladem koncového bodu může být https://mydemo.search.windows.net . |
Přečtěte si další informace o ověřování bez klíčů a nastavení proměnných prostředí.
Nastavení
Vytvořte novou složku
full-text-quickstart
, která bude obsahovat aplikaci, a otevřete v této složce Visual Studio Code pomocí následujícího příkazu:mkdir full-text-quickstart && cd full-text-quickstart
Vytvořte následující
package.json
příkaz:npm init -y
Nainstalujte klientskou knihovnu Azure AI Search (Azure.Search.Documents) pro JavaScript pomocí:
npm install @azure/search-documents
Pro doporučené ověřování bez hesla nainstalujte klientskou knihovnu Azure Identity pomocí:
npm install @azure/identity
Vytvoření, načtení a dotazování indexu vyhledávání
V předchozí části nastavení jste nainstalovali klientskou knihovnu Azure AI Search a další závislosti.
V této části přidáte kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spuštěním programu zobrazíte výsledky v konzole. Podrobné vysvětlení kódu najdete v části s vysvětlením kódu .
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit DefaultAzureCredential
objektem AzureKeyCredential
.
String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
Vytvořte nový soubor s názvem index.js a vložte do index.js následující kód:
// 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); });
Vytvořte soubor s názvem hotels.json a vložte do hotels.json následující kód:
{ "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" } } ] }
Vytvořte soubor s názvem hotels_quickstart_index.json a vložte do hotels_quickstart_index.json následující kód:
{ "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" ] } ] }
Přihlaste se k Azure pomocí následujícího příkazu:
az login
Spusťte kód JavaScriptu pomocí následujícího příkazu:
node index.js
Vysvětlení kódu
Vytvoření indexu
Soubor hotels_quickstart_index.json definuje, jak Azure AI Search funguje s dokumenty, které načtete v dalším kroku. Každé pole je identifikováno name
a má zadané type
. Každé pole má také řadu atributů indexu, které určují, jestli může Azure AI Search prohledávat, filtrovat, řadit a omezující vlastnosti pole. Většina polí je jednoduchých datových typů, ale některé, jako AddressType
jsou složité typy, které umožňují vytvářet bohaté datové struktury v indexu. Další informace opodporovaných
S naší definicí indexu chceme importovat hotels_quickstart_index.json v horní části index.js , aby hlavní funkce přistupovala k definici indexu.
const indexDefinition = require('./hotels_quickstart_index.json');
V rámci hlavní funkce pak vytvoříme , SearchIndexClient
která se použije k vytváření a správě indexů pro Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Dále chceme odstranit index, pokud už existuje. Tato operace je běžným postupem pro testovací nebo ukázkový kód.
Provedeme to tak, že definujeme jednoduchou funkci, která se pokusí odstranit index.
async function deleteIndexIfExists(indexClient, indexName) {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Ke spuštění funkce extrahujeme název indexu z definice indexu indexClient
deleteIndexIfExists()
a předáme indexName
funkci spolu s funkcí.
const indexName = indexDefinition["name"];
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
Potom jsme připraveni vytvořit index pomocí createIndex()
metody.
console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);
console.log(`Index named ${index.name} has been created.`);
Nahrání dokumentů
Ve službě Azure AI Search jsou dokumenty datové struktury, které jsou vstupy indexování a výstupy z dotazů. Tato data můžete odeslat do indexu nebo použít indexer. V tomto případě naprogramujeme dokumenty do indexu.
Vstupy dokumentů můžou být řádky v databázi, objekty blob v úložišti objektů blob nebo jako v této ukázce dokumenty JSON na disku. Podobně jako v případě, že jsme to udělali indexDefinition
, musíme také importovat hotels.json
v horní části index.js , aby k datům bylo možné přistupovat v naší hlavní funkci.
const hotelData = require('./hotels.json');
Abychom mohli data indexovat do vyhledávacího indexu, musíme teď vytvořit .SearchClient
SearchIndexClient
Zatímco se používá k vytvoření a správě indexu, SearchClient
slouží k nahrání dokumentů a dotazování indexu.
Existují dva způsoby, jak vytvořit SearchClient
. První možností je vytvořit úplně SearchClient
od začátku:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
Alternativně můžete použít getSearchClient()
metodu SearchIndexClient
vytvoření SearchClient
:
const searchClient = indexClient.getSearchClient(indexName);
Teď, když je klient definovaný, nahrajte dokumenty do indexu vyhledávání. V tomto případě použijeme metodu mergeOrUploadDocuments()
, která nahraje dokumenty nebo je sloučí s existujícím dokumentem, pokud už existuje dokument se stejným klíčem.
console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Prohledání indexu
Když jste vytvořili index a nahráli jste dokumenty, můžete do indexu odesílat dotazy. V této části pošleme do indexu vyhledávání pět různých dotazů, abychom vám ukázali různé části funkcí dotazů, které jsou vám k dispozici.
Dotazy jsou napsané ve sendQueries()
funkci, kterou voláme v hlavní funkci následujícím způsobem:
await sendQueries(searchClient);
Dotazy se odesílají pomocí search()
metody searchClient
. Prvním parametrem je hledaný text a druhý parametr určuje možnosti hledání.
Příklad dotazu 1
První dotaz vyhledá *
, což je ekvivalent pro hledání všeho a vybere tři pole v indexu. Osvědčeným postupem je pouze select
pole, která potřebujete, protože stažení nepotřebných dat může do dotazů přidat latenci.
Pro searchOptions
tento dotaz je includeTotalCount
také nastavena true
hodnota , která vrátí počet nalezených odpovídajících výsledků.
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
}
Zbývající níže uvedené dotazy by se také měly přidat do sendQueries()
funkce. Jsou zde odděleny pro čitelnost.
Příklad dotazu 2
V dalším dotazu určíme hledaný termín "wifi"
a zahrneme také filtr, který vrátí pouze výsledky, ve kterých je stav roven 'FL'
. Výsledky jsou také objednány hotelovým hotelem 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)}`);
}
Příklad dotazu 3
Dále je hledání omezeno na jedno prohledávatelné pole pomocí parametru searchFields
. Tento přístup je skvělou možností, jak zefektivnit dotaz, pokud víte, že vás zajímají jenom shody v určitých polích.
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();
Příklad dotazu 4
Další běžnou možností zahrnutí do dotazu je facets
. Omezující vlastnosti umožňují vytvářet filtry v uživatelském rozhraní, aby uživatelé snadno věděli, na jaké hodnoty můžou filtrovat.
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)}`);
}
Příklad dotazu 5
Poslední dotaz používá getDocument()
metodu searchClient
. Díky tomu můžete dokument efektivně načíst podle jeho klíče.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Souhrn dotazů
Předchozí dotazy zobrazují v dotazu několik způsobů párování termínů: fulltextové vyhledávání, filtry a automatické dokončování.
Fulltextové vyhledávání a filtry se provádějí pomocí searchClient.search
metody. Vyhledávací dotaz lze předat v řetězci searchText
, zatímco výraz filtru lze předat ve filter
vlastnosti SearchOptions
třídy. Pokud chcete filtrovat bez hledání, stačí předat searchText
parametr search
metody "*". Pokud chcete hledat bez filtrování, nechejte filter
vlastnost nenasazenou nebo vůbec nepředávejte SearchOptions
instanci.
Naučte se používat klientskou knihovnu Azure.Search.Documents k vytvoření, načtení a dotazování indexu vyhledávání pomocí ukázkových dat pro fulltextové vyhledávání. Fulltextové vyhledávání používá Apache Lucene k indexování a dotazům a algoritmus hodnocení BM25 pro vyhodnocování výsledků.
V tomto rychlém startu se vytvoří a dotazuje malý index rychlého startu obsahující data o čtyřech hotelech.
Tip
Dokončený poznámkový blok si můžete stáhnout a spustit.
Požadavky
- Aktivní předplatné Azure – Vytvořte si ho zdarma
- Azure AI Search. Pokud službu nemáte, vytvořte ji. Pro účely tohoto rychlého startu můžete použít úroveň Free.
- Visual Studio Code s rozšířením Pythonu nebo ekvivalentním integrovaným vývojovém prostředím (IDE) s Pythonem 3.10 nebo novějším Pokud nemáte nainstalovanou vhodnou verzi Pythonu, můžete postupovat podle pokynů v kurzu VS Code Python.
Požadavky pro Microsoft Entra ID
Pro doporučené ověřování bez klíčů s ID Microsoft Entra musíte:
- Nainstalujte Azure CLI, které se používá pro ověřování bez klíčů pomocí ID Microsoft Entra.
- Přiřaďte uživatelskému
Search Service Contributor
účtu obě role iSearch Index Data Contributor
role. Role můžete přiřadit na webu Azure Portal v části Řízení přístupu (IAM)>Přidat přiřazení role. Další informace najdete v tématu Připojení k Azure AI Search pomocí rolí.
Načtení informací o prostředcích
Abyste mohli aplikaci ověřit pomocí Search Azure AI, musíte načíst následující informace:
Název proměnné | Hodnota |
---|---|
SEARCH_API_ENDPOINT |
Tuto hodnotu najdete na webu Azure Portal. Vyberte vyhledávací službu a pak v nabídce vlevo vyberte Přehled. Hodnota url v části Essentials je koncový bod, který potřebujete. Příkladem koncového bodu může být https://mydemo.search.windows.net . |
Přečtěte si další informace o ověřování bez klíčů a nastavení proměnných prostředí.
Nastavení prostředí
Vzorový kód spustíte v poznámkovém bloku Jupyter. Proto musíte nastavit prostředí pro spouštění poznámkových bloků Jupyter.
Stáhněte nebo zkopírujte ukázkový poznámkový blok z GitHubu.
Otevřete poznámkový blok v editoru Visual Studio Code.
Vytvořte nové prostředí Pythonu, které se použije k instalaci balíčků, které potřebujete pro účely tohoto kurzu.
Důležité
Neinstalujte balíčky do globální instalace Pythonu. Při instalaci balíčků Pythonu byste měli vždy používat virtuální prostředí nebo prostředí Conda, jinak můžete přerušit globální instalaci Pythonu.
Nastavení může trvat minutu. Pokud narazíte na problémy, prohlédni si prostředí Pythonu ve VS Code.
Pokud je ještě nemáte, nainstalujte poznámkové bloky Jupyter a jádro IPythonu pro poznámkové bloky Jupyter.
pip install jupyter pip install ipykernel python -m ipykernel install --user --name=.venv
Vyberte jádro poznámkového bloku.
- V pravém horním rohu poznámkového bloku vyberte Vybrat jádro.
- Pokud se
.venv
zobrazí v seznamu, vyberte ho. Pokud ho nevidíte, vyberte Vybrat jiné prostředí>.venv
Pythonu jádra.>
Vytvoření, načtení a dotazování indexu vyhledávání
V této části přidáte kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spuštěním programu zobrazíte výsledky v konzole. Podrobné vysvětlení kódu najdete v části s vysvětlením kódu .
Ujistěte se, že je poznámkový blok otevřený v jádru
.venv
, jak je popsáno v předchozí části.Spuštěním první buňky kódu nainstalujte požadované balíčky, včetně azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet ! pip install azure-identity --quiet ! pip install python-dotenv --quiet
Obsah druhé buňky kódu nahraďte následujícím kódem v závislosti na metodě ověřování.
Poznámka:
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit
DefaultAzureCredential
objektemAzureKeyCredential
.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"
Odeberte následující dva řádky z buňky Vytvořit kód indexu. Přihlašovací údaje jsou již nastaveny v předchozí buňce kódu.
from azure.core.credentials import AzureKeyCredential credential = AzureKeyCredential(search_api_key)
Spuštěním buňky Vytvořit index kódu vytvořte vyhledávací index.
Spusťte zbývající buňky kódu postupně, aby se načetly dokumenty a spouštěly dotazy.
Vysvětlení kódu
Vytvoření indexu
SearchIndexClient
slouží k vytváření a správě indexů pro Azure AI Search. Každé pole je identifikováno a name
má zadané type
.
Každé pole má také řadu atributů indexu, které určují, jestli může Azure AI Search prohledávat, filtrovat, řadit a omezující vlastnosti pole. Většina polí je jednoduchých datových typů, ale některé, jako AddressType
jsou složité typy, které umožňují vytvářet bohaté datové struktury v indexu. Další informace opodporovaných
Vytvoření datové části dokumentů a nahrání dokumentů
Použijte akci indexu pro typ operace, například nahrání nebo sloučení a nahrání. Dokumenty pocházejí z ukázky HotelsData na GitHubu.
Prohledání indexu
Výsledky dotazu můžete získat hned po indexování prvního dokumentu, ale skutečné testování indexu by mělo počkat na indexování všech dokumentů.
Použijte metodu vyhledávání třídy search.client.
Ukázkové dotazy v poznámkovém bloku jsou:
- Základní dotaz: Provede prázdné hledání (
search=*
), vrátí neřaděný seznam (skóre hledání = 1,0) libovolných dokumentů. Vzhledem k tomu, že neexistují žádná kritéria, jsou ve výsledcích zahrnuty všechny dokumenty. - Dotaz termínů: Přidá do vyhledávacího výrazu celé termíny ("wifi"). Tento dotaz určuje, že výsledky obsahují pouze tato pole v
select
příkazu. Omezení polí, která se vrátí, minimalizuje množství dat odesílaných zpět přes drát a snižuje latenci vyhledávání. - Filtrovaný dotaz: Přidejte výraz filtru, který vrací pouze ty hotely s hodnocením větším než čtyři, seřazené sestupně.
- Obory polí: Přidejte
search_fields
do oboru provádění dotazu na konkrétní pole. - Omezující vlastnosti: Vygenerujte omezující vlastnosti pro kladné shody nalezené ve výsledcích hledání. Neexistují žádné nulové shody. Pokud výsledky hledání neobsahují termín wifi, wifi se nezobrazí ve fasetové navigační struktuře.
- Vyhledání dokumentu: Vrátí dokument na základě jeho klíče. Tato operace je užitečná, pokud chcete poskytnout podrobnou analýzu, když uživatel vybere položku ve výsledku hledání.
- Automatické dokončování: Zadejte potenciální shody, protože uživatel zadá do vyhledávacího pole. Automatické dokončování používá modul pro návrhy (
sg
) ke zjištění, která pole obsahují potenciální shody pro žádosti s návrhy. V tomto rychlém startu jsouTags
tato pole ,Address/City
,Address/Country
. Pokud chcete simulovat automatické dokončování, předejte písmena sa jako částečný řetězec. Metoda automatického dokončování SearchClient odesílá zpět potenciální shody termínů.
Odebrání indexu
Pokud jste s tímto indexem hotovi, můžete ho odstranit spuštěním buňky Vyčistit kód. Odstranění nepotřebných indexů uvolní místo pro procházení dalších rychlých startů a kurzů.
Naučte se používat klientskou knihovnu Azure.Search.Documents k vytvoření, načtení a dotazování indexu vyhledávání pomocí ukázkových dat pro fulltextové vyhledávání. Fulltextové vyhledávání používá Apache Lucene k indexování a dotazům a algoritmus hodnocení BM25 pro vyhodnocování výsledků.
V tomto rychlém startu se vytvoří a dotazuje malý index rychlého startu obsahující data o čtyřech hotelech.
Tip
Zdrojový kód si můžete stáhnout, abyste mohli začít s hotovým projektem, nebo si podle těchto kroků vytvořte vlastní.
Požadavky
- Aktivní předplatné Azure – Vytvořte si ho zdarma
- Azure AI Search. Pokud službu nemáte, vytvořte ji. Pro účely tohoto rychlého startu můžete použít úroveň Free.
Požadavky pro Microsoft Entra ID
Pro doporučené ověřování bez klíčů s ID Microsoft Entra musíte:
- Nainstalujte Azure CLI, které se používá pro ověřování bez klíčů pomocí ID Microsoft Entra.
- Přiřaďte uživatelskému
Search Service Contributor
účtu obě role iSearch Index Data Contributor
role. Role můžete přiřadit na webu Azure Portal v části Řízení přístupu (IAM)>Přidat přiřazení role. Další informace najdete v tématu Připojení k Azure AI Search pomocí rolí.
Načtení informací o prostředcích
Abyste mohli aplikaci ověřit pomocí Search Azure AI, musíte načíst následující informace:
Název proměnné | Hodnota |
---|---|
SEARCH_API_ENDPOINT |
Tuto hodnotu najdete na webu Azure Portal. Vyberte vyhledávací službu a pak v nabídce vlevo vyberte Přehled. Hodnota url v části Essentials je koncový bod, který potřebujete. Příkladem koncového bodu může být https://mydemo.search.windows.net . |
Přečtěte si další informace o ověřování bez klíčů a nastavení proměnných prostředí.
Nastavení
Vytvořte novou složku
full-text-quickstart
, která bude obsahovat aplikaci, a otevřete v této složce Visual Studio Code pomocí následujícího příkazu:mkdir full-text-quickstart && cd full-text-quickstart
Vytvořte následující
package.json
příkaz:npm init -y
package.json
Aktualizujte na ECMAScript následujícím příkazem:npm pkg set type=module
Nainstalujte klientskou knihovnu Azure AI Search (Azure.Search.Documents) pro JavaScript pomocí:
npm install @azure/search-documents
Pro doporučené ověřování bez hesla nainstalujte klientskou knihovnu Azure Identity pomocí:
npm install @azure/identity
Vytvoření, načtení a dotazování indexu vyhledávání
V předchozí části nastavení jste nainstalovali klientskou knihovnu Azure AI Search a další závislosti.
V této části přidáte kód pro vytvoření indexu vyhledávání, načtení s dokumenty a spouštění dotazů. Spuštěním programu zobrazíte výsledky v konzole. Podrobné vysvětlení kódu najdete v části s vysvětlením kódu .
Ukázkový kód v tomto rychlém startu používá PRO doporučené ověřování bez klíčů ID Microsoft Entra. Pokud dáváte přednost použití klíče rozhraní API, můžete objekt nahradit DefaultAzureCredential
objektem AzureKeyCredential
.
const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
Vytvořte nový soubor s názvem index.ts a vložte do index.ts následující kód:
// 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); });
Vytvořte soubor s názvem hotels.json a vložte do hotels.json následující kód:
{ "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
Vytvořte soubor pro transpilování kódu TypeScript a zkopírujte následující kód pro 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"] }
Transpilovat z TypeScriptu do JavaScriptu.
tsc
Přihlaste se k Azure pomocí následujícího příkazu:
az login
Spusťte kód JavaScriptu pomocí následujícího příkazu:
node index.js
Vysvětlení kódu
Vytvoření indexu
Vytvořte soubor hotels_quickstart_index.json. Tento soubor definuje, jak Azure AI Search funguje s dokumenty, které načtete v dalším kroku. Každé pole je identifikováno name
a má zadané type
. Každé pole má také řadu atributů indexu, které určují, jestli může Azure AI Search prohledávat, filtrovat, řadit a omezující vlastnosti pole. Většina polí je jednoduchých datových typů, ale některé, jako AddressType
jsou složité typy, které umožňují vytvářet bohaté datové struktury v indexu. Další informace opodporovaných
Chceme importovat hotels_quickstart_index.json , aby hlavní funkce přistupovala k definici indexu.
import indexDefinition from './hotels_quickstart_index.json';
interface HotelIndexDefinition {
name: string;
fields: SimpleField[] | ComplexField[];
suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;
V rámci hlavní funkce pak vytvoříme , SearchIndexClient
která se použije k vytváření a správě indexů pro Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Dále chceme odstranit index, pokud už existuje. Tato operace je běžným postupem pro testovací nebo ukázkový kód.
Provedeme to tak, že definujeme jednoduchou funkci, která se pokusí odstranit index.
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.');
}
}
Ke spuštění funkce extrahujeme název indexu z definice indexu indexClient
deleteIndexIfExists()
a předáme indexName
funkci spolu s funkcí.
// 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);
Potom jsme připraveni vytvořit index pomocí createIndex()
metody.
console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);
console.log(`Index named ${index.name} has been created.`);
Nahrání dokumentů
Ve službě Azure AI Search jsou dokumenty datové struktury, které jsou vstupy indexování a výstupy z dotazů. Tato data můžete odeslat do indexu nebo použít indexer. V tomto případě naprogramujeme dokumenty do indexu.
Vstupy dokumentů můžou být řádky v databázi, objekty blob v úložišti objektů blob nebo jako v této ukázce dokumenty JSON na disku. Můžete si stáhnout hotels.json nebo vytvořit vlastní soubor hotels.json s následujícím obsahem:
Podobně jako u indexDefinition je potřeba importovat hotels.json
také do horní části index.ts , aby k datům bylo možné přistupovat v hlavní funkci.
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"];
Abychom mohli data indexovat do vyhledávacího indexu, musíme teď vytvořit .SearchClient
SearchIndexClient
Zatímco se používá k vytvoření a správě indexu, SearchClient
slouží k nahrání dokumentů a dotazování indexu.
Existují dva způsoby, jak vytvořit SearchClient
. První možností je vytvořit úplně SearchClient
od začátku:
const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));
Alternativně můžete použít getSearchClient()
metodu SearchIndexClient
vytvoření SearchClient
:
const searchClient = indexClient.getSearchClient<Hotel>(indexName);
Teď, když je klient definovaný, nahrajte dokumenty do indexu vyhledávání. V tomto případě použijeme metodu mergeOrUploadDocuments()
, která nahraje dokumenty nebo je sloučí s existujícím dokumentem, pokud už existuje dokument se stejným klíčem. Pak zkontrolujte, jestli operace proběhla úspěšně, protože existuje alespoň první dokument.
console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Spusťte program znovu pomocí tsc && node index.ts
příkazu . Měli byste vidět trochu odlišnou sadu zpráv od zpráv, které jste viděli v kroku 1. Tentokrát existuje index a měla by se zobrazit zpráva o jejím odstranění, než aplikace vytvoří nový index a publikuje do něj data.
Než spustíme dotazy v dalším kroku, definujte funkci, která má program čekat na jednu sekundu. To se provádí jenom pro účely testování/ukázky, aby se zajistilo dokončení indexování a dostupnost dokumentů v indexu pro naše dotazy.
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Pokud chcete, aby program čekal na jednu sekundu sleep
, zavolejte funkci:
sleep(1000);
Prohledání indexu
Když jste vytvořili index a nahráli jste dokumenty, můžete do indexu odesílat dotazy. V této části pošleme do indexu vyhledávání pět různých dotazů, abychom vám ukázali různé části funkcí dotazů, které jsou vám k dispozici.
Dotazy jsou napsané ve sendQueries()
funkci, kterou voláme v hlavní funkci následujícím způsobem:
await sendQueries(searchClient);
Dotazy se odesílají pomocí search()
metody searchClient
. Prvním parametrem je hledaný text a druhý parametr určuje možnosti hledání.
Příklad dotazu 1
První dotaz vyhledá *
, což je ekvivalent pro hledání všeho a vybere tři pole v indexu. Osvědčeným postupem je pouze select
pole, která potřebujete, protože stažení nepotřebných dat může do dotazů přidat latenci.
U searchOptions
tohoto dotazu je includeTotalCount
také nastavena hodnota true
, která vrátí počet nalezených odpovídajících výsledků.
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
}
Zbývající níže uvedené dotazy by se také měly přidat do sendQueries()
funkce. Jsou zde odděleny pro čitelnost.
Příklad dotazu 2
V dalším dotazu určíme hledaný termín "wifi"
a zahrneme také filtr, který vrátí pouze výsledky, ve kterých je stav roven 'FL'
. Výsledky jsou také objednány hotelovým hotelem 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)}`);
}
Příklad dotazu 3
Dále je hledání omezeno na jedno prohledávatelné pole pomocí parametru searchFields
. Tento přístup je skvělou možností, jak zefektivnit dotaz, pokud víte, že vás zajímají jenom shody v určitých polích.
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)}`);
}
Příklad dotazu 4
Další běžnou možností zahrnutí do dotazu je facets
. Fasety umožňují poskytovat samoobslužné přechody k podrobnostem z výsledků v uživatelském rozhraní. Výsledky omezujících vlastností se dají v podokně výsledků převést na zaškrtávací políčka.
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)}`);
}
Příklad dotazu 5
Poslední dotaz používá getDocument()
metodu searchClient
. Díky tomu můžete dokument efektivně načíst podle jeho klíče.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Souhrn dotazů
Předchozí dotazy zobrazují v dotazu několik způsobů párování termínů: fulltextové vyhledávání, filtry a automatické dokončování.
Fulltextové vyhledávání a filtry se provádějí pomocí searchClient.search
metody. Vyhledávací dotaz lze předat v řetězci searchText
, zatímco výraz filtru lze předat ve filter
vlastnosti SearchOptions
třídy. Pokud chcete filtrovat bez hledání, stačí předat searchText
parametr search
metody "*". Pokud chcete hledat bez filtrování, nechejte filter
vlastnost nenasazenou nebo vůbec nepředávejte SearchOptions
instanci.
Vyčištění prostředků
Pokud pracujete s vlastním předplatným, je vhodné vždy na konci projektu zkontrolovat, jestli budete vytvořené prostředky ještě potřebovat. Prostředky, které necháte spuštěné, vás stojí peníze. Prostředky můžete odstraňovat jednotlivě nebo můžete odstranit skupinu prostředků, a odstranit tak celou sadu prostředků najednou.
Prostředky můžete najít a spravovat na webu Azure Portal pomocí odkazu Všechny prostředky nebo skupiny prostředků v levém navigačním podokně.
Pokud používáte bezplatnou službu, mějte na paměti, že jste omezeni na tři indexy, indexery a zdroje dat. Jednotlivé položky na webu Azure Portal můžete odstranit, abyste zůstali pod limitem.
Další krok
V tomto rychlém startu jste prošli sadou úloh, kterými jste vytvořili index, načetli ho s dokumenty a spustili dotazy. V různých fázích jsme vzali zástupce, abychom zjednodušili kód pro čitelnost a porozumění. Teď, když znáte základní koncepty, vyzkoušejte kurz, který volá rozhraní API služby Azure AI Search ve webové aplikaci.