Wybierz język programowania dla następnego kroku. Biblioteki klienta Azure.Search.Documents są dostępne w zestawach AZURE SDK dla platformy .NET, Python, Java i JavaScript/Typescript.
Skompiluj aplikację konsolową przy użyciu biblioteki klienta Azure.Search.Documents , aby utworzyć, załadować i wysłać zapytanie do indeksu wyszukiwania.
Alternatywnie możesz pobrać kod źródłowy, aby rozpocząć od gotowego projektu lub wykonać te kroki, aby utworzyć własny.
Konfigurowanie środowiska
Uruchom program Visual Studio i utwórz nowy projekt dla aplikacji konsolowej.
W obszarze Narzędzia>Menedżer pakietów NuGet wybierz pozycję Zarządzaj pakietami NuGet dla rozwiązania....
Wybierz przycisk Przeglądaj.
Wyszukaj pakiet Azure.Search.Documents i wybierz wersję 11.0 lub nowszą.
Wybierz pozycję Zainstaluj , aby dodać zestaw do projektu i rozwiązania.
Tworzenie klienta wyszukiwania
W Program.cs zmień przestrzeń nazw na AzureSearch.SDK.Quickstart.v11
, a następnie dodaj następujące using
dyrektywy.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
Skopiuj następujący kod, aby utworzyć dwóch klientów. Element SearchIndexClient tworzy indeks, a element SearchClient ładuje i wykonuje zapytania dotyczące istniejącego indeksu. Oba te elementy wymagają punktu końcowego usługi i klucza interfejsu API administratora do uwierzytelniania z uprawnieniami tworzenia/usuwania.
Ponieważ kod tworzy identyfikator URI, określ tylko nazwę usługi wyszukiwania we serviceName
właściwości .
static void Main(string[] args)
{
string serviceName = "<your-search-service-name>";
string apiKey = "<your-search-service-admin-api-key>";
string indexName = "hotels-quickstart";
// Create a SearchIndexClient to send create/delete index commands
Uri serviceEndpoint = new Uri($"https://{serviceName}.search.windows.net/");
AzureKeyCredential credential = new AzureKeyCredential(apiKey);
SearchIndexClient adminClient = new SearchIndexClient(serviceEndpoint, credential);
// Create a SearchClient to load and query documents
SearchClient srchclient = new SearchClient(serviceEndpoint, indexName, credential);
. . .
}
Tworzenie indeksu
W tym przewodniku Szybki start skompilujesz indeks Hotels, dla którego załadujesz dane hotelowe i wykonasz zapytania. W tym kroku zdefiniuj pola w indeksie. Każda definicja pola zawiera nazwę, typ danych i atrybuty określające sposób użycia pola.
W tym przykładzie metody synchroniczne biblioteki Azure.Search.Documents są używane dla uproszczenia i czytelności. Jednak w przypadku scenariuszy produkcyjnych należy użyć metod asynchronicznych, aby zapewnić skalowalność i elastyczność aplikacji. Na przykład należy użyć polecenia CreateIndexAsync zamiast polecenia CreateIndex.
Dodaj pustą definicję klasy do projektu: Hotel.cs
Skopiuj następujący kod do Hotel.cs , aby zdefiniować strukturę dokumentu hotelowego. Atrybuty w polu określają sposób jej użycia w aplikacji. Na przykład IsFilterable
atrybut musi być przypisany do każdego pola obsługującego wyrażenie 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; }
}
}
W bibliotece klienta Azure.Search.Documents można użyć pól SearchableField i SimpleField, aby usprawnić definicje pól. Oba są pochodnymi pola wyszukiwania i mogą potencjalnie uprościć kod:
SimpleField
może być dowolnym typem danych, zawsze nie można wyszukiwać (jest ignorowany w przypadku zapytań wyszukiwania pełnotekstowego) i jest pobierany (nie jest ukryty). Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Można użyć SimpleField
elementu dla identyfikatorów dokumentów lub pól używanych tylko w filtrach, aspektach lub profilach oceniania. Jeśli tak, pamiętaj, aby zastosować wszelkie atrybuty niezbędne do scenariusza, takie jak IsKey = true
identyfikator dokumentu. Aby uzyskać więcej informacji, zobacz SimpleFieldAttribute.cs w kodzie źródłowym.
SearchableField
musi być ciągiem i zawsze można wyszukiwać i pobierać. Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Ponieważ ten typ pola można wyszukiwać, obsługuje synonimy i pełne uzupełnienie właściwości analizatora. Aby uzyskać więcej informacji, zobacz SearchableFieldAttribute.cs w kodzie źródłowym.
Niezależnie od tego, czy używasz podstawowego SearchField
interfejsu API, czy jednego z modeli pomocnika, musisz jawnie włączyć atrybuty filtru, aspektu i sortowania. Na przykład IsFilterable, IsSortable i IsFacetable muszą być jawnie przypisane, jak pokazano w poprzednim przykładzie.
Dodaj drugą pustą definicję klasy do projektu: Address.cs. Skopiuj następujący kod do klasy .
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; }
}
}
Utwórz jeszcze dwie klasy: Hotel.Methods.cs i Address.Methods.cs dla ToString()
przesłonięć. Te klasy są używane do renderowania wyników wyszukiwania w danych wyjściowych konsoli. Zawartość tych klas nie jest podana w tym artykule, ale możesz skopiować kod z plików w usłudze GitHub.
W Program.cs utwórz obiekt SearchIndex, a następnie wywołaj metodę CreateIndex, aby wyrazić indeks w usłudze wyszukiwania. Indeks zawiera również element SearchSuggester umożliwiający automatyczne uzupełnianie w określonych polach.
// Create hotels-quickstart index
private static void CreateIndex(string indexName, SearchIndexClient adminClient)
{
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);
adminClient.CreateOrUpdateIndex(definition);
}
Ładowanie dokumentów
Usługa Azure AI Search wyszukuje zawartość przechowywaną w usłudze. W tym kroku załadujesz dokumenty JSON zgodne z właśnie utworzonym indeksem hotelowym.
W usłudze Azure AI Search dokumenty wyszukiwania to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Dane wejściowe dokumentu uzyskane z zewnętrznego źródła danych mogą być wierszami w bazie danych, obiektami blob w usłudze Blob Storage lub dokumentami JSON na dysku. W tym przykładzie używamy skrótu i osadzamy dokumenty JSON dla czterech hoteli w samym kodzie.
Podczas przekazywania dokumentów należy użyć obiektu IndexDocumentsBatch . Obiekt IndexDocumentsBatch
zawiera kolekcję akcji, z których każda zawiera dokument i właściwość informującą usługę Azure AI Search o tym, jaką akcję wykonać (przekazywanie, scalanie, usuwanie i scalanieOrUpload).
W Program.cs utwórz tablicę dokumentów i akcji indeksu, a następnie przekaż tablicę do IndexDocumentsBatch
. Następujące dokumenty są zgodne z indeksem hotels-quickstart zdefiniowanym przez klasę hotelową.
// 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"
}
}),
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "2",
HotelName = "Old Century 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.",
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 = "Gastronomic 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 Palace 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.",
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.",
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}");
}
}
Po zainicjowaniu obiektu IndexDocumentsBatch możesz wysłać go do indeksu, wywołując element IndexDocuments w obiekcie SearchClient.
Dodaj następujące wiersze do Main()
. Ładowanie dokumentów odbywa się przy użyciu elementu SearchClient, ale operacja wymaga również uprawnień administratora w usłudze, która jest zwykle skojarzona z elementem SearchIndexClient. Jednym ze sposobów skonfigurowania tej operacji jest pobranie elementu SearchClient za pośrednictwem SearchIndexClient
(adminClient
w tym przykładzie).
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);
Ponieważ jest to aplikacja konsolowa, która uruchamia wszystkie polecenia sekwencyjnie, dodaj 2-sekundowy czas oczekiwania między indeksowaniem a zapytaniami.
// 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);
2-sekundowe opóźnienie rekompensuje indeksowanie, co jest asynchroniczne, dzięki czemu wszystkie dokumenty można indeksować przed wykonaniem zapytań. Kodowanie w opóźnieniu jest zwykle konieczne tylko w przypadku pokazów, testów i przykładowych aplikacji.
Przeszukiwanie indeksu
Wyniki zapytania można uzyskać zaraz po indeksowanym pierwszym dokumencie, ale rzeczywiste testowanie indeksu powinno poczekać, aż wszystkie dokumenty zostaną zindeksowane.
W tej sekcji dodano dwie funkcje: logikę zapytań i wyniki. W przypadku zapytań użyj metody Search . Ta metoda przyjmuje tekst wyszukiwania (ciąg zapytania) i inne opcje.
Klasa SearchResults reprezentuje wyniki.
W Program.cs utwórz metodę WriteDocuments
, która wyświetla wyniki wyszukiwania w konsoli programu .
// 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();
}
Utwórz metodę RunQueries
do wykonywania zapytań i zwracania wyników. Wyniki to obiekty hotelowe. W tym przykładzie przedstawiono podpis metody i pierwsze zapytanie. To zapytanie demonstruje parametr Select, który umożliwia tworzenie wyniku przy użyciu wybranych pól z dokumentu.
// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient srchclient)
{
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 = srchclient.Search<Hotel>("*", options);
WriteDocuments(response);
W drugim zapytaniu wyszukaj termin, dodaj filtr, który wybiera dokumenty, w których ocena jest większa niż 4, a następnie sortuj według klasyfikacji w kolejności malejącej. Filter to wyrażenie logiczne, które jest obliczane za pośrednictwem pól IsFilterable w indeksie. Filtrowanie zapytań obejmuje lub wyklucza wartości. W związku z tym nie ma wskaźnika istotności skojarzonego z zapytaniem 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 = srchclient.Search<Hotel>("hotels", options);
WriteDocuments(response);
Trzecie zapytanie demonstruje searchFields
metodę , używaną do określania zakresu operacji wyszukiwania pełnotekstowego do określonych pól.
// 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 = srchclient.Search<Hotel>("pool", options);
WriteDocuments(response);
Czwarte zapytanie demonstruje facets
metodę , która może służyć do struktury struktury nawigacji aspektowej.
// 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 = srchclient.Search<Hotel>("*", options);
WriteDocuments(response);
W piątym zapytaniu zwróć określony dokument. Wyszukiwanie dokumentu to typowa odpowiedź na OnClick
zdarzenie w zestawie wyników.
// Query 5
Console.WriteLine("Query #5: Look up a specific document...\n");
Response<Hotel> lookupResponse;
lookupResponse = srchclient.GetDocument<Hotel>("3");
Console.WriteLine(lookupResponse.Value.HotelId);
Ostatnie zapytanie pokazuje składnię autouzupełniania, symulując częściowe dane wejściowe użytkownika sa , która rozpoznaje dwa możliwe dopasowania w polach źródłowych skojarzonych z sugerowanym elementem zdefiniowanym w indeksie.
// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
var autoresponse = srchclient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);
Add RunQueries
to Main()
.
// Call the RunQueries method to invoke a series of queries
Console.WriteLine("Starting queries...\n");
RunQueries(srchclient);
// End the program
Console.WriteLine("{0}", "Complete. Press any key to end this program...\n");
Console.ReadKey();
Poprzednie zapytania pokazują wiele sposobów dopasowywania terminów w zapytaniu: wyszukiwanie pełnotekstowe, filtry i autouzupełnianie.
Wyszukiwanie pełnotekstowe i filtry są wykonywane przy użyciu metody SearchClient.Search . Zapytanie wyszukiwania można przekazać w searchText
ciągu, a wyrażenie filtru można przekazać we właściwości Filter klasy SearchOptions. Aby filtrować bez wyszukiwania, wystarczy przekazać "*"
searchText
parametr metody Search . Aby wyszukać bez filtrowania, pozostaw Filter
właściwość niezastawioną lub nie przekazuj SearchOptions
w ogóle wystąpienia.
Uruchamianie programu
Naciśnij F5 , aby ponownie skompilować aplikację i uruchomić program w całości.
Dane wyjściowe obejmują komunikaty z elementu Console.WriteLine z dodawaniem informacji o zapytaniu i wynikach.
Użyj notesu Jupyter i biblioteki azure-search-documents w zestawie Azure SDK dla języka Python, aby utworzyć, załadować i wysłać zapytanie do indeksu wyszukiwania.
Alternatywnie możesz pobrać i uruchomić gotowy notes.
Konfigurowanie środowiska
Użyj programu Visual Studio Code z rozszerzeniem języka Python lub równoważnym środowiskiem IDE z językiem Python w wersji 3.10 lub nowszej.
W tym przewodniku Szybki start zalecamy środowisko wirtualne:
Uruchom program Visual Studio Code.
Otwórz paletę poleceń (Ctrl+Shift+P).
Wyszukaj frazę Python: Create Environment (Tworzenie środowiska).
Wybierz pozycję Venv.
Wybierz interpreter języka Python. Wybierz wersję 3.10 lub nowszą.
Skonfigurowanie może potrwać minutę. Jeśli wystąpią problemy, zobacz Środowiska języka Python w programie VS Code.
Instalowanie pakietów i ustawianie zmiennych
Zainstaluj pakiety, w tym azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
Podaj punkt końcowy i klucz interfejsu API dla usługi:
search_endpoint: str = "PUT-YOUR-SEARCH-SERVICE-ENDPOINT-HERE"
search_api_key: str = "PUT-YOUR-SEARCH-SERVICE-ADMIN-API-KEY-HERE"
index_name: str = "hotels-quickstart"
Tworzenie indeksu
from azure.core.credentials import AzureKeyCredential
credential = AzureKeyCredential(search_api_key)
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents import SearchClient
from azure.search.documents.indexes.models import (
ComplexField,
SimpleField,
SearchFieldDataType,
SearchableField,
SearchIndex
)
# Create a search schema
index_client = SearchIndexClient(
endpoint=search_endpoint, credential=credential)
fields = [
SimpleField(name="HotelId", type=SearchFieldDataType.String, key=True),
SearchableField(name="HotelName", type=SearchFieldDataType.String, sortable=True),
SearchableField(name="Description", type=SearchFieldDataType.String, analyzer_name="en.lucene"),
SearchableField(name="Description_fr", type=SearchFieldDataType.String, analyzer_name="fr.lucene"),
SearchableField(name="Category", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="Tags", collection=True, type=SearchFieldDataType.String, facetable=True, filterable=True),
SimpleField(name="ParkingIncluded", type=SearchFieldDataType.Boolean, facetable=True, filterable=True, sortable=True),
SimpleField(name="LastRenovationDate", type=SearchFieldDataType.DateTimeOffset, facetable=True, filterable=True, sortable=True),
SimpleField(name="Rating", type=SearchFieldDataType.Double, facetable=True, filterable=True, sortable=True),
ComplexField(name="Address", fields=[
SearchableField(name="StreetAddress", type=SearchFieldDataType.String),
SearchableField(name="City", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="StateProvince", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="PostalCode", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="Country", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
])
]
scoring_profiles = []
suggester = [{'name': 'sg', 'source_fields': ['Tags', 'Address/City', 'Address/Country']}]
# Create the search index
index = SearchIndex(name=index_name, fields=fields, suggesters=suggester, scoring_profiles=scoring_profiles)
result = index_client.create_or_update_index(index)
print(f' {result.name} created')
Tworzenie ładunku dokumentów
Użyj akcji indeksu dla typu operacji, takiej jak przekazywanie lub scalanie i przekazywanie. Dokumenty pochodzą z przykładu HotelsData w usłudze GitHub.
# Create a documents payload
documents = [
{
"@search.action": "upload",
"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.",
"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.60,
"Address": {
"StreetAddress": "677 5th Ave",
"City": "New York",
"StateProvince": "NY",
"PostalCode": "10022",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "2",
"HotelName": "Old Century 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.",
"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.60,
"Address": {
"StreetAddress": "140 University Town Center Dr",
"City": "Sarasota",
"StateProvince": "FL",
"PostalCode": "34243",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "3",
"HotelName": "Gastronomic 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.80,
"Address": {
"StreetAddress": "3393 Peachtree Rd",
"City": "Atlanta",
"StateProvince": "GA",
"PostalCode": "30326",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "4",
"HotelName": "Sublime Palace 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.",
"Description_fr": "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.",
"Category": "Boutique",
"Tags": [ "concierge", "view", "24-hour front desk service" ],
"ParkingIncluded": "true",
"LastRenovationDate": "1960-02-06T00:00:00Z",
"Rating": 4.60,
"Address": {
"StreetAddress": "7400 San Pedro Ave",
"City": "San Antonio",
"StateProvince": "TX",
"PostalCode": "78216",
"Country": "USA"
}
}
]
Przekaż dokumenty
# Upload documents to the index
search_client = SearchClient(endpoint=search_endpoint,
index_name=index_name,
credential=credential)
try:
result = search_client.upload_documents(documents=documents)
print("Upload of new document succeeded: {}".format(result[0].succeeded))
except Exception as ex:
print (ex.message)
index_client = SearchIndexClient(
endpoint=search_endpoint, credential=credential)
Uruchamianie pierwszego zapytania
Użyj metody wyszukiwania klasy search.client.
W tym przykładzie jest wykonywane puste wyszukiwanie (search=*
), zwracając nies rankingową listę (wynik wyszukiwania = 1,0) dowolnych dokumentów. Ponieważ nie ma żadnych kryteriów, wszystkie dokumenty są uwzględniane w wynikach.
# Run an empty query (returns selected fields, all documents)
results = search_client.search(query_type='simple',
search_text="*" ,
select='HotelName,Description',
include_total_count=True)
print ('Total Documents Matching Query:', results.get_count())
for result in results:
print(result["@search.score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
Uruchamianie zapytania terminowego
Następne zapytanie dodaje całe terminy do wyrażenia wyszukiwania ("wifi"). To zapytanie określa, że wyniki zawierają tylko te pola w instrukcji select
. Ograniczenie pól, które wracają, minimalizuje ilość danych wysyłanych z powrotem przez sieć i zmniejsza opóźnienie wyszukiwania.
results = search_client.search(query_type='simple',
search_text="wifi" ,
select='HotelName,Description,Tags',
include_total_count=True)
print ('Total Documents Matching Query:', results.get_count())
for result in results:
print(result["@search.score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
Dodaj filtr
Dodaj wyrażenie filtru, zwracając tylko te hotele o klasyfikacji większej niż cztery posortowane w kolejności malejącej.
# Add a filter
results = search_client.search(
search_text="hotels",
select='HotelId,HotelName,Rating',
filter='Rating gt 4',
order_by='Rating desc')
for result in results:
print("{}: {} - {} rating".format(result["HotelId"], result["HotelName"], result["Rating"]))
Dodawanie zakresu pól
Dodaj search_fields
do zakresu wykonywania zapytań do określonych pól.
# Add search_fields to scope query matching to the HotelName field
results = search_client.search(
search_text="sublime",
search_fields=['HotelName'],
select='HotelId,HotelName')
for result in results:
print("{}: {}".format(result["HotelId"], result["HotelName"]))
Dodawanie aspektów
Aspekty są generowane dla dodatnich dopasowań znalezionych w wynikach wyszukiwania. Brak dopasowań zerowych. Jeśli wyniki wyszukiwania nie zawierają terminu wifi, sieć Wi-Fi nie jest wyświetlana w strukturze nawigacji aspektowej.
# Return facets
results = search_client.search(search_text="*", facets=["Category"])
facets = results.get_facets()
for facet in facets["Category"]:
print(" {}".format(facet))
Wyszukiwanie dokumentu
Zwracanie dokumentu na podstawie jego klucza. Ta operacja jest przydatna, jeśli chcesz podać przeglądanie szczegółowe, gdy użytkownik wybierze element w wynikach wyszukiwania.
# Look up a specific document by ID
result = search_client.get_document(key="3")
print("Details for hotel '3' are:")
print("Name: {}".format(result["HotelName"]))
print("Rating: {}".format(result["Rating"]))
print("Category: {}".format(result["Category"]))
Dodawanie autouzupełniania
Autouzupełnianie może zapewnić potencjalne dopasowania jako typy użytkowników w polu wyszukiwania.
Autouzupełnianie używa sugestora (sg
), aby wiedzieć, które pola zawierają potencjalne dopasowania do żądań sugestora. W tym przewodniku Szybki start te pola to Tags
: , Address/City
Address/Country
.
Aby symulować autouzupełnianie, przekaż litery sa jako ciąg częściowy. Metoda autouzupełniania elementu SearchClient wysyła potencjalne dopasowania terminów.
# Autocomplete a query
search_suggestion = 'sa'
results = search_client.autocomplete(
search_text=search_suggestion,
suggester_name="sg",
mode='twoTerms')
print("Autocomplete for:", search_suggestion)
for result in results:
print (result['text'])
Skompiluj aplikację konsolową Java przy użyciu biblioteki Azure.Search.Documents , aby utworzyć, załadować i wysłać zapytanie do indeksu wyszukiwania.
Alternatywnie możesz pobrać kod źródłowy, aby rozpocząć od gotowego projektu lub wykonać te kroki, aby utworzyć własny.
Konfigurowanie środowiska
Skorzystaj z poniższych narzędzi, aby utworzyć ten przewodnik Szybki start.
Tworzenie projektu
Uruchom program Visual Studio Code.
Otwórz paletę poleceń przy użyciu Ctrl+Shift+P. Wyszukaj pozycję Utwórz projekt Java.
Wybierz pozycję Maven.
Wybierz pozycję maven-archetype-quickstart.
Wybierz najnowszą wersję, obecnie 1.4.
Wprowadź ciąg azure.search.sample jako identyfikator grupy.
Wprowadź ciąg azuresearchquickstart jako identyfikator artefaktu.
Wybierz folder, w który chcesz utworzyć projekt.
Zakończ tworzenie projektu w zintegrowanym terminalu. Naciśnij Enter, aby zaakceptować wartość domyślną "1.0-SNAPSHOT", a następnie wpisz "y", aby potwierdzić właściwości projektu.
Otwórz folder, w którym został utworzony projekt.
Określanie zależności narzędzia Maven
Otwórz plik pom.xml i dodaj następujące zależności.
<dependencies>
<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>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
Zmień wersję kompilatora Java na 11.
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
Tworzenie klienta wyszukiwania
Otwórz klasę App
w obszarze src, main, java, azure, search, sample. Dodaj następujące dyrektywy importu.
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.credential.AzureKeyCredential;
import com.azure.core.util.Context;
import com.azure.search.documents.SearchClient;
import com.azure.search.documents.SearchClientBuilder;
import com.azure.search.documents.models.SearchOptions;
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.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;
Poniższy przykład zawiera symbole zastępcze dla nazwy usługi wyszukiwania, klucza interfejsu API administratora, który przyznaje uprawnienia do tworzenia i usuwania oraz nazwy indeksu. Zastąp prawidłowe wartości dla wszystkich trzech symboli zastępczych. Utwórz dwóch klientów: Element SearchIndexClient tworzy indeks, a element SearchClient ładuje i wykonuje zapytania dotyczące istniejącego indeksu. Oba te elementy wymagają punktu końcowego usługi i klucza interfejsu API administratora do uwierzytelniania z uprawnieniami do tworzenia i usuwania.
public static void main(String[] args) {
var searchServiceEndpoint = "<YOUR-SEARCH-SERVICE-URL>";
var adminKey = new AzureKeyCredential("<YOUR-SEARCH-SERVICE-ADMIN-KEY>");
String indexName = "<YOUR-SEARCH-INDEX-NAME>";
SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
.endpoint(searchServiceEndpoint)
.credential(adminKey)
.buildClient();
SearchClient searchClient = new SearchClientBuilder()
.endpoint(searchServiceEndpoint)
.credential(adminKey)
.indexName(indexName)
.buildClient();
}
Tworzenie indeksu
W tym przewodniku Szybki start skompilujesz indeks Hotels, dla którego załadujesz dane hotelowe i wykonasz zapytania. W tym kroku zdefiniuj pola w indeksie. Każda definicja pola zawiera nazwę, typ danych i atrybuty określające sposób użycia pola.
W tym przykładzie metody synchroniczne biblioteki azure-search-documents są używane dla uproszczenia i czytelności. Jednak w przypadku scenariuszy produkcyjnych należy użyć metod asynchronicznych, aby zapewnić skalowalność i elastyczność aplikacji. Na przykład należy użyć klasy SearchAsyncClient zamiast elementu SearchClient.
Dodaj pustą definicję klasy do projektu: Hotel.java
Skopiuj następujący kod do Hotel.java
pliku , aby zdefiniować strukturę dokumentu hotelowego. Atrybuty w polu określają sposób jej użycia w aplikacji. Na przykład adnotacja IsFilterable musi być przypisana do każdego pola obsługującego wyrażenie filtru
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package azure.search.sample;
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 "";
}
}
}
W bibliotece klienta Azure.Search.Documents można użyć pól SearchableField i SimpleField , aby usprawnić definicje pól.
SimpleField
może być dowolnym typem danych, zawsze nie można wyszukiwać (jest ignorowany w przypadku zapytań wyszukiwania pełnotekstowego) i jest pobierany (nie jest ukryty). Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Możesz użyć pola SimpleField dla identyfikatorów dokumentów lub pól używanych tylko w filtrach, aspektach lub profilach oceniania. Jeśli tak, pamiętaj, aby zastosować wszelkie atrybuty, które są niezbędne dla scenariusza, takie jak IsKey = true dla identyfikatora dokumentu.
SearchableField
musi być ciągiem i zawsze można wyszukiwać i pobierać. Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Ponieważ ten typ pola można wyszukiwać, obsługuje synonimy i pełne uzupełnienie właściwości analizatora.
Niezależnie od tego, czy używasz podstawowego SearchField
interfejsu API, czy jednego z modeli pomocnika, musisz jawnie włączyć atrybuty filtru, aspektu i sortowania. Na przykład isFilterable
, isSortable
i isFacetable
muszą być jawnie przypisane, jak pokazano w poprzednim przykładzie.
Dodaj drugą pustą definicję klasy do projektu: Address.java
. Skopiuj następujący kod do klasy .
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package azure.search.sample;
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;
}
W App.java
pliku utwórz SearchIndex
obiekt w metodzie main
, a następnie wywołaj createOrUpdateIndex
metodę , aby utworzyć indeks w usłudze wyszukiwania. Indeks zawiera również element umożliwiający SearchSuggester
włączenie autouzupełniania w określonych polach.
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
Ładowanie dokumentów
Usługa Azure AI Search wyszukuje zawartość przechowywaną w usłudze. W tym kroku załadujesz dokumenty JSON zgodne z właśnie utworzonym indeksem hotelowym.
W usłudze Azure AI Search dokumenty wyszukiwania to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Dane wejściowe dokumentu uzyskane z zewnętrznego źródła danych mogą być wierszami w bazie danych, obiektami blob w usłudze Blob Storage lub dokumentami JSON na dysku. W tym przykładzie używamy skrótu i osadzamy dokumenty JSON dla czterech hoteli w samym kodzie.
Podczas przekazywania dokumentów należy użyć obiektu IndexDocumentsBatch . Obiekt IndexDocumentsBatch
zawiera kolekcję indeksów, z których każda zawiera dokument i właściwość informującą usługę Azure AI Search o tym, jaką akcję należy wykonać (przekazywanie, scalanie, usuwanie i scalanieOrUpload).
W App.java
pliku utwórz dokumenty i akcje indeksu, a następnie przekaż je do IndexDocumentsBatch
elementu . Następujące dokumenty są zgodne z indeksem hotels-quickstart zdefiniowanym przez klasę hotelową.
// 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");
}
}
Po zainicjowaniu obiektu można wysłać go do indeksu IndexDocumentsBatch
, wywołując element indexDocuments w SearchClient
obiekcie.
Dodaj następujące wiersze do Main()
. Ładowanie dokumentów odbywa się przy użyciu polecenia SearchClient
.
// Upload sample hotel documents to the Search Index
uploadDocuments(searchClient);
Ponieważ jest to aplikacja konsolowa, która uruchamia wszystkie polecenia sekwencyjnie, dodaj 2-sekundowy czas oczekiwania między indeksowaniem a zapytaniami.
// 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)
{
}
2-sekundowe opóźnienie rekompensuje indeksowanie, co jest asynchroniczne, dzięki czemu wszystkie dokumenty można indeksować przed wykonaniem zapytań. Kodowanie w opóźnieniu jest zwykle konieczne tylko w przypadku pokazów, testów i przykładowych aplikacji.
Przeszukiwanie indeksu
Wyniki zapytania można uzyskać zaraz po indeksowanym pierwszym dokumencie, ale rzeczywiste testowanie indeksu powinno poczekać, aż wszystkie dokumenty zostaną zindeksowane.
W tej sekcji dodano dwie funkcje: logikę zapytań i wyniki. W przypadku zapytań użyj metody Search. Ta metoda przyjmuje tekst wyszukiwania (ciąg zapytania) i inne opcje.
W App.java
pliku utwórz metodę WriteDocuments
, która wyświetla wyniki wyszukiwania w konsoli programu .
// 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();
}
Utwórz metodę RunQueries
do wykonywania zapytań i zwracania wyników. Wyniki to Hotel
obiekty. W tym przykładzie przedstawiono podpis metody i pierwsze zapytanie. To zapytanie demonstruje Select
parametr, który umożliwia tworzenie wyniku przy użyciu wybranych pól 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));
}
W drugim zapytaniu wyszukaj termin, dodaj filtr, który wybiera dokumenty, w których ocena jest większa niż 4, a następnie sortuj według klasyfikacji w kolejności malejącej. Filter jest wyrażeniem logicznym, które jest obliczane względem isFilterable
pól w indeksie. Filtrowanie zapytań obejmuje lub wyklucza wartości. W związku z tym nie ma wskaźnika istotności skojarzonego z zapytaniem 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));
Trzecie zapytanie demonstruje searchFields
metodę , używaną do określania zakresu operacji wyszukiwania pełnotekstowego do określonych pól.
// 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));
Czwarte zapytanie demonstruje facets
metodę , która może służyć do struktury struktury nawigacji aspektowej.
// 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));
W piątym zapytaniu zwróć określony 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();
Ostatnie zapytanie pokazuje składnię autouzupełniania, s, która rozwiązuje dwa możliwe dopasowania w sourceFields
skojarzonym z sugerowanym elementem zdefiniowanym w indeksie.
// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
Add RunQueries
to Main()
.
// 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");
Poprzednie zapytania pokazują wiele sposobów dopasowywania terminów w zapytaniu: wyszukiwanie pełnotekstowe, filtry i autouzupełnianie.
Wyszukiwanie pełnotekstowe i filtry są wykonywane przy użyciu metody SearchClient.search . Zapytanie wyszukiwania można przekazać w searchText
ciągu, a wyrażenie filtru można przekazać we filter
właściwości klasy SearchOptions . Aby filtrować bez wyszukiwania, wystarczy przekazać wartość "*" dla searchText
parametru search
metody . Aby wyszukać bez filtrowania, pozostaw filter
właściwość niezastawioną lub nie przekazuj SearchOptions
w ogóle wystąpienia.
Uruchamianie programu
Naciśnij F5, aby ponownie skompilować aplikację i uruchomić program w całości.
Dane wyjściowe obejmują komunikaty z System.out.println
elementu z dodatkiem informacji o zapytaniu i wynikach.
Utwórz aplikację Node.js przy użyciu biblioteki @azure/search-documents w celu tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania.
Alternatywnie możesz pobrać kod źródłowy, aby rozpocząć od gotowego projektu lub wykonać te kroki, aby utworzyć własny.
Konfigurowanie środowiska
Do utworzenia tego przewodnika Szybki start użyliśmy następujących narzędzi.
Tworzenie projektu
Uruchom program Visual Studio Code.
Otwórz paletę poleceń za pomocą Ctrl+Shift+P i otwórz zintegrowany terminal.
Utwórz katalog deweloperów, podając mu nazwę Szybki start:
mkdir quickstart
cd quickstart
Zainicjuj pusty projekt za pomocą narzędzia npm, uruchamiając następujące polecenie. Aby w pełni zainicjować projekt, naciśnij Enter wiele razy, aby zaakceptować wartości domyślne, z wyjątkiem licencji, którą należy ustawić na MIT.
npm init
Zainstaluj @azure/search-documents
zestaw SDK języka JavaScript/TypeScript dla usługi Azure AI Search.
npm install @azure/search-documents
Zainstaluj program dotenv
, który służy do importowania zmiennych środowiskowych, takich jak nazwa usługi wyszukiwania i klucz interfejsu API.
npm install dotenv
Przejdź do katalogu szybki start , a następnie upewnij się, że projekt i jego zależności zostały skonfigurowane, sprawdzając, czy plik package.json wygląda podobnie do następującego kodu json:
{
"name": "quickstart",
"version": "1.0.0",
"description": "Azure AI Search Quickstart",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Azure",
"Search"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@azure/search-documents": "^11.3.0",
"dotenv": "^16.0.2"
}
}
Utwórz plik env do przechowywania parametrów usługi wyszukiwania:
SEARCH_API_KEY=<YOUR-SEARCH-ADMIN-API-KEY>
SEARCH_API_ENDPOINT=<YOUR-SEARCH-SERVICE-URL>
Zastąp YOUR-SEARCH-SERVICE-URL
wartość nazwą adresu URL punktu końcowego usługi wyszukiwania. Zastąp <YOUR-SEARCH-ADMIN-API-KEY>
ciąg zapisanym wcześniej kluczem administratora.
Tworzenie pliku index.js
Następnie utworzymy plik index.js , który jest głównym plikiem hostujący nasz kod.
W górnej części tego pliku zaimportujemy bibliotekę @azure/search-documents
:
const { SearchIndexClient, SearchClient, AzureKeyCredential, odata } = require("@azure/search-documents");
Następnie musimy wymagać, aby pakiet odczytał dotenv
parametry z pliku env w następujący sposób:
// Load the .env file if it exists
require("dotenv").config();
// Getting endpoint and apiKey from .env file
const endpoint = process.env.SEARCH_API_ENDPOINT || "";
const apiKey = process.env.SEARCH_API_KEY || "";
Dzięki naszym importom i zmiennym środowiskowym możemy zdefiniować funkcję main.
Większość funkcji w zestawie SDK jest asynchroniczna, dlatego robimy naszą główną funkcję async
. Dołączymy również poniżej main().catch()
funkcji main, aby przechwycić i zarejestrować wszelkie napotkane błędy:
async function main() {
console.log(`Running Azure AI Search JavaScript quickstart...`);
if (!endpoint || !apiKey) {
console.log("Make sure to set valid values for endpoint and apiKey with proper authorization.");
return;
}
// remaining quickstart code will go here
}
main().catch((err) => {
console.error("The sample encountered an error:", err);
});
Dzięki tej funkcji możemy utworzyć indeks.
tworzenie indeksu
Utwórz plik hotels_quickstart_index.json. Ten plik definiuje sposób działania usługi Azure AI Search z dokumentami, które będą ładowane w następnym kroku. Każde pole zostanie zidentyfikowane przez element name
i ma określony type
element . Każde pole ma również szereg atrybutów indeksu, które określają, czy usługa Azure AI Search może wyszukiwać, filtrować, sortować i aspekty po polu. Większość pól to proste typy danych, ale niektóre, takie jak AddressType
typy złożone, które umożliwiają tworzenie rozbudowanych struktur danych w indeksie. Więcej informacji na temat obsługiwanych typów danych i atrybutów indeksu opisano w artykule Create Index (REST) (Tworzenie indeksu (REST).
Dodaj następującą zawartość, aby hotels_quickstart_index.json lub pobrać plik.
{
"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"
]
}
]
}
W naszej definicji indeksu chcemy zaimportować hotels_quickstart_index.json w górnej części index.js , aby funkcja główna mogła uzyskać dostęp do definicji indeksu.
const indexDefinition = require('./hotels_quickstart_index.json');
W ramach funkcji main utworzymy element SearchIndexClient
, który służy do tworzenia indeksów usługi Azure AI Search i zarządzania nimi.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Następnie chcemy usunąć indeks, jeśli już istnieje. Ta operacja jest powszechną praktyką w przypadku kodu testowego/demonstracyjnego.
Robimy to, definiując prostą funkcję, która próbuje usunąć indeks.
async function deleteIndexIfExists(indexClient, indexName) {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Aby uruchomić funkcję, wyodrębniamy nazwę indeksu z definicji indeksu i przekazujemy indexName
element wraz z funkcją deleteIndexIfExists()
indexClient
.
const indexName = indexDefinition["name"];
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
Następnie możemy utworzyć indeks za pomocą createIndex()
metody .
console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);
console.log(`Index named ${index.name} has been created.`);
Uruchamianie aplikacji przykładowej
Na tym etapie możesz uruchomić przykład. Użyj okna terminalu, aby uruchomić następujące polecenie:
node index.js
Jeśli pobrano kod źródłowy i nie zainstalowano jeszcze wymaganych pakietów, uruchom najpierw polecenie npm install
.
Powinna zostać wyświetlona seria komunikatów opisujących akcje wykonywane przez program.
Otwórz przegląd usługi wyszukiwania w witrynie Azure Portal. Wybierz kartę Indeksy . Powinien zostać wyświetlony podobny do następującego przykładu:
W następnym kroku dodasz dane do indeksu.
Ładowanie dokumentów
W usłudze Azure AI Search dokumenty to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Możesz wypchnąć takie dane do indeksu lub użyć indeksatora. W takim przypadku programowo wypchniemy dokumenty do indeksu.
Dane wejściowe dokumentu mogą być wierszami w bazie danych, obiektach blob w usłudze Blob Storage lub, jak w tym przykładzie, dokumentach JSON na dysku. Możesz pobrać hotels.json lub utworzyć własny plik hotels.json z następującą zawartością:
{
"value": [
{
"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.",
"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": "Old Century 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.",
"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": "Gastronomic 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 Palace 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.",
"Description_fr": "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.",
"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"
}
}
]
}
Podobnie jak indexDefinition
w przypadku elementu , musimy również zaimportować hotels.json
w górnej części index.js , aby można było uzyskać dostęp do danych w naszej funkcji głównej.
const hotelData = require('./hotels.json');
Aby zaindeksować dane do indeksu wyszukiwania, musimy teraz utworzyć element SearchClient
. SearchIndexClient
Element służy do tworzenia indeksu i zarządzania nim, SearchClient
ale służy do przekazywania dokumentów i wykonywania zapytań względem indeksu.
Istnieją dwa sposoby tworzenia elementu SearchClient
. Pierwszą opcją jest utworzenie od SearchClient
podstaw:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
Alternatywnie możesz użyć getSearchClient()
metody , SearchIndexClient
aby utworzyć element SearchClient
:
const searchClient = indexClient.getSearchClient(indexName);
Teraz, gdy klient jest zdefiniowany, przekaż dokumenty do indeksu wyszukiwania. W takim przypadku używamy mergeOrUploadDocuments()
metody , która przekazuje dokumenty lub scala je z istniejącym dokumentem, jeśli dokument o tym samym kluczu już istnieje.
console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Uruchom ponownie program za pomocą polecenia node index.js
. W kroku 1 powinien zostać wyświetlony nieco inny zestaw komunikatów. Tym razem indeks istnieje i powinien zostać wyświetlony komunikat o usunięciu go, zanim aplikacja utworzy nowy indeks i opublikuje do niego dane.
Przed uruchomieniem zapytań w następnym kroku zdefiniuj funkcję, aby program czekał na jedną sekundę. Odbywa się to tylko w celach testowych/demonstracyjnych, aby upewnić się, że indeksowanie zakończy się i że dokumenty są dostępne w indeksie dla naszych zapytań.
function sleep(ms) {
var d = new Date();
var d2 = null;
do {
d2 = new Date();
} while (d2 - d < ms);
}
Aby program czekał na jedną sekundę, wywołaj sleep
funkcję w następujący sposób:
sleep(1000);
Przeszukiwanie indeksu
Po przekazaniu indeksu i przekazaniu dokumentów możesz wysyłać zapytania do indeksu. W tej sekcji wysyłamy pięć różnych zapytań do indeksu wyszukiwania, aby zademonstrować różne dostępne funkcje zapytań.
Zapytania są zapisywane w sendQueries()
funkcji, którą wywołujemy w funkcji main w następujący sposób:
await sendQueries(searchClient);
Zapytania są wysyłane przy użyciu search()
metody searchClient
. Pierwszy parametr to tekst wyszukiwania, a drugi parametr określa opcje wyszukiwania.
Pierwsze zapytanie wyszukuje *
element , który jest odpowiednikiem wyszukiwania wszystkiego i wybiera trzy pola w indeksie. Jest to najlepsze rozwiązanie tylko select
dla potrzebnych pól, ponieważ ściąganie niepotrzebnych danych może powodować opóźnienie zapytań.
Dla searchOptions
tego zapytania ustawiono includeTotalCount
również wartość true
, która zwraca liczbę znalezionych pasujących wyników.
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
}
Pozostałe zapytania opisane poniżej należy również dodać do sendQueries()
funkcji. Są one tutaj oddzielone w celu czytelności.
W następnym zapytaniu określamy termin "wifi"
wyszukiwania, a także uwzględniamy filtr, aby zwracać wyniki tylko wtedy, gdy stan jest równy 'FL'
. Wyniki są również uporządkowane przez hotel 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)}`);
}
Następnie wyszukiwanie jest ograniczone do pojedynczego pola z możliwością wyszukiwania przy użyciu parametru searchFields
. Takie podejście jest doskonałym rozwiązaniem, aby zwiększyć wydajność zapytania, jeśli wiesz, że interesuje Cię tylko dopasowanie w niektórych polach.
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();
Inną typową opcją uwzględnienia w zapytaniu jest facets
. Aspekty umożliwiają tworzenie filtrów w interfejsie użytkownika, aby ułatwić użytkownikom poznanie wartości, do których mogą filtrować.
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)}`);
}
Ostatnie zapytanie używa getDocument()
metody searchClient
. Dzięki temu można efektywnie pobierać dokument przy użyciu jego klucza.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Uruchamianie aplikacji przykładowej
Uruchom program przy użyciu polecenia node index.js
. Teraz oprócz poprzednich kroków zapytania są wysyłane i wyniki zapisywane w konsoli programu .
Utwórz aplikację Node.js przy użyciu biblioteki @azure/search-documents w celu tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania.
Alternatywnie możesz pobrać kod źródłowy, aby rozpocząć od gotowego projektu lub wykonać te kroki, aby utworzyć własny.
Konfigurowanie środowiska
Do utworzenia tego przewodnika Szybki start użyliśmy następujących narzędzi.
Tworzenie projektu
Uruchom program Visual Studio Code.
Otwórz paletę poleceń za pomocą Ctrl+Shift+P i otwórz zintegrowany terminal.
Utwórz katalog deweloperów, podając mu nazwę Szybki start:
mkdir quickstart
cd quickstart
Zainicjuj pusty projekt za pomocą narzędzia npm, uruchamiając następujące polecenie. Aby w pełni zainicjować projekt, naciśnij Enter wiele razy, aby zaakceptować wartości domyślne, z wyjątkiem licencji, którą należy ustawić na MIT.
npm init
Zainstaluj @azure/search-documents
zestaw SDK języka JavaScript/TypeScript dla usługi Azure AI Search.
npm install @azure/search-documents
Zainstaluj program dotenv
, który służy do importowania zmiennych środowiskowych, takich jak nazwa usługi wyszukiwania i klucz interfejsu API.
npm install dotenv
Przejdź do katalogu szybki start , a następnie upewnij się, że projekt i jego zależności zostały skonfigurowane, sprawdzając, czy plik package.json wygląda podobnie do następującego kodu json:
{
"name": "quickstart",
"version": "1.0.0",
"description": "Azure AI Search Quickstart",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"Azure",
"Search"
],
"author": "Your Name",
"license": "MIT",
"dependencies": {
"@azure/search-documents": "^12.1.0",
"dotenv": "^16.4.5"
}
}
Utwórz plik env do przechowywania parametrów usługi wyszukiwania:
SEARCH_API_KEY=<YOUR-SEARCH-ADMIN-API-KEY>
SEARCH_API_ENDPOINT=<YOUR-SEARCH-SERVICE-URL>
Zastąp YOUR-SEARCH-SERVICE-URL
wartość nazwą adresu URL punktu końcowego usługi wyszukiwania. Zastąp <YOUR-SEARCH-ADMIN-API-KEY>
ciąg zapisanym wcześniej kluczem administratora.
Tworzenie pliku index.ts
Następnie utworzymy plik index.ts , który jest głównym plikiem hostujący nasz kod.
W górnej części tego pliku zaimportujemy bibliotekę @azure/search-documents
:
import {
AzureKeyCredential,
ComplexField,
odata,
SearchClient,
SearchFieldArray,
SearchIndex,
SearchIndexClient,
SearchSuggester,
SimpleField
} from "@azure/search-documents";
Następnie musimy wymagać, aby pakiet odczytał dotenv
parametry z pliku env w następujący sposób:
// Load the .env file if it exists
import dotenv from 'dotenv';
dotenv.config();
// Getting endpoint and apiKey from .env file
const endpoint = process.env.SEARCH_API_ENDPOINT || "";
const apiKey = process.env.SEARCH_API_KEY || "";
Dzięki naszym importom i zmiennym środowiskowym możemy zdefiniować funkcję main.
Większość funkcji w zestawie SDK jest asynchroniczna, dlatego robimy naszą główną funkcję async
. Dołączymy również poniżej main().catch()
funkcji main, aby przechwycić i zarejestrować wszelkie napotkane błędy:
async function main() {
console.log(`Running Azure AI Search JavaScript quickstart...`);
if (!endpoint || !apiKey) {
console.log("Make sure to set valid values for endpoint and apiKey with proper authorization.");
return;
}
// remaining quickstart code will go here
}
main().catch((err) => {
console.error("The sample encountered an error:", err);
});
Dzięki tej funkcji możemy utworzyć indeks.
tworzenie indeksu
Utwórz plik hotels_quickstart_index.json. Ten plik definiuje sposób działania usługi Azure AI Search z dokumentami, które będą ładowane w następnym kroku. Każde pole zostanie zidentyfikowane przez element name
i ma określony type
element . Każde pole ma również szereg atrybutów indeksu, które określają, czy usługa Azure AI Search może wyszukiwać, filtrować, sortować i aspekty po polu. Większość pól to proste typy danych, ale niektóre, takie jak AddressType
typy złożone, które umożliwiają tworzenie rozbudowanych struktur danych w indeksie. Więcej informacji na temat obsługiwanych typów danych i atrybutów indeksu opisano w artykule Create Index (REST) (Tworzenie indeksu (REST).
Dodaj następującą zawartość, aby hotels_quickstart_index.json lub pobrać plik.
{
"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"
]
}
]
}
W naszej definicji indeksu chcemy zaimportować hotels_quickstart_index.json w górnej części index.ts , aby funkcja główna mogła uzyskać dostęp do definicji indeksu.
// Importing the index definition and sample data
import indexDefinition from './hotels_quickstart_index.json';
interface HotelIndexDefinition {
name: string;
fields: SimpleField[] | ComplexField[];
suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;
W ramach funkcji main utworzymy element SearchIndexClient
, który służy do tworzenia indeksów usługi Azure AI Search i zarządzania nimi.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Następnie chcemy usunąć indeks, jeśli już istnieje. Ta operacja jest powszechną praktyką w przypadku kodu testowego/demonstracyjnego.
Robimy to, definiując prostą funkcję, która próbuje usunąć indeks.
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.');
}
}
Aby uruchomić funkcję, wyodrębniamy nazwę indeksu z definicji indeksu i przekazujemy indexName
element wraz z funkcją deleteIndexIfExists()
indexClient
.
// 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);
Następnie możemy utworzyć indeks za pomocą createIndex()
metody .
console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);
console.log(`Index named ${index.name} has been created.`);
Uruchamianie aplikacji przykładowej
Na tym etapie możesz przystąpić do kompilowania i uruchamiania przykładu. Użyj okna terminalu, aby uruchomić następujące polecenia, aby skompilować źródło za pomocą tsc
polecenia , a następnie uruchomić źródło za pomocą node
polecenia :
tsc
node index.ts
Jeśli pobrano kod źródłowy i nie zainstalowano jeszcze wymaganych pakietów, uruchom najpierw polecenie npm install
.
Powinna zostać wyświetlona seria komunikatów opisujących akcje wykonywane przez program.
Otwórz przegląd usługi wyszukiwania w witrynie Azure Portal. Wybierz kartę Indeksy . Powinien zostać wyświetlony podobny do następującego przykładu:
W następnym kroku dodasz dane do indeksu.
Ładowanie dokumentów
W usłudze Azure AI Search dokumenty to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Możesz wypchnąć takie dane do indeksu lub użyć indeksatora. W takim przypadku programowo wypchniemy dokumenty do indeksu.
Dane wejściowe dokumentu mogą być wierszami w bazie danych, obiektach blob w usłudze Blob Storage lub, jak w tym przykładzie, dokumentach JSON na dysku. Możesz pobrać hotels.json lub utworzyć własny plik hotels.json z następującą zawartością:
{
"value": [
{
"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.",
"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": "Old Century 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.",
"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": "Gastronomic 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 Palace 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.",
"Description_fr": "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.",
"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"
}
}
]
}
Podobnie jak w przypadku elementu indexDefinition, musimy również zaimportować hotels.json
w górnej części index.ts , aby można było uzyskać dostęp do danych w naszej funkcji main.
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"];
Aby zaindeksować dane do indeksu wyszukiwania, musimy teraz utworzyć element SearchClient
. SearchIndexClient
Element służy do tworzenia indeksu i zarządzania nim, SearchClient
ale służy do przekazywania dokumentów i wykonywania zapytań względem indeksu.
Istnieją dwa sposoby tworzenia elementu SearchClient
. Pierwszą opcją jest utworzenie od SearchClient
podstaw:
const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));
Alternatywnie możesz użyć getSearchClient()
metody , SearchIndexClient
aby utworzyć element SearchClient
:
const searchClient = indexClient.getSearchClient<Hotel>(indexName);
Teraz, gdy klient jest zdefiniowany, przekaż dokumenty do indeksu wyszukiwania. W takim przypadku używamy mergeOrUploadDocuments()
metody , która przekazuje dokumenty lub scala je z istniejącym dokumentem, jeśli dokument o tym samym kluczu już istnieje. Następnie sprawdź, czy operacja zakończyła się pomyślnie, ponieważ istnieje co najmniej pierwszy dokument.
console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Uruchom ponownie program za pomocą polecenia tsc && node index.ts
. W kroku 1 powinien zostać wyświetlony nieco inny zestaw komunikatów. Tym razem indeks istnieje i powinien zostać wyświetlony komunikat o usunięciu go, zanim aplikacja utworzy nowy indeks i opublikuje do niego dane.
Przed uruchomieniem zapytań w następnym kroku zdefiniuj funkcję, aby program czekał na jedną sekundę. Odbywa się to tylko w celach testowych/demonstracyjnych, aby upewnić się, że indeksowanie zakończy się i że dokumenty są dostępne w indeksie dla naszych zapytań.
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Aby program czekał na jedną sekundę, wywołaj sleep
funkcję:
sleep(1000);
Przeszukiwanie indeksu
Po przekazaniu indeksu i przekazaniu dokumentów możesz wysyłać zapytania do indeksu. W tej sekcji wysyłamy pięć różnych zapytań do indeksu wyszukiwania, aby zademonstrować różne dostępne funkcje zapytań.
Zapytania są zapisywane w sendQueries()
funkcji, którą wywołujemy w funkcji main w następujący sposób:
await sendQueries(searchClient);
Zapytania są wysyłane przy użyciu search()
metody searchClient
. Pierwszy parametr to tekst wyszukiwania, a drugi parametr określa opcje wyszukiwania.
Pierwsze zapytanie wyszukuje *
element , który jest odpowiednikiem wyszukiwania wszystkiego i wybiera trzy pola w indeksie. Jest to najlepsze rozwiązanie tylko select
dla potrzebnych pól, ponieważ ściąganie niepotrzebnych danych może powodować opóźnienie zapytań.
Dla searchOptions
tego zapytania ustawiono includeTotalCount
również wartość true
, która zwróci liczbę znalezionych pasujących wyników.
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
}
Pozostałe zapytania opisane poniżej należy również dodać do sendQueries()
funkcji. Są one tutaj oddzielone w celu czytelności.
W następnym zapytaniu określamy termin "wifi"
wyszukiwania, a także uwzględniamy filtr, aby zwracać wyniki tylko wtedy, gdy stan jest równy 'FL'
. Wyniki są również uporządkowane przez hotel 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)}`);
}
Następnie wyszukiwanie jest ograniczone do pojedynczego pola z możliwością wyszukiwania przy użyciu parametru searchFields
. Takie podejście jest doskonałym rozwiązaniem, aby zwiększyć wydajność zapytania, jeśli wiesz, że interesuje Cię tylko dopasowanie w niektórych polach.
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)}`);
}
Inną typową opcją uwzględnienia w zapytaniu jest facets
. Aspekty umożliwiają zapewnienie samodzielnego przechodzenia do szczegółów z wyników w interfejsie użytkownika. Wyniki aspektów można przekształcić w pola wyboru w okienku wyników.
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)}`);
}
Ostatnie zapytanie używa getDocument()
metody searchClient
. Dzięki temu można efektywnie pobierać dokument przy użyciu jego klucza.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Ponowne uruchamianie przykładu
Skompiluj i uruchom program za pomocą polecenia tsc && node index.ts
. Teraz oprócz poprzednich kroków zapytania są wysyłane i wyniki zapisywane w konsoli programu .
Jeśli pracujesz w ramach własnej subskrypcji, dobrym pomysłem po zakończeniu projektu jest sprawdzenie, czy dalej potrzebujesz utworzonych zasobów. Uruchomione zasoby mogą generować koszty. Zasoby możesz usuwać pojedynczo lub jako grupę zasobów, usuwając cały zestaw zasobów.
Zasoby można znaleźć w witrynie Azure Portal i zarządzać nimi, korzystając z linku Wszystkie zasoby lub Grupy zasobów w okienku nawigacji po lewej stronie.
Jeśli używasz bezpłatnej usługi, pamiętaj, że masz ograniczenie do trzech indeksów, indeksatorów i źródeł danych. Aby utrzymać limit, możesz usunąć poszczególne elementy w witrynie Azure Portal.
W tym przewodniku Szybki start przepracowaliśmy zestaw zadań w celu utworzenia indeksu, załadowania go przy użyciu dokumentów i uruchamiania zapytań. Na różnych etapach zajęliśmy skróty, aby uprościć kod pod kątem czytelności i zrozumienia. Teraz, gdy znasz podstawowe pojęcia, wypróbuj samouczek, który wywołuje interfejsy API usługi Azure AI Search w aplikacji internetowej.