Adicionar navegação facetada a uma aplicação de pesquisa

A navegação facetada é usada para filtragem de detalhamento autodirigida nos resultados da consulta em um aplicativo de pesquisa, onde seu aplicativo oferece controles de formulário para definir o escopo da pesquisa para grupos de documentos (por exemplo, categorias ou marcas) e o Azure AI Search fornece as estruturas de dados e filtros para apoiar a experiência.

Neste artigo, aprenda as etapas básicas para criar uma estrutura de navegação facetada no Azure AI Search.

  • Definir atributos de campo no índice
  • Estruturar o pedido e a resposta
  • Adicionar controles de navegação e filtros na camada de apresentação

O código na camada de apresentação faz o trabalho pesado em uma experiência de navegação facetada. As demonstrações e exemplos listados no final deste artigo fornecem código de trabalho que mostra como reunir tudo.

Navegação facetada numa página de pesquisa

As facetas são dinâmicas e retornadas em uma consulta. Uma resposta de pesquisa traz consigo todas as categorias de facetas usadas para navegar pelos documentos no resultado. A consulta é executada primeiro e, em seguida, as facetas são extraídas dos resultados atuais e montadas em uma estrutura de navegação facetada.

Na Pesquisa de IA do Azure, as facetas têm uma camada de profundidade e não podem ser hierárquicas. Se você não estiver familiarizado com estruturas de navegação facetadas, o exemplo a seguir mostra uma à esquerda. As contagens indicam o número de correspondências para cada faceta. Um mesmo documento pode ser representado em múltiplas facetas.

Screenshot of faceted search results.

As facetas podem ajudá-lo a encontrar o que procura, ao mesmo tempo que garantem que não obtém resultados nulos. Como desenvolvedor, as facetas permitem que você exponha os critérios de pesquisa mais úteis para navegar em seu índice de pesquisa.

Ativar facetas no índice

A facetagem é habilitada campo a campo em uma definição de índice quando você define o atributo "facetable" como true.

Embora não seja estritamente necessário, você também deve definir o atributo "filtrável" para que possa criar os filtros necessários que apoiam a experiência de navegação facetada em seu aplicativo de pesquisa.

O exemplo a seguir do índice de amostra "hotéis" mostra "facetable" e "filterable" em campos de cardinalidade baixa que contêm valores únicos ou frases curtas: "Categoria", "Tags", "Classificação".

{
  "name": "hotels",  
  "fields": [
    { "name": "hotelId", "type": "Edm.String", "key": true, "searchable": false, "sortable": false, "facetable": false },
    { "name": "Description", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
    { "name": "HotelName", "type": "Edm.String", "facetable": false },
    { "name": "Category", "type": "Edm.String", "filterable": true, "facetable": true },
    { "name": "Tags", "type": "Collection(Edm.String)", "filterable": true, "facetable": true },
    { "name": "Rating", "type": "Edm.Int32", "filterable": true, "facetable": true },
    { "name": "Location", "type": "Edm.GeographyPoint" }
  ]
}

Escolher campos

As facetas podem ser calculadas sobre campos de valor único, bem como coleções. Os campos que funcionam melhor na navegação facetada têm estas características:

  • Baixa cardinalidade (um pequeno número de valores distintos que se repetem ao longo dos documentos no seu corpus de pesquisa)

  • Valores descritivos curtos (uma ou duas palavras) que renderizarão bem em uma árvore de navegação

Os valores dentro de um campo, e não o nome do campo em si, produzem as facetas em uma estrutura de navegação facetada. Se a faceta for um campo de cadeia de caracteres chamado Cor, as facetas serão azul, verde e qualquer outro valor para esse campo.

Como prática recomendada, verifique os campos para valores nulos, erros ortográficos ou discrepâncias de maiúsculas e minúsculas e versões simples e plurais da mesma palavra. Por padrão, filtros e facetas não passam por análise lexical ou verificação ortográfica, o que significa que todos os valores de um campo "facetable" são facetas potenciais, mesmo que as palavras difiram por um caractere. Opcionalmente, você pode atribuir um normalizador a um campo "filtrável" e "facetable" para suavizar variações em caixa e caracteres.

Padrões em REST e SDKs do Azure

Se você estiver usando um dos SDKs do Azure, seu código deverá definir explicitamente os atributos de campo. Por outro lado, a API REST tem padrões para atributos de campo com base no tipo de dados. Os seguintes tipos de dados são "filtráveis" e "facetable" por padrão:

  • Edm.String
  • Edm.DateTimeOffset
  • Edm.Boolean
  • Edm.Int32, , Edm.Int64Edm.Double
  • Coleções de qualquer um dos tipos acima, por exemplo Collection(Edm.String) ou Collection(Edm.Double)

Não é possível usar Edm.GeographyPoint campos OR Collection(Edm.GeographyPoint) na navegação facetada. As facetas funcionam melhor em campos com baixa cardinalidade. Devido à resolução de coordenadas geográficas, é raro que dois conjuntos de coordenadas sejam iguais em um determinado conjunto de dados. Como tal, as facetas não são suportadas para coordenadas geográficas. Você precisaria de um campo de cidade ou região para facetar por localização.

Gorjeta

Como prática recomendada para otimização de desempenho e armazenamento, desative a opção para campos que nunca devem ser usados como faceta. Em particular, os campos de cadeia de caracteres para valores exclusivos, como um ID ou nome de produto, devem ser definidos para "facetable": false evitar seu uso acidental (e ineficaz) na navegação facetada. Isso é especialmente verdadeiro para a API REST que habilita filtros e facetas por padrão.

Pedido e resposta de facetas

As facetas são especificadas na consulta e a estrutura de navegação facetada é retornada na parte superior da resposta. A estrutura de um pedido e resposta é bastante simples. Na verdade, o verdadeiro trabalho por trás da navegação facetada está na camada de apresentação, abordada em uma seção posterior.

O exemplo REST a seguir é uma consulta não qualificada () que tem como escopo todo o índice ("search": "*"consulte o exemplo de hotéis internos). As facetas são geralmente uma lista de campos, mas esta consulta mostra apenas um para uma resposta mais legível abaixo.

POST https://{{service_name}}.search.windows.net/indexes/hotels/docs/search?api-version={{api_version}}
{
    "search": "*",
    "queryType": "simple",
    "select": "",
    "searchFields": "",
    "filter": "",
    "facets": [ "Category"], 
    "orderby": "",
    "count": true
}

É útil inicializar uma página de pesquisa com uma consulta aberta para preencher completamente a estrutura de navegação facetada. Assim que você passar os termos de consulta na solicitação, a estrutura de navegação facetada terá como escopo apenas as correspondências nos resultados, em vez de todo o índice.

A resposta para o exemplo acima inclui a estrutura de navegação facetada na parte superior. A estrutura é composta por valores de "Categoria" e uma contagem dos hotéis para cada um. É seguido pelo resto dos resultados da pesquisa, cortados aqui para maior brevidade. Este exemplo funciona bem por vários motivos. O número de facetas para este campo está abaixo do limite (o padrão é 10), então todas elas aparecem, e cada hotel no índice de 50 hotéis está representado exatamente em uma dessas categorias.

{
    "@odata.context": "https://demo-search-svc.search.windows.net/indexes('hotels')/$metadata#docs(*)",
    "@odata.count": 50,
    "@search.facets": {
        "Category": [
            {
                "count": 13,
                "value": "Budget"
            },
            {
                "count": 12,
                "value": "Resort and Spa"
            },
            {
                "count": 9,
                "value": "Luxury"
            },
            {
                "count": 7,
                "value": "Boutique"
            },
            {
                "count": 5,
                "value": "Suite"
            },
            {
                "count": 4,
                "value": "Extended-Stay"
            }
        ]
    },
    "value": [
        {
            "@search.score": 1.0,
            "HotelId": "1",
            "HotelName": "Secret Point Motel",
            "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
            "Category": "Boutique",
            "Tags": [
                "pool",
                "air conditioning",
                "concierge"
            ],
            "ParkingIncluded": false,
        }
    ]
}

Sintaxe das facetas

Um parâmetro de consulta de faceta é definido como uma lista delimitada por vírgulas de campos "facetable" e, dependendo do tipo de dados, pode ser parametrizado para definir contagens, ordens de classificação e intervalos: count:<integer>, , , sort:<>interval:<integer>e values:<list>. Para obter mais detalhes sobre parâmetros de faceta, consulte "Parâmetros de consulta" na API REST.

POST https://{{service_name}}.search.windows.net/indexes/hotels/docs/search?api-version={{api_version}}
{
    "search": "*",
    "facets": [ "Category", "Tags,count:5", "Rating,values:1|2|3|4|5"],
    "count": true
}

Para cada árvore de navegação facetada, há um limite padrão das dez principais facetas. Esse padrão faz sentido para estruturas de navegação porque mantém a lista de valores em um tamanho gerenciável. Você pode substituir o padrão atribuindo um valor a "count". Por exemplo, "Tags,count:5" reduz o número de tags na seção Tags para as cinco principais.

Apenas para valores Numeric e DateTime, você pode definir explicitamente valores no campo faceta (por exemplo, facet=Rating,values:1|2|3|4|5) para separar os resultados em intervalos contíguos (intervalos baseados em valores numéricos ou períodos de tempo). Alternativamente, você pode adicionar "intervalo", como em facet=Rating,interval:1.

Cada intervalo é construído usando 0 como ponto de partida, um valor da lista como um ponto de extremidade e, em seguida, cortado do intervalo anterior para criar intervalos discretos.

Discrepâncias na contagem de facetas

Em determinadas circunstâncias, você pode achar que as contagens de facetas não são totalmente precisas devido à arquitetura de fragmentação. Cada índice de pesquisa é distribuído em vários fragmentos, e cada fragmento relata as principais facetas N por contagem de documentos, que são então combinadas em um único resultado. Como são apenas as principais facetas N para cada fragmento, é possível perder ou subestimar os documentos correspondentes na resposta da faceta.

Para garantir a precisão, você pode inflar artificialmente a conta:<número para um grande número> para forçar o relatório completo de cada fragmento. Você pode especificar "count": "0" para facetas ilimitadas. Ou, você pode definir "contar" para um valor maior ou igual ao número de valores exclusivos do campo facetado. Por exemplo, se você estiver enfrentando por um campo "tamanho" que tenha cinco valores exclusivos, poderá definir "count:5" para garantir que todas as correspondências sejam representadas na resposta da faceta.

A contrapartida com essa solução alternativa é o aumento da latência da consulta, portanto, use-a somente quando necessário.

Camada de apresentação

No código do aplicativo, o padrão é usar parâmetros de consulta de faceta para retornar a estrutura de navegação facetada junto com os resultados da faceta, além de uma expressão $filter. A expressão de filtro manipula o evento click e restringe ainda mais o resultado da pesquisa com base na seleção de facetas.

Combinação de facetas e filtros

O trecho de código a seguir do arquivo na demonstração do NYCJobs adiciona o Título Comercial selecionado ao filtro se você selecionar um valor na faceta Título do JobsSearch.cs Negócio.

if (businessTitleFacet != "")
  filter = "business_title eq '" + businessTitleFacet + "'";

Aqui está outro exemplo da amostra de hotéis. O trecho de código a seguir é adicionado categoyrFacet ao filtro se um usuário selecionar um valor da faceta da categoria.

if (!String.IsNullOrEmpty(categoryFacet))
    filter = $"category eq '{categoryFacet}'";

HTML para navegação facetada

O exemplo a seguir, retirado do arquivo do index.cshtml aplicativo de exemplo NYCJobs, mostra a estrutura HTML estática para exibir navegação facetada na página de resultados da pesquisa. A lista de facetas é construída ou reconstruída dinamicamente quando você envia um termo de pesquisa ou seleciona ou desmarca uma faceta.

<div class="widget sidebar-widget jobs-filter-widget">
  <h5 class="widget-title">Filter Results</h5>
    <p id="filterReset"></p>
    <div class="widget-content">

      <h6 id="businessTitleFacetTitle">Business Title</h6>
      <ul class="filter-list" id="business_title_facets">
      </ul>

      <h6>Location</h6>
      <ul class="filter-list" id="posting_type_facets">
      </ul>

      <h6>Posting Type</h6>
      <ul class="filter-list" id="posting_type_facets"></ul>

      <h6>Minimum Salary</h6>
      <ul class="filter-list" id="salary_range_facets">
      </ul>

  </div>
</div>

Crie HTML dinamicamente

O trecho de código a seguir da demonstração (também da index.cshtml NYCJobs) cria dinamicamente o HTML para exibir a primeira faceta, Business Title. Funções semelhantes criam dinamicamente o HTML para as outras facetas. Cada faceta tem um rótulo e uma contagem, que exibe o número de itens encontrados para esse resultado de faceta.

function UpdateBusinessTitleFacets(data) {
  var facetResultsHTML = '';
  for (var i = 0; i < data.length; i++) {
    facetResultsHTML += '<li><a href="javascript:void(0)" onclick="ChooseBusinessTitleFacet(\'' + data[i].Value + '\');">' + data[i].Value + ' (' + data[i].Count + ')</span></a></li>';
  }

  $("#business_title_facets").html(facetResultsHTML);
}

Dicas para trabalhar com facetas

Esta seção é uma coleção de dicas e soluções alternativas que podem ser úteis.

Preservar uma estrutura de navegação de facetas de forma assíncrona de resultados filtrados

Um dos desafios da navegação facetada no Azure AI Search é que as facetas existem apenas para os resultados atuais. Na prática, é comum reter um conjunto estático de facetas para que o usuário possa navegar ao contrário, refazendo etapas para explorar caminhos alternativos através do conteúdo de pesquisa.

Embora este seja um caso de uso comum, não é algo que a estrutura de navegação facetada atualmente fornece prontamente. Os desenvolvedores que desejam facetas estáticas normalmente contornam a limitação emitindo duas consultas filtradas: uma com escopo para os resultados, a outra usada para criar uma lista estática de facetas para fins de navegação.

Facetas claras

Ao criar a página de resultados da pesquisa, lembre-se de adicionar um mecanismo para limpar facetas. Se você adicionar caixas de seleção, poderá ver facilmente como limpar os filtros. Para outros layouts, você pode precisar de um padrão de trilha ou outra abordagem criativa. No exemplo de hotéis em C#, você pode enviar uma pesquisa vazia para redefinir a página. Em contraste, o aplicativo de exemplo NYCJobs fornece um clicável [X] após uma faceta selecionada para limpar a faceta, que é uma fila visual mais forte para o usuário.

Corte os resultados da faceta com mais filtros

Os resultados de facetas são documentos encontrados nos resultados da pesquisa que correspondem a um termo de faceta. No exemplo a seguir, nos resultados de pesquisa para computação em nuvem, 254 itens também têm especificação interna como um tipo de conteúdo. Os itens não são necessariamente mutuamente exclusivos. Se um item atender aos critérios de ambos os filtros, ele será contado em cada um. Essa duplicação é possível ao enfrentar campos Collection(Edm.String) , que geralmente são usados para implementar a marcação de documentos.

Search term: "cloud computing"
Content type
   Internal specification (254)
   Video (10)

Em geral, se você achar que os resultados de facetas são consistentemente muito grandes, recomendamos que você adicione mais filtros para dar aos usuários mais opções para restringir a pesquisa.

Uma experiência de pesquisa apenas com facetas

Se o seu aplicativo usa exclusivamente navegação facetada (ou seja, sem caixa de pesquisa), você pode marcar o campo como searchable=false, filterable=truepara facetable=true produzir um índice mais compacto. Seu índice não incluirá índices invertidos e não haverá análise de texto ou tokenização. Os filtros são feitos em partidas exatas no nível do personagem.

Validar entradas em tempo de consulta

Se você criar a lista de facetas dinamicamente com base na entrada de usuário não confiável, valide se os nomes dos campos facetados são válidos. Ou, fuja dos nomes ao criar URLs usando o .NET ou o Uri.EscapeDataString() equivalente na plataforma de sua escolha.

Demonstrações e amostras

Vários exemplos incluem navegação facetada. Esta seção tem links para os exemplos e também observa qual biblioteca de cliente e idioma é usado para cada um.

Adicionar pesquisa a aplicações Web (React)

Tutoriais e exemplos em C#, Python e JavaScript incluem navegação facetada, bem como filtros, sugestões e preenchimento automático. Esses exemplos usam o React para a camada de apresentação.

Código de exemplo e demonstração do NYCJobs (Ajax)

O exemplo NYCJobs é um aplicativo MVC ASP.NET que usa Ajax na camada de apresentação. Ele está disponível como um aplicativo de demonstração ao vivo e como código-fonte no repositório Azure-Samples no GitHub.