Pesquisa facetada

Com a pesquisa de atributos, é possível anexar informações categóricas a documentos. Um atributo é um par atributo/valor. Por exemplo, o atributo "tamanho" pode ter os valores "pequeno", "médio" e "grande".

Com o uso das pesquisas de atributo, é possível recuperar informações resumidas para ajudar você a refinar uma consulta e detalhar os resultados em uma série de etapas.

Isso é útil para aplicativos como sites de compras, em que você pretende oferecer um grupo de filtros para que os clientes restrinjam os produtos que eles queiram ver.

A distribuição dos valores de um atributo é mostrada pelos dados agregados dele. Por exemplo, o atributo "tamanho" pode aparecer em muitos dos documentos do respectivo grupo de resultados. Os dados agregados desse atributo podem mostrar que o valor "pequeno" aparece 100 vezes, "médio" 300 vezes e "grande" 250 vezes. Cada par atributo/valor representa um subgrupo de documentos no resultado da consulta. Uma chave, chamada de refinamento, está associada a cada par. Inclua refinamentos em uma consulta para recuperar documentos que correspondam à string de consulta e que tenham os valores de atributo correspondentes a um ou mais refinamentos.

Quando você executa uma pesquisa, pode escolher quais atributos coletar e mostrar com os resultados, ou habilitar a descoberta de atributos para selecionar automaticamente aqueles que aparecem com mais frequência nos documentos.

Como adicionar atributos a um documento

Adicione atributos a um documento antes de adicionar o documento a um índice. Faça isso ao mesmo tempo em que você especifica os campos do documento:

package app

import (
    "appengine"
    "appengine/search"
    "net/http"
)

type ComputerDoc struct {
    Name      search.Atom
    Type      search.Atom `search:",facet"`
    RAMSizeGB float64     `search:",facet"`
}

func handlePut(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    index, err := search.Open("products")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    _, err = index.Put(c, "doc1", &ComputerDoc{
        Name:      "x86",
        Type:      "computer",
        RAMSizeGB: 8.0,
    })
    // Handle err and write HTTP response.
}

Um atributo é semelhante a um campo de documento: ele tem um nome e um valor.

Os nomes dos atributos seguem as mesmas regras dos campos de documentos: diferenciam maiúsculas de minúsculas e só podem conter caracteres ASCII. Eles precisam começar com uma letra e podem conter letras, números ou sublinhado. O nome não pode ter mais de 500 caracteres.

O valor de um atributo pode ser uma string atômica com no máximo 500 caracteres ou um número (um valor de ponto flutuante de dupla precisão entre -2.147.483.647 e 2.147.483.647).

Para atribuir vários valores a um atributo em um documento, adicione um atributo com o mesmo nome e tipo várias vezes, usando um valor diferente a cada vez.

Não há limite para o número de valores que um atributo pode ter. Também não há limite para o número de atributos que podem ser adicionados a um documento nem para o número de atributos de nome único em um índice.

Observe que, cada vez que você usa um atributo, ele pode assumir um valor atômico ou numérico. Um atributo com o nome "tamanho" pode ser anexado a um documento com o valor da string "pequeno" e a outro documento com o valor numérico 8. Na verdade, o mesmo atributo pode aparecer várias vezes no mesmo documento com ambos os tipos de valores. Não recomendamos usar valores atômicos e numéricos para o mesmo atributo, mesmo que seja permitido.

O atributo tem um tipo específico quando adicionado a um documento, mas os resultados da pesquisa juntam todos os valores. Por exemplo, os resultados para o atributo "tamanho" podem mostrar que há 100 instâncias do valor "pequeno", 150 instâncias de "médio" e 135 instâncias de valores numéricos no intervalo [4, 8). Os valores numéricos exatos e a distribuição de frequência deles não são mostrados.

Quando você recupera um documento usando uma consulta, não consegue acessar diretamente os atributos e valores dele. É preciso solicitar o retorno das informações do atributo na consulta, como explicado na próxima seção.

Como usar uma pesquisa para recuperar informações de atributos

Solicite o back-end da pesquisa para descobrir os atributos mais utilizados. Isso é chamado de descoberta automática de atributos. Também é possível recuperar informações de atributos explicitamente selecionando um atributo por nome, ou por nome e valor. Combine e misture os três tipos de recuperação de atributos em uma só consulta.

A solicitação de informações do atributo não afetará os documentos retornados pela consulta. Ela pode afetar o desempenho. Fazer uma pesquisa de atributos com a profundidade padrão de 1000 tem o mesmo efeito de configurar o limite do scorer de opções de classificação para 1000.

Descoberta automática de atributos

Com a descoberta automática, é possível procurar os atributos que aparecem com mais frequência no agregado dos documentos. Por exemplo, digamos que os documentos correspondentes à consulta incluam um atributo "cor" que aparece 5 vezes com o valor "vermelho", 5 vezes com o valor "branco" e 5 vezes com a cor "azul". Esse atributo tem uma contagem total de 15. Para fins de descoberta, ele estaria em uma classificação mais alta do que outro atributo chamado "tom", que aparece nos mesmos documentos correspondentes 6 vezes com o valor "escuro" e 7 vezes com o valor "claro".

É preciso configurar a descoberta de atributos na consulta para ativá-la:

func handleSearch(w http.ResponseWriter, r *http.Request) {
    index, err := search.Open("products")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    it := index.Search(c, "name:x86", &search.SearchOptions{
        Facets: {
            search.AutoFacetDiscovery(0, 0),
        },
    })
    facets, err := it.Facets()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    for _, results := range facets {
        for i, facet := range result {
            // The facet results are grouped by facet name.
            // Print the name of each group before its values.
            if i == 0 {
                fmt.Fprintf(w, "Facet %s:\n", facet.Name)
            }
            fmt.Fprintf(w, "    %v: count=%d", facet.Value, facet.Count)
        }
    }
}

Quando você recupera os atributos por descoberta, apenas os dez valores mais frequentes deles são retornados por padrão. É possível aumentar esse limite em até 100 usando o primeiro parâmetro para AutoFacetDiscovery.

Observe que a descoberta automática de atributos não foi feita para retornar todos os atributos possíveis e os respectivos valores. Os atributos retornados pela descoberta podem variar a cada execução. Se você quiser um conjunto fixo de atributos, use um parâmetro return_facets na consulta.

Os valores de string são retornados individualmente. Os valores numéricos de um atributo descoberto são retornados em um único intervalo [min. máx.). É possível examinar esse intervalo e criar um menor para consulta posterior.

Como selecionar atributos pelo nome

Para recuperar informações sobre um atributo apenas pelo nome dele, adicione um FacetDiscovery com o nome do atributo às opções de pesquisa da consulta:

it := index.Search(c, "name:x86", &search.SearchOptions{
    Facets: {
        FacetDiscovery("Type"),
        FacetDiscovery("RAMSizeGB"),
    },
})

Quando você recupera os atributos pelo nome, apenas os 10 valores mais frequentes são retornados.

Como selecionar atributos por nome e valor

Para recuperar informações apenas sobre valores específicos de um atributo, adicione um FacetDiscovery com o nome do atributo e os valores de interesse:

it := index.Search(c, "name:x86", &search.SearchOptions{
    Facets: {
        // Fetch the "Type" facet with Values "computer" and "printer"
        FacetDiscovery("Type",
            search.Atom("computer"),
            search.Atom("printer"),
        ),
        // Fetch the "RAMSizeGB" facet with values in the ranges [0, 4), [4, 8), and [8, max]
        FacetDiscovery("RAMSizeGB",
            search.Range{Start: 0, End: 4},
            search.Range{Start: 4, End: 8},
            search.AtLeast(8),
        ),
    },
})

Os valores em um único FacetDiscovery precisam ser do mesmo tipo, ou uma lista de valores de search.Atom ou, para números, uma lista de search.Range, que são intervalos fechados à esquerda (início) e abertos à direita (final). Se o atributo tiver uma mistura de valores de string e número, adicione opções de FacetDisovery separadas para cada um.

Opções

Adicione a opção FacetDocumentDepth em SearchOptions à consulta para controlar o número mínimo de documentos a serem avaliados para coletar informações de atributos. Se não for especificado, o padrão para esta profundidade é 1000.

Observe que a profundidade do atributo geralmente é muito maior do que o limite da consulta. Os resultados dos atributos são calculados para, no mínimo, o número da profundidade dos documentos. Se você definir o limite de pontuação das opções de classificação acima da profundidade, ele será usado no lugar.

Como recuperar resultados de atributos

Quando você usa parâmetros da pesquisa de atributos em uma consulta, a informação dos atributos agregados vem com o resultado da consulta.

A pesquisa Iterator tem um método Facets, que retorna as informações do atributo agregado como [][]FacetResult. Os resultados são organizados de modo que haja uma fração para cada atributo que apareceu em um documento correspondente à consulta. Para cada resultado, você receberá:

  • O nome do atributo.
  • Um valor para o atributo, da lista dos valores mais frequentes.
  • A contagem aproximada de quantas vezes esse valor apareceu.

Observe que a lista de valores incluirá a string e os valores numéricos do atributo. Se o atributo foi descoberto automaticamente, os valores numéricos dele são retornados como um intervalo [min. máx.). Se você requisitar explicitamente um atributo numérico com um ou mais intervalos na consulta, a lista conterá um intervalo fechado-aberto [início fim) para cada intervalo.

A lista de valores de atributo pode não incluir todos os valores encontrados nos documentos, uma vez que as opções de consulta determinam quantos documentos examinar e quantos valores retornar.

A informação agregada para cada atributo pode ser lida a partir do iterador:

it := index.Search(...)
facets, err := it.Facets()  // And check err != nil.
for _, results := range facets {
    for _, facet := range results {
        ...
    }
}

Por exemplo, documentos que incluam um atributo "tamanho" com valores de string e valores numéricos podem ser retornados pela consulta. Os resultados desse atributo serão criados assim:

[][]search.FacetResult{
    {
        {Name: "size", Value: search.Range{Start: 8, End: 10}, Count: 22},
        {Name: "size", Value: search.Atom("small"), Count: 100},
        {Name: "size", Value: search.Atom("medium"), Count: 300},
        {Name: "size", Value: search.Atom("large"), Count: 250},
    },
}

Como usar atributos para refinar/filtrar uma consulta

Cada FacetResult pode ser usado para restringir ainda mais os resultados e incluir apenas documentos que tenham esses valores de atributo. Para refinar as consultas com uma ou mais dessas chaves, passe-as como opções de pesquisa:

it := index.Search(c, "...", &search.SearchOptions{
    Refinements: []search.Facet{
        facetResult1.Facet,
        facetResult2.Facet,
    },
})

É possível combinar refinamentos para um ou mais atributos na mesma solicitação. Todos os refinamentos pertencentes ao mesmo atributo serão unidos com um OR. Os refinamentos para atributos diferentes serão combinados com AND.

Também é possível criar um Facet personalizado manualmente para uso como refinamento. Consulte a referência para mais informações.