Dados e bancos de dados
Apache Solr: índices invertidos, cadeias de análise e ajuste de relevância
Intermediário8 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
Um mecanismo de busca responde a uma pergunta diferente da de um banco de dados: não quais linhas correspondem, mas quais documentos são os mais relevantes. Este guia explica o índice invertido e a cadeia de análise que o alimenta, e depois aborda o schema do Solr, a indexação e os query parsers, o scoring BM25, facetas e destaque, as coleções do SolrCloud e o trabalho prático de ajustar a relevância.
O que você vai aprender
- Explicar por que um índice invertido responde a consultas de texto que um índice relacional não consegue responder
- Projetar uma cadeia de análise com tokenizers e filtros, e entender por que a análise na indexação e na consulta precisa ser compatível
- Definir tipos de campo no Solr, indexar documentos e consultá-los com edismax
- Usar facetas, destaque e sugestões para construir uma experiência de busca de verdade
- Ajustar a relevância com boosts de campo, boosts de frase e function queries, e rodar o Solr em cluster
Na primeira vez que um time troca uma consulta LIKE '%term%' por um mecanismo de busca, o que surpreende não é a velocidade. É que os resultados voltam em uma ordem útil. Um banco de dados responde a uma pergunta booleana — esta linha corresponde? — e o ranking fica por sua conta. Um mecanismo de busca responde a uma pergunta graduada: entre os documentos que correspondem, quais atendem melhor à consulta, considerando o quão raro é cada termo e quanto do documento ele ocupa?
O Apache Solr é um servidor de busca construído sobre o Apache Lucene e há duas décadas sustenta buscas em catálogos, sites e documentos. Este guia trata do Solr 9: o que o índice realmente contém, como o texto vira tokens, como definir um schema e consultá-lo, e como funciona o ajuste de relevância depois que o básico está rodando.
Por que um mecanismo de busca não é um índice de banco de dados
Um índice B-tree mapeia o valor inteiro de uma coluna para as linhas que o contêm. Um mecanismo de busca constrói um índice invertido: um dicionário de termos, cada um apontando para uma lista de postings com os documentos que contêm aquele termo, junto com posições e frequências.
Doc 1: "Estuary birds of northern Portugal"
Doc 3: "Bird migration over the estuary"
term postings (doc: positions, freq)
----------------------------------------------
bird 1:[2]/1 3:[1]/1
estuari 1:[1]/1 3:[5]/1
migrat 3:[2]/1
portug 1:[5]/1
Daí decorrem duas coisas. Buscar uma palavra é uma consulta a um dicionário mais uma fusão de listas, então continua rápido quer o corpus tenha dez mil documentos, quer dez milhões. E, como o índice registra quantas vezes e onde cada termo aparece, ele consegue pontuar as correspondências em vez de apenas encontrá-las.
O modelo de scoring padrão é o BM25. Em termos simples, um documento pontua mais quando o termo da consulta aparece mais vezes nele (com retornos fortemente decrescentes), quando o termo é raro no corpus inteiro e quando o campo que o contém é curto — um termo em um título de cinco palavras vale mais do que o mesmo termo perdido em um corpo de mil palavras. O Lucene usa o BM25 como similaridade padrão desde o Solr 6; o antigo modelo TF-IDF continua disponível por compatibilidade.
O trade-off é real. O Solr não tem transações, nem joins dignos do nome, nem atualização in-place; mudar um único campo reescreve o documento inteiro. Trate-o como uma projeção derivada de um sistema de registro, nunca como o registro em si — esse papel continua com o banco relacional, com os hábitos de modelagem e de indexação abordados no guia de planos de execução.
A cadeia de análise
O texto bruto vira termos de índice passando por um analisador, composto por uma cadeia de filtros de caracteres, um tokenizer e uma cadeia de filtros de tokens. Acertar essa parte é a maior parte do trabalho de uma boa experiência de busca.
- Os filtros de caracteres atuam sobre a string bruta: removem HTML, mapeiam
œparaoe, normalizam a pontuação. - O tokenizer divide o fluxo em tokens. O
StandardTokenizersegue os limites de palavra do Unicode; oWhitespaceTokenizerquebra apenas nos espaços; oClassicTokenizerpreserva coisas como códigos de produto e hostnames. - Os filtros de tokens transformam o fluxo: passam para minúsculas, removem stop words, aplicam sinônimos, removem acentos (
ASCIIFoldingFilter), quebramWiFi-2.4GHzem pedaços (WordDelimiterGraphFilter) e reduzem as palavras ao radical (SnowballPorterFilterpara vários idiomas, ou o mais conservadorKStemFilterpara inglês).
A regra que mais causa confusão: a análise na indexação e a análise na consulta precisam ser compatíveis. Se o índice reduz birds a bird, mas a consulta não, nada corresponde. O Solr permite declarar as duas cadeias separadamente, e por bons motivos: a expansão de sinônimos e a geração de EdgeNGram costumam ser aplicadas só na indexação, para que a consulta continue sendo um único termo. A tela Analysis da interface de administração mostra os dois fluxos de tokens lado a lado e responde a "por que isso não bate?" mais rápido do que qualquer raciocínio.
Schema e tipos de campo
Um schema do Solr declara tipos de campo (uma cadeia de análise mais opções de armazenamento) e os campos que os usam. O Solr 9 usa um managed schema editável pela Schema API, embora a forma XML seja a que deixa a estrutura mais clara:
<fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.ASCIIFoldingFilterFactory"/>
<filter class="solr.SynonymGraphFilterFactory" synonyms="synonyms.txt" expand="true"/>
<filter class="solr.KStemFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.ASCIIFoldingFilterFactory"/>
<filter class="solr.KStemFilterFactory"/>
</analyzer>
</fieldType>
<field name="id" type="string" indexed="true" stored="true" required="true"/>
<field name="title" type="text_en" indexed="true" stored="true"/>
<field name="body" type="text_en" indexed="true" stored="false"/>
<field name="category" type="string" indexed="true" stored="true" docValues="true"/>
<field name="price" type="pdouble" indexed="true" stored="true" docValues="true"/>
<field name="published" type="pdate" indexed="true" stored="true" docValues="true"/>
Quatro atributos carregam o significado. indexed torna um campo pesquisável. stored permite devolver o valor original. docValues constrói uma estrutura orientada a colunas usada para ordenação, facetas e function queries — ative-o em tudo o que você faceta ou ordena, e aí pode deixar stored desligado nos textos grandes que nunca exibe. E o tipo string não é analisado: a correspondência é exata — justamente o que você quer para identificadores, códigos de categoria e valores de faceta, e justamente o que você não quer para texto corrido.
Campos pelos quais você filtra de forma estrutural devem ser string; campos em que pessoas digitam palavras devem ser um TextField. Errar essa distinção é o erro de schema mais comum.
Indexação e consulta
Os documentos são enviados como JSON via HTTP, um a um ou em lotes. Use lotes, sempre — commits por documento são a causa clássica de um indexador lento.
curl -X POST 'http://localhost:8983/solr/catalogue/update?commitWithin=10000' \
-H 'Content-Type: application/json' --data-binary '[
{ "id": "BK-1042", "title": "Field Guide to Estuaries",
"body": "A practical guide to birds, tides and salt marshes.",
"category": "reference", "price": 24.90, "published": "2026-02-11T00:00:00Z" }
]'
Use commitWithin (ou as configurações de autoCommit no solrconfig.xml) em vez de fazer commits explícitos: um hard commit grava os segmentos em disco, um soft commit torna os documentos visíveis para os searchers, e disparar qualquer um dos dois a cada documento destrói a vazão. Busca near real-time significa um soft commit a cada um ou dois segundos, não a cada escrita.
As consultas vão para o handler select. O parser edismax é o padrão certo para texto digitado pelo usuário: ele nunca lança erro de sintaxe por causa de uma pontuação perdida e distribui a consulta por vários campos com pesos independentes.
GET /solr/catalogue/select?defType=edismax
&q=estuary birds
&qf=title^4 body^1 category^2
&pf=title^8
&mm=2<-1
&fq=category:reference
&fq=price:[* TO 30]
&sort=score desc, published desc
&rows=20&fl=id,title,price,score
Cada parâmetro tem sua razão de estar ali. qf lista os campos a pesquisar e seus boosts. pf favorece documentos em que os termos da consulta aparecem como frase, e é assim que "estuary birds" vence um documento que cita as duas palavras em parágrafos sem relação. mm (minimum should match) controla quantos termos precisam estar presentes — 2<-1 significa "com dois termos, exija os dois; acima disso, permita que falte um". As filter queries fq são booleanas, não afetam o score e são cacheadas separadamente, então as restrições estruturais ficam ali, e não em q. Adicionar debugQuery=true retorna a consulta interpretada e uma explicação completa do score de cada documento; é, de longe, o diagnóstico mais útil que o Solr oferece.
Facetas, destaque e sugestões
Uma caixa de busca sozinha não é uma experiência de busca. Três recursos transformam resultados em navegação:
Facetas contam os documentos correspondentes por valor e alimentam a barra lateral de filtros. A JSON Facet API lida com aninhamento e estatísticas que os parâmetros antigos não conseguem:
{
"query": "estuary birds",
"filter": ["category:reference"],
"facet": {
"by_category": { "type": "terms", "field": "category", "limit": 10,
"facet": { "avg_price": "avg(price)" } },
"price_bands": { "type": "range", "field": "price", "start": 0, "end": 100, "gap": 20 }
}
}
Destaque (highlighting) devolve trechos com os termos encontrados marcados, para que o usuário veja por que um resultado correspondeu. O highlighter unified é o padrão atual e funciona melhor com storeOffsetsWithPositions ativado no campo.
Sugestões e ortografia. O componente suggester oferece autocompletar a partir de um dicionário construído sobre um campo; o componente spellcheck sugere correções do tipo "você quis dizer" a partir dos próprios termos do índice. Os dois precisam ser configurados no solrconfig.xml e reconstruídos conforme o conteúdo muda.
Rodando o Solr em cluster
O SolrCloud é o modo distribuído do Solr. Uma coleção é um índice lógico dividido em shards; cada shard tem uma ou mais réplicas espalhadas por nós diferentes, e uma réplica por shard é eleita líder e recebe o tráfego de indexação. O estado do cluster e a configuração ficam no Apache ZooKeeper, que também cuida da eleição de líder. A configuração é enviada como um configset, de modo que todos os nós rodam o mesmo schema e o mesmo solrconfig.xml.
# Upload a configset, then create a sharded, replicated collection
solr zk upconfig -n catalogue -d /opt/configsets/catalogue -z zk1:2181
curl 'http://localhost:8983/solr/admin/collections?action=CREATE&name=catalogue&numShards=2&replicationFactor=2&collection.configName=catalogue'
O dimensionamento segue algumas regras confiáveis. Faça sharding por tamanho de índice e vazão de indexação, e replique por vazão de consultas e disponibilidade; uma consulta é distribuída para todos os shards e os resultados são mesclados, então shards além do necessário acrescentam latência em vez de reduzi-la. Mantenha o heap da JVM moderado e deixe bastante RAM para o page cache do sistema operacional, porque o Lucene depende de arquivos mapeados em memória. Rodar um Solr de nó único em um contêiner serve bem para desenvolvimento — veja os fundamentos de Docker —, mas produção exige pelo menos três nós de ZooKeeper.
Ajustando a relevância
O trabalho de relevância é empírico. Comece com um conjunto de consultas reais cujos bons resultados já foram combinados, mude uma única coisa e meça de novo.
- Boosts de campo (
qf) expressam que uma correspondência no título vale mais do que uma no corpo. Mantenha as proporções modestas; boosts extremos tornam todo o resto irrelevante. - Boosts de frase (
pf,pf2,pf3) recompensam a proximidade e costumam ser a mudança isolada de maior retorno. - Function queries incorporam ao score sinais não textuais:
boost=recip(ms(NOW,published),3.16e-11,1,1)favorece documentos recentes,bf=sqrt(popularity)favorece os populares. Prefira oboostmultiplicativo aobfaditivo, para que um sinal forte não consiga soterrar o score textual. - Sinônimos e stop words corrigem o descompasso de vocabulário entre como você escreve e como os usuários buscam. Mantenha o arquivo de sinônimos como um ativo do produto — é ali que mora o conhecimento do domínio.
- Learning to rank reordena os N primeiros resultados com um modelo treinado. Só vale a pena depois de esgotar o básico e quando você tem dados de cliques.
O Solr não é a ferramenta certa para todo problema com cara de busca. Grandes volumes de dados gerados por máquinas, com retenção baseada em tempo, pertencem a uma plataforma de logs — veja gerenciamento centralizado de logs com o Elastic Stack — e uma simples busca por palavra-chave em documentos que você já armazena pode ser resolvida pelos índices de texto do MongoDB, abordados no guia de MongoDB.
Principais pontos
- Um índice invertido mapeia termos para listas de postings com frequências e posições, e é isso que torna possível a recuperação ranqueada; o BM25 recompensa a frequência do termo, a raridade do termo e campos curtos.
- A cadeia de análise (filtros de caracteres, tokenizer, filtros de tokens) define o que é pesquisável, e as cadeias de indexação e de consulta precisam produzir tokens compatíveis.
- Use
stringpara valores estruturais exatos e umTextFieldpara texto corrido; ativedocValuesem tudo o que você ordena, faceta ou usa em uma função. - Indexe em lotes com
commitWithine soft commits; hard commits por documento são o erro de desempenho clássico. - Consulte a entrada do usuário com edismax, coloque as restrições estruturais em filtros
fqcacheados e diagnostique o scoring comdebugQuery=true. - Ajuste a relevância com boosts de campo e de frase e function queries multiplicativas, medindo contra um conjunto fixo de consultas avaliadas, e não pela intuição.
Fontes e leituras complementares
Perguntas frequentes
- Posso usar a busca full text do meu banco de dados em vez do Solr?
- Para uma caixa de busca sobre algumas centenas de milhares de linhas, com necessidades modestas de relevância, a busca full text do PostgreSQL ou os índices de texto do MongoDB dão conta perfeitamente e poupam você de operar mais um serviço. Parta para o Solr quando precisar de análise configurável por idioma, navegação por facetas, destaque, sugestões, ajuste de relevância com boosts de campo e de frase, ou de um volume de consultas que atrapalharia seu banco transacional.
- Devo usar o Solr como meu armazenamento de dados principal?
- Não. O Solr não tem transações, nem chaves estrangeiras, nem atualização in-place — uma atualização reescreve o documento inteiro — e reindexar depois de uma mudança de schema é uma operação rotineira que pressupõe que os dados existem em outro lugar. Mantenha o sistema de registro em um banco de dados e trate o índice do Solr como uma projeção derivada dele, que pode ser reconstruída.
- Por que minha frase exata não encontra nada?
- Quase sempre é um descompasso de análise. As cadeias de análise da indexação e da consulta precisam produzir tokens comparáveis, então um stemmer, um filtro de minúsculas ou um filtro de sinônimos aplicado em só um dos lados quebra a correspondência sem nenhum aviso. Abra a tela Analysis na interface de administração do Solr, cole o valor indexado e a consulta, e compare os fluxos de tokens etapa por etapa.
- Qual a diferença entre Solr e Elasticsearch?
- Os dois são construídos sobre o Apache Lucene e compartilham a mesma estrutura de índice, os mesmos conceitos de análise e o scoring BM25, então o comportamento de busca é, de modo geral, comparável. Eles diferem nas APIs, no clustering (SolrCloud baseado em ZooKeeper versus uma camada de coordenação embutida), no licenciamento e no ecossistema. O Solr continua sob licença Apache e é forte em busca clássica de sites e catálogos; o Elasticsearch domina as cargas de logs e observabilidade.