Banco de Dados

Banco de Dados

Melhorando a Experiência do Usuário com Azure Search – Parte 04: recursos avançados

10 set, 2018
Publicidade

Os mecanismos de busca que temos atualmente estão cada vez mais poderosos. E o mesmo acontece com o Azure Search.

No artigo anterior, mostrei como este serviço funciona em operações básicas, mas isso é só a ponta do iceberg. Existe uma grande variedade de funcionalidades e recursos adicionais neste serviço.

Apresento a seguir algumas funcionalidades mais sofisticadas do Azure Search.

Pensando no mundo real

Quando se trabalha com uma base de conhecimento pequena, raramente vamos nos importar com performance das consultas, quantidade de documentos retornados em uma consulta ou mesmo a quantidade de consultas simultâneas, mas no mundo real as coisas são diferentes. Quando sua base chega a ordem de Terabytes, as preocupações e os cuidados necessários para que sua aplicação funcione bem passam a ser bem diferentes.

Vem daí a necessidade de termos recursos mais poderosos, seja para aumentar a velocidade da consulta, para identificação de uma resposta com maior probabilidade de acerto, ou para que se reduza a sobrecarga de trabalho do serviço a fim de permitir que mais usuários possam acessá-lo simultaneamente.

Tipos de consulta

Já apresentei consultas que são classificadas como “pesquisas de texto de forma livre”, que chamei de consultas básicas. Este é o primeiro tipo, mas o Azure Search oferece 10 tipos diferentes de consultas. As demais costumam ser chamadas genericamente de pesquisas avançadas.

As pesquisas avançadas estão intimamente ligadas ao “modelo” ou esquema definido para o índice. Podemos fazer buscas em campos específicos dos documentos, filtrar documentos, ordená-los e realizar buscas de diferentes formas.

Para facilitar, apresento outra vez o quadro com o modelo dos documentos que utilizo no índice criado no segundo artigo da série.

{

    "id" : "31"

  , "MIDIA" : "iMasters"

  , "LINGUA" : "Português"

  , "AUTOR" : "Wagner Crivelini"

  , "TITULO" : "Dicas e truques para importação de arquivos em formato texto"

  , "TEMA" : "Tem inúmeras coisas no mundo de TI que nos são vendidas como se fossem mais fáceis que roubar doce... "

  , "DATA" : "03/12/2013"

  , "LINK" : "https://imasters.com.br/banco-de-dados/dicas-e-truques-para-importacao-de-arquivos-em-formato-texto"

  , "TAGS" : "arquivos texto"

}

Observe que na definição do índice “publications”, defini que todos os campos são do tipo “SEARCHABLE”, com exceção do campo “DATA”.

Observação: os nomes de campos distinguem letras maiúsculas e minúsculas; é necessário conhecer a grafia exata para usá-los nas pesquisas.

A seguir apresento alguns exemplos onde exploro quatro tipos de pesquisas avançadas:

  • Pesquisas por proximidade
  • Pesquisas “fuzzy” (que usam lógica nebulosa)
  • Pesquisas com “boosting” para termos especiais
  • Pesquisas com expressões regulares

Observe que nos exemplos que apresento a seguir eu incluo o parâmetro “queryType=full” para informar que estou realizando pesquisas complexas.

Pesquisas por proximidade

Se tentamos fazer uma busca de duas palavras usando consultas simples, temos apenas duas alternativas: ou pesquisamos uma cadeia de caracteres exata, ou buscamos documentos que contenham as duas palavras desejadas. É comum que nenhuma das opções resolva seu problema.

A primeira alternativa é restrita demais. A segunda é aberta demais. Como fazer para buscar palavras que estejam próximas umas das outras, independente da sequência em que se apresentam?

Pronto, é isso que faz a pesquisa por proximidade.

Veja o exemplo a seguir. Sei que a base de dados tem 17 documentos que apresentam a palavra “modelo” (sugiro que não acredite em mim e tente verificar por conta própria), mas quero fazer uma busca que traga apenas documentos que tenham as palavras “modelo” e “importante” separadas por até três outras palavras.

Essa é a pesquisa que apresento no quadro a seguir. Especifico as palavras que desejo encontrar e uso o operador “~” para informar qual é a distância aceitável para minha pesquisa.

Body (resultado):

(foi encontrado um único documento)

{

    "@odata.context": "https://wcrivelini-test1.search.windows.net/indexes('publications')/$metadata#docs",

    "@odata.count": 1,

    "value": [

        {

            "@search.score": 0.07630738,

            "id": "56",

            "MIDIA": "iMasters",

            "LINGUA": "Português",

            "AUTOR": "Wagner Crivelini",

            "TITULO": "Identificando tabelas sem índices",

            "TEMA": "A maioria dos arquitetos de bancos de dados concorda que toda tabela deve ter uma chave primária. Em outras palavras, toda tabela precisa ter um identificador de registros que garanta a unicidade dos dados (quer dizer: que não existam dados duplicados). Essa é uma característica tão importante no modelo relacional que, no momento em que se cria a chave primária da tabela (daqui por diante chamaremos apenas de PK, do inglês “primary key”), automaticamente o SQL Server cria também um índice clusterizado sobre este(s) mesmo(s) campo(s).",

            "DATA": 42814,

            "LINK": "https://imasters.com.br/banco-de-dados/sql-server/identificando-tabelas-sem-indices/?trace=1519021197&source=admin",

            "TAGS": "HEAP"

        }

    ]

}

Observe que a sequência de palavras que existe no documento não alterou o resultado da busca.

Pesquisas Fuzzy

Já teve problema para encontrar um documento que, no final das contas, você descobriu que a palavra pesquisada tinha sido digitada de forma errada?

Para estas situações, pode-se executar pesquisas com lógica nebulosa (“fuzzy logic”). Estas pesquisas buscam palavras semelhantes a um radical: informa-se o radical desejado seguido do operador “~” e um valor entre 0 (valor default que pode ser omitido) e 2. Este operador especifica a distância para edição do texto buscado. Quanto maior o valor informado, mais alternativas do radical serão aceitas.

Veja um exemplo interessante: quero buscar os artigos que tenham em seu título a palavra “base”, mas decidi usar uma pesquisa Fuzzy. Para simplificar o resultado, observe que especifico que a busca será feita apenas no campo TITULO e que o resultado exibirá apenas este campo. O resultado é bastante interessante.

Body (resultado):

{

    "@odata.context": "https://wcrivelini-test1.search.windows.net/indexes('publications')/$metadata#docs(TITULO)",

    "@odata.count": 5,

    "value": [

        {

            "@search.score": 1.1838781,

            "TITULO": "Preparando sua Base de Dados para o Mundo"

        },

        {

            "@search.score": 1.1838781,

            "TITULO": "Usando a dica HASH JOIN - estudo de caso"

        },

        {

            "@search.score": 0.9183874,

            "TITULO": "As famosas bases de dados “lentas”"

        },

        {

            "@search.score": 0.9183874,

            "TITULO": "Restaurando bases de dados em instâncias diferentes"

        },

        {

            "@search.score": 0.59193903,

            "TITULO": "Databasecast 65: front-end, back-end e DBA"

        }

    ]

}

Veja que o resultado traz artigos que tenham em seu título as palavras “base”, “bases”, “caso” e “back”. A variabilidade da palavra encontrada é até maior do que se poderia esperar, mas é fácil perceber a lógica usada para incluir nesta lista as palavras “cASo” e “BAck”.

Pesquisas com boosting de termos

Esse tipo de pesquisa envolve duas ou mais palavras atribuindo um peso a cada uma delas. Isso informa ao otimizador de buscas qual é o termo prioritário e, assim, aumenta a nota (ou “score”) dos documentos que tiverem esta palavra.

Para informar o peso de uma palavra, basta acrescentar o operador “^”, seguido de um número inteiro.

No exemplo a seguir quero encontrar entre os artigos que já publiquei, aqueles que falassem do produto CASSANDRA comparado a SGBDs transacionais. Então criei a pesquisa a seguir, que busca no campo TEMA os termos “CASSANDRA” e “transacional”, sendo que o primeiro tem peso 2.

Body (resultado):

{

    "@odata.context": "https://wcrivelini-test1.search.windows.net/indexes('publications')/$metadata#docs(TITULO,TEMA)",

    "@odata.count": 4,

    "value": [

        {

            "@search.score": 0.96760786,

            "TITULO": "Databasecast 68: História do Cassandra",

            "TEMA": "Neste episódio do DatabaseCast Mauro Pichilian (@pichiliani) ), Wagner Crivelini (@wcrivelini) e o convidado Eiti Kimura (@eitikimura) flertam com as mulheres para falar sobre o Cassandra. Neste episódio você vai aprender como se adaptar rápido a mudanças, andar sobre brasas em fogo e cacos de vidro, como o Cassandra é um NoSQL diferentes dos demais, deixar a consistência transacional de lado e marcar os erros na migração para um NoSQL na sua cartela de bingo."

        },

        {

            "@search.score": 0.2599703,

            "TITULO": "DatabaseCast 61: Particionamento de dados",

            "TEMA": "Neste episódio do DatabaseCast Mauro Pichilian (@pichiliani), Wagner Crivelini (@wcrivelini) e o convidado Ricardo Rezende (@ricarezende) separam os dados como se eles estivesse dentro de um pacote de M&Ms. Neste episódio você vai aprender quais são os tipos de particionamento no Oracle, SQL Server, MySQL, PostgreSQL, MongoDB e Cassandra, discutir se o Hadoop é ou não um banco de dados, mandar um alô para o pessoal do Suriname e da Suécia e aprender a chamar certas soluções de gambiware."

        },

        {

            "@search.score": 0.19911498,

            "TITULO": "DatabaseCast 03: NoSQL",

            "TEMA": "Nesta terceira edição do DatabaseCast os participantes deixam a consistência transacional de lado para falar sobre uma das novas tecnologias da área: NoSQL ou Not Only SQL."

        },

        {

            "@search.score": 0.1161504,

            "TITULO": "Respeite a normalização",

            "TEMA": "Existem vários tipos de aplicações que não precisam de normalização. Estão aí os modelos NOSQL, cada vez mais populares, que não me deixam mentir. Mas se você vai trabalhar no mundo transacional, faça um favor a você mesmo: tenha muito respeito pela normalização do seu banco de dados relacional. Normalização é uma das principais ferramentas para garantir que sua base trabalhe com dados de boa qualidade, ou seja: dados consistentes e sem redundância."

        }

    ]

}

Foram encontrados quatro documentos, mas os scores variam bastante e não é difícil entender:

  • O primeiro documento tem nota 0,97 porque ele contém os dois termos pesquisados
  • O documento 2 tem uma nota muito menor (0,26), porque ele contém só a palavra principal, “CASSANDRA”.
  • O documento 3 tem nota menor (0,20) porque ele só contém a palavra secundária, “transacional”.
  • E o documento 4 tem nota ainda menor (0,12), porque ele só tem a palavra “transacional”, mas bem mais longe do início do campo TEMA.

A questão da distância, ou seja, o número de palavras descartadas até encontrar a(s) palavra(s) desejada(s), explica também porque o documento 2 tem uma nota baixa mesmo contendo a palavra principal.

Pesquisas com expressões regulares

Quando se fala de pesquisas de dados, ainda não há nada mais conhecido e respeitado do que o uso de expressões regulares. Essa “linguagem” de pesquisa de padrões apareceu nos anos 1950 e resiste bravamente até hoje. Não é à toa que este tipo de pesquisa também seja suportado pelo Azure Search.

Neste exemplo, busco pelas palavras “ROLLUP” e “ROLLBACK” usando uma expressão REGEX elementar. Lembre-se de que o Azure Search reconhece expressões REGEX desde que estejam entre caracteres “/”, isto é, no início e no final da expressão.

Body (resultado):

 {

    "@odata.context": "https://wcrivelini-test1.search.windows.net/indexes('publications')/$metadata#docs(AUTOR,TITULO,TEMA)",

    "@odata.count": 3,

    "value": [

        {

            "@search.score": 0.37796447,

            "AUTOR": "Wagner Crivelini",

            "TITULO": "Trabalhando com transações",

            "TEMA": "A ideia de se trabalhar com transações foi uma grande invenção na história dos bancos de dados. Elas facilitam enormemente o controle de operações, além de garantirem a integridade dos dados. Resumindo: frequentemente a dupla COMMIT/ROLLBACK salva a pátria!"

        },

        {

            "@search.score": 0.37796447,

            "AUTOR": "Wagner Crivelini",

            "TITULO": "Construindo relatórios de referência cruzada no Microsoft SQL SERVER 2005 – Parte 2 : Estudo dos operadores PIVOT e ROLLUP",

            "TEMA": " No artigo anterior desta série, vimos os recursos básicos da linguagem T-SQL que são normalmente usados em consultas relacionadas a Business Intelligence (BI). Na verdade, as cláusulas, operadores e funções ali expostos devem fazer parte da “caixa de ferramentas” de qualquer desenvolvedor que use a linguagem SQL. Neste artigo vamos um pouco mais além. Vamos explorar, de fato, a geração de relatórios de referência cruzada (crosstab) baseados em linguagem SQL. Se você fizer uma consulta rápida no Google antes de ler este artigo, vai ver que existe uma unanimidade entre os AUTOR que escrevem sobre este tema: todos dizem que, se possível, você deve evitar usar linguagem SQL para tarefas como esta. "

        },

        {

            "@search.score": 0.37796447,

            "AUTOR": "Wagner Crivelini",

            "TITULO": "DesafioSQL #49 : operadores CUBE/ROLLUP",

            "TEMA": " Wagner Crivelini vem novamente nos brindar com mais um desafio. Desta vez, o velho fantasma da sumarização será confrontado e de uma maneira muito divertida, utilizando como exemplo o velho e empolgante jogo de “Super Trunfo”. E, novamente, a solução deste desafio usará o padrão ANSI, o que permite a implementação da mesma solução e vários SGBDs. E caso você tenha um desafio que gostaria de compartilhar conosco, envie sua sugestão para webeditor@sqlmagazine.com.br Divirtam-se."

        }

    ]

}

O resultado foi exatamente o esperado, deixando de fora documentos com palavras como “trollar” e “Rock’n’Roll”.

Filtrando documentos

A ideia de um filtro é praticamente oposta à ideia de busca. O filtro exclui documentos antes da busca ser executada, ou seja, estamos escolhendo um subgrupo da base de conhecimento para depois iniciarmos uma busca.

É possível filtrar documentos usando operadores lógicos (AND, OR , NOT), matemáticos (igual, maior, menor, etc) e outros.

Pode-se também combinar filtros e diferentes tipos de busca. No exemplo a seguir preparei uma pesquisa que mostra todas as minhas publicações que tratavam de NOSQL publicadas até o fim do ano de 2013.

Um detalhe importante: quando criei esta base de conhecimento, eu acabei carregando os documentos convertendo datas em números inteiros. Portanto, ao invés de filtrar pela data 31/12/2013, devo filtrar pelo número correspondente a esta data, que é 41639.

Body (resultado):

{

    "@odata.context": "https://wcrivelini-test1.search.windows.net/indexes('publications')/$metadata#docs",

    "@odata.count": 3,

    "value": [

        {

            "@search.score": 3.5645275,

            "TITULO": "Minhas Primeiras Impressões sobre NoSQL",

            "TEMA": "Minha experiência com bases de dados NoSQL é, para todos os efeitos práticos, zero. Meu primeiro contato com a ideia de uma base NoSQL foi em 2010, gravando um programa do DatabaseCast em que íamos entrevistar alguns especialistas. Passados três anos, eu continuo um leigo no assunto. Mas, durante esse tempo, o NoSQL ficou no meu radar. Leio, converso com gente que entende e procuro ir aprendendo aos poucos.",

            "DATA": 41576,

        },

        {

            "@search.score": 1.0664054,

            "TITULO": "DatabaseCast 03: NoSQL",

            "TEMA": "Nesta terceira edição do DatabaseCast os participantes deixam a consistência transacional de lado para falar sobre uma das novas tecnologias da área: NoSQL ou Not Only SQL.",

            "DATA": 40387,

        },

        {

            "@search.score": 0.14182188,

          "TITULO": "Respeite a normalização",

            "TEMA": "Existem vários tipos de aplicações que não precisam de normalização. Estão aí os modelos NOSQL, cada vez mais populares, que não me deixam mentir. Mas se você vai trabalhar no mundo transacional, faça um favor a você mesmo: tenha muito respeito pela normalização do seu banco de dados relacional. Normalização é uma das principais ferramentas para garantir que sua base trabalhe com dados de boa qualidade, ou seja: dados consistentes e sem redundância.",

            "DATA": 41374,

       }

    ]

}

Limitação do tamanho das pesquisas

A primeira questão de limite de tamanho a se considerar é o tamanho do próprio endpoint. Esta é uma questão simples de se resolver, bastando substituir o método GET (que uso nestes exemplos) pelo método POST.

Mas existem também os limites do próprio serviço de pesquisa. Estes limites são muito grandes e dificilmente são atingidos, mas eles existem. São dois:

  • Consultas devem usar no máximo 1024 cláusulas, incluindo AND, OR, NOT, etc
  • Os termos da consulta não podem exceder o limite de 32 Kb, ou seja, mais de 30 mil caracteres.
  • Dificilmente estes limites são atingidos, até porque a execução de uma única consulta tão extensa teria uma performance caótica e poderia até mesmo comprometer todo serviço de busca.

Comentários finais

Apresentei aqui exemplos elementares usando alguns recursos avançados de pesquisa do Azure Search. Para quaisquer fins práticos, apenas arranhei a superfície do mecanismo, já que se pode fazer buscas extremamente complexas.

De qualquer modo, em algumas ocasiões pode-se obter resultados de performance melhores caso seja possível executá-la apenas com recursos básicos, então vale a pena verificar o comportamento dos seus índices e buscas antes de implementá-los na sua aplicação.

No próximo artigo falarei sobre técnicas de apresentação e ranqueamento de resultados das buscas. Até lá!

Leituras sugeridas