Escolha uma linguagem de programação para a próxima etapa. As bibliotecas de cliente Azure.Search.Documents estão disponíveis nos SDKs do Azure para .NET, Python, Java e JavaScript/Typescript.
Crie um aplicativo de console usando a biblioteca de cliente Azure.Search.Documents para criar, carregar e consultar um índice de pesquisa.
Como alternativa, você pode baixar o código-fonte para começar com um projeto concluído ou seguir estas etapas para criar o seu próprio.
Configurar o ambiente
Inicie o Visual Studio e crie um novo projeto para um aplicativo de console.
Em Ferramentas>Gerenciador de Pacotes NuGet, selecione Gerenciar Pacotes NuGet para Solução....
Selecione Procurar.
Procure o pacote Azure.Search.Documents e selecione a versão 11.0 ou posterior.
Selecione Instalar para adicionar o assembly ao seu projeto e solução.
Criar um cliente de pesquisa
No Program.cs, altere o namespace para AzureSearch.SDK.Quickstart.v11
e adicione as seguintes using
diretivas.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
Copie o código a seguir para criar dois clientes. SearchIndexClient cria o índice e SearchClient carrega e consulta um índice existente. Ambos precisam do ponto de extremidade do serviço e de uma chave de API de administrador para autenticação com direitos de criação/exclusão.
Como o código cria o URI para você, especifique apenas o nome do serviceName
serviço de pesquisa na propriedade.
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);
. . .
}
Criar um índice
Este guia de início rápido cria um índice de hotéis que você carregará com os dados do hotel e executará consultas. Nesta etapa, defina os campos no índice. Cada definição de campo inclui um nome, tipo de dados e atributos que determinam como o campo é usado.
Neste exemplo, os métodos síncronos da biblioteca Azure.Search.Documents são usados para simplicidade e legibilidade. No entanto, para cenários de produção, você deve usar métodos assíncronos para manter seu aplicativo escalável e responsivo. Por exemplo, você usaria CreateIndexAsync em vez de CreateIndex.
Adicione uma definição de classe vazia ao seu projeto: Hotel.cs
Copie o código a seguir para Hotel.cs para definir a estrutura de um documento do hotel. Os atributos no campo determinam como ele é usado em um aplicativo. Por exemplo, o IsFilterable
atributo deve ser atribuído a cada campo que ofereça suporte a uma expressão de filtro.
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; }
}
}
Na biblioteca de cliente Azure.Search.Documents , você pode usar SearchableField e SimpleField para simplificar as definições de campo. Ambos são derivados de um SearchField e podem simplificar seu código:
SimpleField
pode ser qualquer tipo de dados, é sempre não pesquisável (é ignorado para consultas de pesquisa de texto completo) e é recuperável (não está oculto). Outros atributos estão desativados por padrão, mas podem ser habilitados. Você pode usar um SimpleField
para IDs de documento ou campos usados apenas em filtros, facetas ou perfis de pontuação. Em caso afirmativo, certifique-se de aplicar todos os atributos necessários para o cenário, como IsKey = true
para uma ID de documento. Para obter mais informações, consulte SimpleFieldAttribute.cs no código-fonte.
SearchableField
deve ser uma cadeia de caracteres e é sempre pesquisável e recuperável. Outros atributos estão desativados por padrão, mas podem ser habilitados. Como esse tipo de campo é pesquisável, ele suporta sinônimos e o complemento completo das propriedades do analisador. Para obter mais informações, consulte o SearchableFieldAttribute.cs no código-fonte.
Se você usa a API básica SearchField
ou um dos modelos auxiliares, você deve habilitar explicitamente os atributos de filtro, faceta e classificação. Por exemplo, IsFilterable, IsSortable e IsFacetable devem ser explicitamente atribuídos, conforme mostrado no exemplo anterior.
Adicione uma segunda definição de classe vazia ao seu projeto: Address.cs. Copie o código a seguir para a classe.
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; }
}
}
Crie mais duas classes: Hotel.Methods.cs e Address.Methods.cs para ToString()
substituições. Essas classes são usadas para renderizar resultados de pesquisa na saída do console. O conteúdo dessas classes não é fornecido neste artigo, mas você pode copiar o código de arquivos no GitHub.
No Program.cs, crie um objeto SearchIndex e chame o método CreateIndex para expressar o índice em seu serviço de pesquisa. O índice também inclui um SearchSuggester para habilitar o preenchimento automático nos campos especificados.
// 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);
}
Carregar documentos
O Azure AI Search pesquisa sobre o conteúdo armazenado no serviço. Nesta etapa, você carregará documentos JSON que estejam em conformidade com o índice de hotéis que você acabou de criar.
No Azure AI Search, os documentos de pesquisa são estruturas de dados que são entradas para indexação e saídas de consultas. Conforme obtido de uma fonte de dados externa, as entradas de documentos podem ser linhas em um banco de dados, blobs no armazenamento de Blob ou documentos JSON no disco. Neste exemplo, estamos pegando um atalho e incorporando documentos JSON para quatro hotéis no próprio código.
Ao carregar documentos, você deve usar um objeto IndexDocumentsBatch . Um IndexDocumentsBatch
objeto contém uma coleção de Ações, cada uma das quais contém um documento e uma propriedade informando ao Azure AI Search qual ação executar (carregar, mesclar, excluir e mesclarOrUpload).
No Program.cs, crie uma matriz de documentos e ações de índice e, em seguida, passe a matriz para IndexDocumentsBatch
. Os seguintes documentos estão em conformidade com o índice hotels-quickstart, conforme definido pela classe do hotel.
// 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}");
}
}
Depois de inicializar o objeto IndexDocumentsBatch, você pode enviá-lo para o índice chamando IndexDocuments no objeto SearchClient.
Adicione as seguintes linhas a Main()
. O carregamento de documentos é feito usando SearchClient, mas a operação também requer direitos de administrador no serviço, que normalmente é associado a SearchIndexClient. Uma maneira de configurar essa operação é obter SearchClient através (SearchIndexClient
adminClient
neste exemplo).
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
// Load documents
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(ingesterClient);
Como este é um aplicativo de console que executa todos os comandos sequencialmente, adicione um tempo de espera de 2 segundos entre a indexação e as consultas.
// 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);
O atraso de 2 segundos compensa a indexação, que é assíncrona, para que todos os documentos possam ser indexados antes que as consultas sejam executadas. A codificação em um atraso normalmente só é necessária em demonstrações, testes e aplicativos de exemplo.
Pesquisar um índice
Você pode obter os resultados da consulta assim que o primeiro documento for indexado, mas o teste real do índice deve aguardar até que todos os documentos sejam indexados.
Esta seção adiciona duas partes de funcionalidade: lógica de consulta e resultados. Para consultas, use o método Search . Esse método usa texto de pesquisa (a cadeia de caracteres de consulta) e outras opções.
A classe SearchResults representa os resultados.
No Program.cs, crie um WriteDocuments
método que imprima os resultados da pesquisa no console.
// Write search results to console
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.GetResults())
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
private static void WriteDocuments(AutocompleteResults autoResults)
{
foreach (AutocompleteItem result in autoResults.Results)
{
Console.WriteLine(result.Text);
}
Console.WriteLine();
}
Crie um RunQueries
método para executar consultas e retornar resultados. Os resultados são objetos do Hotel. Este exemplo mostra a assinatura do método e a primeira consulta. Esta consulta demonstra o parâmetro Select que permite compor o resultado usando campos selecionados do documento.
// 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);
Na segunda consulta, pesquise um termo, adicione um filtro que selecione documentos em que Classificação é maior que 4 e, em seguida, classifique por Classificação em ordem decrescente. Filter é uma expressão booleana que é avaliada em campos IsFilterable em um índice. As consultas de filtro incluem ou excluem valores. Como tal, não há pontuação de relevância associada a uma consulta de filtro.
// 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);
A terceira consulta demonstra searchFields
, usada para definir o escopo de uma operação de pesquisa de texto completo para campos específicos.
// 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);
A quarta consulta demonstra facets
, que pode ser usada para estruturar uma estrutura de navegação facetada.
// 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);
Na quinta consulta, retorne um documento específico. Uma pesquisa de documentos é uma resposta típica a OnClick
um evento em um conjunto de resultados.
// 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);
A última consulta mostra a sintaxe para preenchimento automático, simulando uma entrada parcial do usuário de sa que resolve para duas correspondências possíveis no sourceFields associado ao sugeridor que você definiu no índice.
// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");
var autoresponse = srchclient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);
Adicionar RunQueries
a 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();
As consultas anteriores mostram várias maneiras de corresponder termos em uma consulta: pesquisa de texto completo, filtros e preenchimento automático.
A pesquisa de texto completo e os filtros são realizados usando o método SearchClient.Search . Uma consulta de pesquisa pode ser passada na searchText
cadeia de caracteres, enquanto uma expressão de filtro pode ser passada na propriedade Filter da classe SearchOptions . Para filtrar sem pesquisar, basta passar "*"
pelo parâmetro do método SearchsearchText
. Para pesquisar sem filtrar, deixe a Filter
propriedade desdefinida ou não passe em uma SearchOptions
instância.
Execute o programa
Pressione F5 para reconstruir o aplicativo e executar o programa em sua totalidade.
A saída inclui mensagens de Console.WriteLine, com a adição de informações de consulta e resultados.
Use um bloco de anotações Jupyter e a biblioteca azure-search-documents no SDK do Azure para Python para criar, carregar e consultar um índice de pesquisa.
Como alternativa, você pode baixar e executar um bloco de anotações concluído.
Configurar o ambiente
Use o Visual Studio Code com a extensão Python, ou um IDE equivalente, com Python 3.10 ou posterior.
Recomendamos um ambiente virtual para este início rápido:
Inicie o Visual Studio Code.
Abra a Paleta de comandos (Ctrl+Shift+P).
Procure por Python: Create Environment.
Selecione Venv.
Selecione um interpretador Python. Escolha a versão 3.10 ou posterior.
A configuração pode demorar um minuto. Se você tiver problemas, consulte Ambientes Python no VS Code.
Instalar pacotes e definir variáveis
Instale pacotes, incluindo azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
Forneça o endpoint e a chave de API para o seu serviço:
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"
Criar um índice
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')
Criar uma carga útil de documentos
Use uma ação de índice para o tipo de operação, como upload ou mesclar e carregar. Os documentos são originários do exemplo HotelsData no 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"
}
}
]
Carregar documentos
# 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)
Execute a sua primeira consulta
Use o método de pesquisa da classe search.client.
Este exemplo executa uma pesquisa vazia (search=*
), retornando uma lista não classificada (pontuação de pesquisa = 1,0) de documentos arbitrários. Como não há critérios, todos os documentos são incluídos nos resultados.
# 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']}")
Executar uma consulta de termo
A próxima consulta adiciona termos inteiros à expressão de pesquisa ("wifi"). Esta consulta especifica que os select
resultados contêm apenas os campos na instrução. Limitar os campos que voltam minimiza a quantidade de dados enviados de volta por fio e reduz a latência de pesquisa.
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']}")
Adicionar um filtro
Adicione uma expressão de filtro, retornando apenas os hotéis com classificação superior a quatro, classificados em ordem decrescente.
# 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"]))
Adicionar escopo de campo
Adicionar search_fields
ao escopo a execução da consulta a campos específicos.
# 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"]))
Adicionar facetas
As facetas são geradas para correspondências positivas encontradas nos resultados da pesquisa. Não há zero correspondências. Se os resultados da pesquisa não incluírem o termo wifi, o wifi não aparecerá na estrutura de navegação facetada.
# Return facets
results = search_client.search(search_text="*", facets=["Category"])
facets = results.get_facets()
for facet in facets["Category"]:
print(" {}".format(facet))
Procurar um documento
Retornar um documento com base em sua chave. Esta operação é útil se você quiser fornecer detalhamento quando um usuário seleciona um item em um resultado de pesquisa.
# 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"]))
Adicionar preenchimento automático
O preenchimento automático pode fornecer possíveis correspondências à medida que o usuário digita na caixa de pesquisa.
O Preenchimento Automático usa um sugeridor (sg
) para saber quais campos contêm possíveis correspondências para solicitações de sugestões. Neste início rápido, esses campos são Tags
, Address/City
, Address/Country
.
Para simular o preenchimento automático, passe as letras sa como uma cadeia de caracteres parcial. O método de preenchimento automático de SearchClient envia de volta possíveis correspondências de termos.
# 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'])
Crie um aplicativo de console Java usando a biblioteca Azure.Search.Documents para criar, carregar e consultar um índice de pesquisa.
Como alternativa, você pode baixar o código-fonte para começar com um projeto concluído ou seguir estas etapas para criar o seu próprio.
Configurar o ambiente
Use as ferramentas a seguir para criar este início rápido.
Criar o projeto
Inicie o Visual Studio Code.
Abra a Paleta de Comandos usando Ctrl+Shift+P. Procure por Criar projeto Java.
Selecione Maven.
Selecione maven-archetype-quickstart.
Selecione a versão mais recente, atualmente 1.4.
Insira azure.search.sample como o ID do grupo.
Digite azuresearchquickstart como o ID do artefato.
Selecione a pasta na qual criar o projeto.
Finalizar a criação do projeto no terminal integrado. Pressione enter para aceitar o padrão para "1.0-SNAPSHOT" e, em seguida, digite "y" para confirmar as propriedades do seu projeto.
Abra a pasta na qual você criou o projeto.
Especificar dependências do Maven
Abra o arquivo pom.xml e adicione as seguintes dependências.
<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>
Altere a versão Java do compilador para 11.
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
Criar um cliente de pesquisa
Abra a App
classe em src, main, java, azure, search, sample. Adicione as seguintes diretivas de importação.
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;
O exemplo a seguir inclui espaços reservados para um nome de serviço de pesquisa, chave de API admin que concede permissões de criação e exclusão e nome de índice. Substitua valores válidos para todos os três espaços reservados. Crie dois clientes: SearchIndexClient cria o índice e SearchClient carrega e consulta um índice existente. Ambos precisam do ponto de extremidade de serviço e de uma chave de API de administrador para autenticação com direitos de criação e exclusão.
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();
}
Criar um índice
Este guia de início rápido cria um índice de hotéis que você carregará com os dados do hotel e executará consultas. Nesta etapa, defina os campos no índice. Cada definição de campo inclui um nome, tipo de dados e atributos que determinam como o campo é usado.
Neste exemplo, os métodos síncronos da biblioteca azure-search-documents são usados para simplicidade e legibilidade. No entanto, para cenários de produção, você deve usar métodos assíncronos para manter seu aplicativo escalável e responsivo. Por exemplo, você usaria SearchAsyncClient em vez de SearchClient.
Adicione uma definição de classe vazia ao seu projeto: Hotel.java
Copie o código a seguir para Hotel.java
definir a estrutura de um documento do hotel. Os atributos no campo determinam como ele é usado em um aplicativo. Por exemplo, a anotação IsFilterable deve ser atribuída a cada campo que ofereça suporte a uma expressão de filtro
// 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 "";
}
}
}
Na biblioteca de cliente Azure.Search.Documents, você pode usar SearchableField e SimpleField para simplificar as definições de campo.
SimpleField
pode ser qualquer tipo de dados, é sempre não pesquisável (é ignorado para consultas de pesquisa de texto completo) e é recuperável (não está oculto). Outros atributos estão desativados por padrão, mas podem ser habilitados. Você pode usar um SimpleField para IDs de documento ou campos usados apenas em filtros, facetas ou perfis de pontuação. Em caso afirmativo, certifique-se de aplicar todos os atributos necessários para o cenário, como IsKey = true para uma ID de documento.
SearchableField
deve ser uma cadeia de caracteres e é sempre pesquisável e recuperável. Outros atributos estão desativados por padrão, mas podem ser habilitados. Como esse tipo de campo é pesquisável, ele suporta sinônimos e o complemento completo das propriedades do analisador.
Se você usa a API básica SearchField
ou um dos modelos auxiliares, você deve habilitar explicitamente os atributos de filtro, faceta e classificação. Por exemplo, isFilterable
, isSortable
, e isFacetable
deve ser explicitamente atribuído, como mostrado no exemplo anterior.
Adicione uma segunda definição de classe vazia ao seu projeto: Address.java
. Copie o código a seguir para a classe.
// 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;
}
No App.java
, crie um SearchIndex
objeto no método e, em seguida, chame main
o createOrUpdateIndex
método para criar o índice no seu serviço de pesquisa. O índice também inclui um SearchSuggester
para habilitar o preenchimento automático nos campos especificados.
// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
.setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
Carregar documentos
O Azure AI Search pesquisa sobre o conteúdo armazenado no serviço. Nesta etapa, você carregará documentos JSON que estejam em conformidade com o índice de hotéis que você acabou de criar.
No Azure AI Search, os documentos de pesquisa são estruturas de dados que são entradas para indexação e saídas de consultas. Conforme obtido de uma fonte de dados externa, as entradas de documentos podem ser linhas em um banco de dados, blobs no armazenamento de Blob ou documentos JSON no disco. Neste exemplo, estamos pegando um atalho e incorporando documentos JSON para quatro hotéis no próprio código.
Ao carregar documentos, você deve usar um objeto IndexDocumentsBatch . Um IndexDocumentsBatch
objeto contém uma coleção de IndexActions, cada um dos quais contém um documento e uma propriedade informando ao Azure AI Search qual ação executar (carregar, mesclar, excluir e mergeOrUpload).
No App.java
, crie documentos e ações de índice e passe-os para IndexDocumentsBatch
. Os seguintes documentos estão em conformidade com o índice hotels-quickstart, conforme definido pela classe do hotel.
// 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");
}
}
Depois de inicializar o IndexDocumentsBatch
objeto, você pode enviá-lo para o índice chamando indexDocuments em seu SearchClient
objeto.
Adicione as seguintes linhas a Main()
. O carregamento de documentos é feito usando SearchClient
o .
// Upload sample hotel documents to the Search Index
uploadDocuments(searchClient);
Como este é um aplicativo de console que executa todos os comandos sequencialmente, adicione um tempo de espera de 2 segundos entre a indexação e as consultas.
// 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)
{
}
O atraso de 2 segundos compensa a indexação, que é assíncrona, para que todos os documentos possam ser indexados antes que as consultas sejam executadas. A codificação em um atraso normalmente só é necessária em demonstrações, testes e aplicativos de exemplo.
Pesquisar um índice
Você pode obter os resultados da consulta assim que o primeiro documento for indexado, mas o teste real do índice deve aguardar até que todos os documentos sejam indexados.
Esta seção adiciona duas partes de funcionalidade: lógica de consulta e resultados. Para consultas, use o método Search. Esse método usa texto de pesquisa (a cadeia de caracteres de consulta) e outras opções.
No App.java
, crie um WriteDocuments
método que imprima os resultados da pesquisa no console.
// 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();
}
Crie um RunQueries
método para executar consultas e retornar resultados. Os resultados são Hotel
objetos. Este exemplo mostra a assinatura do método e a primeira consulta. Esta consulta demonstra o Select
parâmetro que permite compor o resultado usando campos selecionados do documento.
// 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));
}
Na segunda consulta, pesquise um termo, adicione um filtro que selecione documentos em que Classificação é maior que 4 e, em seguida, classifique por Classificação em ordem decrescente. Filtro é uma expressão booleana que é avaliada sobre isFilterable
campos em um índice. As consultas de filtro incluem ou excluem valores. Como tal, não há pontuação de relevância associada a uma consulta de filtro.
// 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));
A terceira consulta demonstra searchFields
, usada para definir o escopo de uma operação de pesquisa de texto completo para campos específicos.
// 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));
A quarta consulta demonstra facets
, que pode ser usada para estruturar uma estrutura de navegação facetada.
// 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));
Na quinta consulta, retorne um documento específico.
// 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();
A última consulta mostra a sintaxe para preenchimento automático, simulando uma entrada parcial do usuário de s que resolve para duas correspondências possíveis no sourceFields
associado com o sugeridor que você definiu no índice.
// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
Adicionar RunQueries
a 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");
As consultas anteriores mostram várias maneiras de corresponder termos em uma consulta: pesquisa de texto completo, filtros e preenchimento automático.
A pesquisa de texto completo e os filtros são realizados usando o método SearchClient.search . Uma consulta de pesquisa pode ser passada na searchText
cadeia de caracteres, enquanto uma expressão de filtro pode ser passada na filter
propriedade da classe SearchOptions . Para filtrar sem pesquisar, basta passar "*" para o searchText
parâmetro do search
método. Para pesquisar sem filtrar, deixe a filter
propriedade desdefinida ou não passe em uma SearchOptions
instância.
Execute o programa
Pressione F5 para reconstruir o aplicativo e executar o programa em sua totalidade.
A saída inclui mensagens de System.out.println
, com a adição de informações de consulta e resultados.
Crie um aplicativo Node.js usando a biblioteca @azure/search-documents para criar, carregar e consultar um índice de pesquisa.
Como alternativa, você pode baixar o código-fonte para começar com um projeto concluído ou seguir estas etapas para criar o seu próprio.
Configurar o ambiente
Usamos as seguintes ferramentas para criar este início rápido.
Criar o projeto
Inicie o Visual Studio Code.
Abra a Paleta de Comandos usando Ctrl+Shift+P e abra o terminal integrado.
Crie um diretório de desenvolvimento, dando-lhe o nome de início rápido:
mkdir quickstart
cd quickstart
Inicialize um projeto vazio com npm executando o seguinte comando. Para inicializar totalmente o projeto, pressione Enter várias vezes para aceitar os valores padrão, exceto para a Licença, que você deve definir como MIT.
npm init
Instale @azure/search-documents
o , o SDK JavaScript /TypeScript para Azure AI Search.
npm install @azure/search-documents
Install dotenv
, que é usado para importar as variáveis de ambiente, como o nome do serviço de pesquisa e a chave da API.
npm install dotenv
Navegue até o diretório de início rápido e confirme se configurou o projeto e suas dependências verificando se o arquivo package.json é semelhante ao seguinte 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"
}
}
Crie um arquivo .env para armazenar os parâmetros do serviço de pesquisa:
SEARCH_API_KEY=<YOUR-SEARCH-ADMIN-API-KEY>
SEARCH_API_ENDPOINT=<YOUR-SEARCH-SERVICE-URL>
Substitua o YOUR-SEARCH-SERVICE-URL
valor pelo nome da URL do ponto de extremidade do serviço de pesquisa. Substitua <YOUR-SEARCH-ADMIN-API-KEY>
pela chave de administrador que você gravou anteriormente.
Criar arquivo index.js
Em seguida, criamos um arquivo index.js , que é o arquivo principal que hospeda nosso código.
Na parte superior deste ficheiro, importamos a @azure/search-documents
biblioteca:
const { SearchIndexClient, SearchClient, AzureKeyCredential, odata } = require("@azure/search-documents");
Em seguida, precisamos exigir que o dotenv
pacote leia os parâmetros do arquivo .env da seguinte maneira:
// 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 || "";
Com nossas variáveis de importação e ambiente em vigor, estamos prontos para definir a função principal.
A maioria das funcionalidades no SDK é assíncrona, por isso fazemos a nossa função async
principal. Também incluímos abaixo main().catch()
a função principal para capturar e registrar quaisquer erros encontrados:
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);
});
Com isso em vigor, estamos prontos para criar um índice.
Criar índice
Crie um arquivo hotels_quickstart_index.json. Este arquivo define como o Azure AI Search funciona com os documentos que você carregará na próxima etapa. Cada campo será identificado por um name
e terá um .type
Cada campo também tem uma série de atributos de índice que especificam se o Azure AI Search pode pesquisar, filtrar, classificar e facetar o campo. A maioria dos campos são tipos de dados simples, mas alguns, como AddressType
são tipos complexos que permitem criar estruturas de dados avançadas em seu índice. Você pode ler mais sobre os tipos de dados suportados e atributos de índice descritos em Criar índice (REST).
Adicione o seguinte conteúdo para hotels_quickstart_index.json ou baixe o arquivo.
{
"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"
]
}
]
}
Com nossa definição de índice em vigor, queremos importar hotels_quickstart_index.json na parte superior da index.js para que a função principal possa acessar a definição de índice.
const indexDefinition = require('./hotels_quickstart_index.json');
Dentro da função principal, criamos um SearchIndexClient
, que é usado para criar e gerenciar índices para o Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Em seguida, queremos excluir o índice, se ele já existir. Esta operação é uma prática comum para o código de teste/demonstração.
Fazemos isso definindo uma função simples que tenta excluir o índice.
async function deleteIndexIfExists(indexClient, indexName) {
try {
await indexClient.deleteIndex(indexName);
console.log('Deleting index...');
} catch {
console.log('Index does not exist yet.');
}
}
Para executar a função, extraímos o nome do índice da definição do índice e passamos o indexName
junto com o indexClient
para a deleteIndexIfExists()
função.
const indexName = indexDefinition["name"];
console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);
Depois disso, estamos prontos para criar o índice com o createIndex()
método.
console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);
console.log(`Index named ${index.name} has been created.`);
Executar o exemplo
Neste ponto, você está pronto para executar o exemplo. Use uma janela de terminal para executar o seguinte comando:
node index.js
Se você baixou o código-fonte e ainda não instalou os pacotes necessários, execute npm install
primeiro.
Você deve ver uma série de mensagens descrevendo as ações que estão sendo tomadas pelo programa.
Abra a Visão geral do seu serviço de pesquisa no portal do Azure. Selecione a guia Índices . Você deve ver algo como o seguinte exemplo:
Na próxima etapa, você adicionará dados ao índice.
Carregar documentos
No Azure AI Search, documentos são estruturas de dados que são entradas para indexação e saídas de consultas. Você pode enviar esses dados por push para o índice ou usar um indexador. Nesse caso, enviaremos programaticamente os documentos para o índice.
As entradas de documentos podem ser linhas em um banco de dados, blobs no armazenamento de Blob ou, como neste exemplo, documentos JSON no disco. Você pode baixar hotels.json ou criar seu próprio arquivo hotels.json com o seguinte conteúdo:
{
"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"
}
}
]
}
Semelhante ao que fizemos com o indexDefinition
, também precisamos importar hotels.json
no topo do index.js para que os dados possam ser acessados em nossa função principal.
const hotelData = require('./hotels.json');
Para indexar dados no índice de pesquisa, agora precisamos criar um SearchClient
arquivo . Enquanto o SearchIndexClient
é usado para criar e gerenciar um índice, o SearchClient
é usado para carregar documentos e consultar o índice.
Há duas maneiras de criar um SearchClient
arquivo . A primeira opção é criar um SearchClient
a partir do zero:
const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));
Como alternativa, você pode usar o getSearchClient()
método do SearchIndexClient
para criar o SearchClient
:
const searchClient = indexClient.getSearchClient(indexName);
Agora que o cliente está definido, carregue os documentos no índice de pesquisa. Neste caso, usamos o mergeOrUploadDocuments()
método, que carrega os documentos ou os mescla com um documento existente se já existir um documento com a mesma chave.
console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Execute o programa novamente com node index.js
o . Você deve ver um conjunto de mensagens ligeiramente diferente daquelas que você viu na Etapa 1. Desta vez, o índice existe e você deve ver uma mensagem sobre como excluí-lo antes que o aplicativo crie o novo índice e publique dados nele.
Antes de executarmos as consultas na próxima etapa, defina uma função para que o programa aguarde um segundo. Isso é feito apenas para fins de teste/demonstração para garantir que a indexação termine e que os documentos estejam disponíveis no índice para nossas consultas.
function sleep(ms) {
var d = new Date();
var d2 = null;
do {
d2 = new Date();
} while (d2 - d < ms);
}
Para que o programa aguarde um segundo, chame a sleep
função como abaixo:
sleep(1000);
Pesquisar um índice
Com um índice criado e documentos carregados, você está pronto para enviar consultas ao índice. Nesta seção, enviamos cinco consultas diferentes para o índice de pesquisa para demonstrar diferentes partes da funcionalidade de consulta disponível para você.
As consultas são escritas em uma sendQueries()
função que chamamos na função principal da seguinte maneira:
await sendQueries(searchClient);
As consultas são enviadas usando o search()
método de searchClient
. O primeiro parâmetro é o texto da pesquisa e o segundo parâmetro especifica as opções de pesquisa.
A primeira consulta pesquisa *
, o que equivale a pesquisar tudo e seleciona três dos campos no índice. É uma prática recomendada apenas select
os campos de que você precisa, pois extrair dados desnecessários pode adicionar latência às suas consultas.
O searchOptions
para esta consulta também tem includeTotalCount
definido como true
, que retorna o número de resultados correspondentes encontrados.
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
}
As restantes consultas descritas abaixo também devem ser adicionadas sendQueries()
à função. Eles são separados aqui para facilitar a leitura.
Na consulta seguinte, especificamos o termo "wifi"
de pesquisa e também incluímos um filtro para retornar resultados somente quando o estado for igual a 'FL'
. Os resultados também são ordenados Rating
pelo hotel.
console.log('Query #2 - Search with filter, orderBy, and select:');
let state = 'FL';
searchOptions = {
filter: odata`Address/StateProvince eq ${state}`,
orderBy: ["Rating desc"],
select: ["HotelId", "HotelName", "Rating"]
};
searchResults = await searchClient.search("wifi", searchOptions);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Em seguida, a pesquisa é limitada a um único campo pesquisável usando o searchFields
parâmetro. Essa abordagem é uma ótima opção para tornar sua consulta mais eficiente se você souber que está interessado apenas em correspondências em determinados campos.
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();
Outra opção comum a ser incluída em uma consulta é facets
. As facetas permitem que você crie filtros em sua interface do usuário para tornar mais fácil para os usuários saberem quais valores eles podem filtrar.
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)}`);
}
A consulta final usa o getDocument()
método do searchClient
. Isso permite que você recupere eficientemente um documento por sua chave.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Executar o exemplo
Execute o programa usando node index.js
. Agora, além das etapas anteriores, as consultas são enviadas e os resultados gravados no console.
Crie um aplicativo Node.js usando a biblioteca @azure/search-documents para criar, carregar e consultar um índice de pesquisa.
Como alternativa, você pode baixar o código-fonte para começar com um projeto concluído ou seguir estas etapas para criar o seu próprio.
Configurar o ambiente
Usamos as seguintes ferramentas para criar este início rápido.
Criar o projeto
Inicie o Visual Studio Code.
Abra a Paleta de Comandos usando Ctrl+Shift+P e abra o terminal integrado.
Crie um diretório de desenvolvimento, dando-lhe o nome de início rápido:
mkdir quickstart
cd quickstart
Inicialize um projeto vazio com npm executando o seguinte comando. Para inicializar totalmente o projeto, pressione Enter várias vezes para aceitar os valores padrão, exceto para a Licença, que você deve definir como MIT.
npm init
Instale @azure/search-documents
o , o SDK JavaScript /TypeScript para Azure AI Search.
npm install @azure/search-documents
Install dotenv
, que é usado para importar as variáveis de ambiente, como o nome do serviço de pesquisa e a chave da API.
npm install dotenv
Navegue até o diretório de início rápido e confirme se configurou o projeto e suas dependências verificando se o arquivo package.json é semelhante ao seguinte 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"
}
}
Crie um arquivo .env para armazenar os parâmetros do serviço de pesquisa:
SEARCH_API_KEY=<YOUR-SEARCH-ADMIN-API-KEY>
SEARCH_API_ENDPOINT=<YOUR-SEARCH-SERVICE-URL>
Substitua o YOUR-SEARCH-SERVICE-URL
valor pelo nome da URL do ponto de extremidade do serviço de pesquisa. Substitua <YOUR-SEARCH-ADMIN-API-KEY>
pela chave de administrador que você gravou anteriormente.
Criar arquivo index.ts
Em seguida, criamos um arquivo index.ts , que é o arquivo principal que hospeda nosso código.
Na parte superior deste ficheiro, importamos a @azure/search-documents
biblioteca:
import {
AzureKeyCredential,
ComplexField,
odata,
SearchClient,
SearchFieldArray,
SearchIndex,
SearchIndexClient,
SearchSuggester,
SimpleField
} from "@azure/search-documents";
Em seguida, precisamos exigir que o dotenv
pacote leia os parâmetros do arquivo .env da seguinte maneira:
// 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 || "";
Com nossas variáveis de importação e ambiente em vigor, estamos prontos para definir a função principal.
A maioria das funcionalidades no SDK é assíncrona, por isso fazemos a nossa função async
principal. Também incluímos abaixo main().catch()
a função principal para capturar e registrar quaisquer erros encontrados:
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);
});
Com isso em vigor, estamos prontos para criar um índice.
Criar índice
Crie um arquivo hotels_quickstart_index.json. Este arquivo define como o Azure AI Search funciona com os documentos que você carregará na próxima etapa. Cada campo será identificado por um name
e terá um .type
Cada campo também tem uma série de atributos de índice que especificam se o Azure AI Search pode pesquisar, filtrar, classificar e facetar o campo. A maioria dos campos são tipos de dados simples, mas alguns, como AddressType
são tipos complexos que permitem criar estruturas de dados avançadas em seu índice. Você pode ler mais sobre os tipos de dados suportados e atributos de índice descritos em Criar índice (REST).
Adicione o seguinte conteúdo para hotels_quickstart_index.json ou baixe o arquivo.
{
"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"
]
}
]
}
Com nossa definição de índice em vigor, queremos importar hotels_quickstart_index.json na parte superior do index.ts para que a função principal possa acessar a definição de índice.
// 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;
Dentro da função principal, criamos um SearchIndexClient
, que é usado para criar e gerenciar índices para o Azure AI Search.
const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));
Em seguida, queremos excluir o índice, se ele já existir. Esta operação é uma prática comum para o código de teste/demonstração.
Fazemos isso definindo uma função simples que tenta excluir o índice.
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.');
}
}
Para executar a função, extraímos o nome do índice da definição do índice e passamos o indexName
junto com o indexClient
para a deleteIndexIfExists()
função.
// 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);
Depois disso, estamos prontos para criar o índice com o createIndex()
método.
console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);
console.log(`Index named ${index.name} has been created.`);
Executar o exemplo
Neste ponto, você está pronto para criar e executar o exemplo. Use uma janela de terminal para executar os seguintes comandos para construir seu código-fonte com e, em tsc
seguida, execute seu código-fonte com node
:
tsc
node index.ts
Se você baixou o código-fonte e ainda não instalou os pacotes necessários, execute npm install
primeiro.
Você deve ver uma série de mensagens descrevendo as ações que estão sendo tomadas pelo programa.
Abra a Visão geral do seu serviço de pesquisa no portal do Azure. Selecione a guia Índices . Você deve ver algo como o seguinte exemplo:
Na próxima etapa, você adicionará dados ao índice.
Carregar documentos
No Azure AI Search, documentos são estruturas de dados que são entradas para indexação e saídas de consultas. Você pode enviar esses dados por push para o índice ou usar um indexador. Nesse caso, enviaremos programaticamente os documentos para o índice.
As entradas de documentos podem ser linhas em um banco de dados, blobs no armazenamento de Blob ou, como neste exemplo, documentos JSON no disco. Você pode baixar hotels.json ou criar seu próprio arquivo hotels.json com o seguinte conteúdo:
{
"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"
}
}
]
}
Semelhante ao que fizemos com o indexDefinition, também precisamos importar hotels.json
no topo do index.ts para que os dados possam ser acessados em nossa função principal.
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"];
Para indexar dados no índice de pesquisa, agora precisamos criar um SearchClient
arquivo . Enquanto o SearchIndexClient
é usado para criar e gerenciar um índice, o SearchClient
é usado para carregar documentos e consultar o índice.
Há duas maneiras de criar um SearchClient
arquivo . A primeira opção é criar um SearchClient
a partir do zero:
const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));
Como alternativa, você pode usar o getSearchClient()
método do SearchIndexClient
para criar o SearchClient
:
const searchClient = indexClient.getSearchClient<Hotel>(indexName);
Agora que o cliente está definido, carregue os documentos no índice de pesquisa. Neste caso, usamos o mergeOrUploadDocuments()
método, que carrega os documentos ou os mescla com um documento existente se já existir um documento com a mesma chave. Em seguida, verifique se a operação foi bem-sucedida porque pelo menos o primeiro documento existe.
console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);
console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);
Execute o programa novamente com tsc && node index.ts
o . Você deve ver um conjunto de mensagens ligeiramente diferente daquelas que você viu na Etapa 1. Desta vez, o índice existe e você deve ver uma mensagem sobre como excluí-lo antes que o aplicativo crie o novo índice e publique dados nele.
Antes de executarmos as consultas na próxima etapa, defina uma função para que o programa aguarde um segundo. Isso é feito apenas para fins de teste/demonstração para garantir que a indexação termine e que os documentos estejam disponíveis no índice para nossas consultas.
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Para que o programa aguarde um segundo, chame a sleep
função:
sleep(1000);
Pesquisar um índice
Com um índice criado e documentos carregados, você está pronto para enviar consultas ao índice. Nesta seção, enviamos cinco consultas diferentes para o índice de pesquisa para demonstrar diferentes partes da funcionalidade de consulta disponível para você.
As consultas são escritas em uma sendQueries()
função que chamamos na função principal da seguinte maneira:
await sendQueries(searchClient);
As consultas são enviadas usando o search()
método de searchClient
. O primeiro parâmetro é o texto da pesquisa e o segundo parâmetro especifica as opções de pesquisa.
A primeira consulta pesquisa *
, o que equivale a pesquisar tudo e seleciona três dos campos no índice. É uma prática recomendada apenas select
os campos de que você precisa, pois extrair dados desnecessários pode adicionar latência às suas consultas.
O searchOptions
para esta consulta também tem includeTotalCount
definido como true
, que retornará o número de resultados correspondentes encontrados.
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
}
As restantes consultas descritas abaixo também devem ser adicionadas sendQueries()
à função. Eles são separados aqui para facilitar a leitura.
Na consulta seguinte, especificamos o termo "wifi"
de pesquisa e também incluímos um filtro para retornar resultados somente quando o estado for igual a 'FL'
. Os resultados também são ordenados Rating
pelo hotel.
console.log('Query #2 - search with filter, orderBy, and select:');
let state = 'FL';
const searchOptions2 = {
filter: odata`Address/StateProvince eq ${state}`,
orderBy: ["Rating desc"],
select: selectFields
};
searchResults = await searchClient.search("wifi", searchOptions2);
for await (const result of searchResults.results) {
console.log(`${JSON.stringify(result.document)}`);
}
Em seguida, a pesquisa é limitada a um único campo pesquisável usando o searchFields
parâmetro. Essa abordagem é uma ótima opção para tornar sua consulta mais eficiente se você souber que está interessado apenas em correspondências em determinados campos.
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)}`);
}
Outra opção comum a ser incluída em uma consulta é facets
. As facetas permitem que você forneça uma análise detalhada autodirigida a partir dos resultados em sua interface do usuário. Os resultados das facetas podem ser transformados em caixas de seleção no painel de resultados.
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)}`);
}
A consulta final usa o getDocument()
método do searchClient
. Isso permite que você recupere eficientemente um documento por sua chave.
console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)
Execute novamente o exemplo
Compile e execute o programa com tsc && node index.ts
o . Agora, além das etapas anteriores, as consultas são enviadas e os resultados gravados no console.
Ao trabalhar na sua própria subscrição, recomendamos que verifique, depois de concluir um projeto, se ainda vai precisar dos recursos que criou. Os recursos que deixar em execução podem custar dinheiro. Pode eliminar recursos individualmente ou eliminar o grupo de recursos para eliminar todo o conjunto de recursos.
Se você estiver usando um serviço gratuito, lembre-se de que está limitado a três índices, indexadores e fontes de dados. Você pode excluir itens individuais no portal para ficar abaixo do limite.
Neste início rápido, você trabalhou em um conjunto de tarefas para criar um índice, carregá-lo com documentos e executar consultas. Em diferentes estágios, pegamos atalhos para simplificar o código para legibilidade e compreensão. Agora que você está familiarizado com os conceitos básicos, experimente um tutorial que chama as APIs do Azure AI Search em um aplicativo Web.