Datos y bases de datos
Apache Solr: índices invertidos, cadenas de análisis y ajuste de relevancia
Intermedio8 min de lecturaActualizado
Esta página se tradujo del inglés con ayuda de IA y se cotejó con el original. Leer la versión en inglés
Un motor de búsqueda responde a una pregunta distinta de la de una base de datos: no qué filas coinciden, sino qué documentos son los más relevantes. Esta guía explica el índice invertido y la cadena de análisis que lo alimenta, y después recorre el esquema de Solr, la indexación y los parsers de consultas, el scoring BM25, las facetas y el resaltado, las colecciones de SolrCloud y el trabajo práctico de ajustar la relevancia.
Lo que aprenderás
- Explicar por qué un índice invertido resuelve consultas de texto que un índice relacional no puede resolver
- Diseñar una cadena de análisis con tokenizers y filtros, y entender por qué el análisis de indexación y el de consulta deben coincidir
- Definir tipos de campo en Solr, indexar documentos y consultarlos con edismax
- Usar facetas, resaltado y sugerencias para construir una experiencia de búsqueda real
- Ajustar la relevancia con boosts de campo, boosts de frase y function queries, y ejecutar Solr en clúster
La primera vez que un equipo sustituye una consulta LIKE '%term%' por un motor de búsqueda, lo que sorprende no es la velocidad. Es que los resultados llegan en un orden útil. Una base de datos responde a una pregunta booleana —¿esta fila coincide?— y cualquier ranking te lo tienes que inventar tú. Un motor de búsqueda responde a una pregunta graduada: de los documentos que coinciden, ¿cuáles satisfacen mejor la consulta, teniendo en cuenta lo raro que es cada término y cuánto espacio ocupa en el documento?
Apache Solr es un servidor de búsqueda construido sobre Apache Lucene, y lleva dos décadas impulsando búsquedas de catálogos, sitios y documentos. Esta guía cubre Solr 9: qué contiene realmente el índice, cómo el texto se convierte en tokens, cómo definir un esquema y consultarlo, y cómo funciona el ajuste de la relevancia una vez que lo básico está en marcha.
Por qué un motor de búsqueda no es un índice de base de datos
Un índice B-tree asocia el valor completo de una columna con las filas que lo contienen. Un motor de búsqueda construye un índice invertido: un diccionario de términos, cada uno apuntando a una lista de postings con los documentos que contienen ese término, junto con posiciones y frecuencias.
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
De aquí se derivan dos cosas. Buscar una palabra consiste en consultar un diccionario y fusionar listas, así que sigue siendo rápido tanto si el corpus tiene diez mil documentos como diez millones. Y como el índice registra cuántas veces y dónde aparece cada término, puede puntuar las coincidencias en lugar de limitarse a encontrarlas.
El modelo de scoring por defecto es BM25. Dicho de forma sencilla, un documento puntúa más cuando el término de la consulta aparece más veces en él (con rendimientos claramente decrecientes), cuando el término es poco frecuente en todo el corpus y cuando el campo que lo contiene es corto: un término en un título de cinco palabras pesa más que el mismo término perdido en un cuerpo de mil palabras. Lucene usa BM25 como similitud por defecto desde Solr 6; el antiguo modelo TF-IDF sigue disponible por compatibilidad.
La contrapartida es real. Solr no tiene transacciones, ni joins dignos de ese nombre, ni actualización in situ; cambiar un solo campo reescribe el documento entero. Trátalo como una proyección derivada de un sistema de referencia, nunca como la referencia en sí: ese papel le corresponde al motor relacional, con los hábitos de diseño e indexación que se explican en la guía de planes de ejecución.
La cadena de análisis
El texto en bruto se convierte en términos de índice a través de un analizador, formado por una cadena de filtros de caracteres, un tokenizer y una cadena de filtros de tokens. Acertar aquí es la mayor parte del trabajo de una buena experiencia de búsqueda.
- Los filtros de caracteres actúan sobre la cadena en bruto: eliminan HTML, convierten
œenoe, normalizan la puntuación. - El tokenizer divide el flujo en tokens.
StandardTokenizersigue los límites de palabra de Unicode;WhitespaceTokenizersolo corta por espacios;ClassicTokenizerconserva cosas como códigos de producto y nombres de host. - Los filtros de tokens transforman el flujo: pasan a minúsculas, eliminan stop words, aplican sinónimos, quitan acentos (
ASCIIFoldingFilter), troceanWiFi-2.4GHz(WordDelimiterGraphFilter) y reducen las palabras a su raíz (SnowballPorterFilterpara muchos idiomas, o el más conservadorKStemFilterpara inglés).
La regla que más confusión genera: el análisis de indexación y el de consulta deben ser compatibles. Si el índice reduce birds a bird pero la consulta no, no coincide nada. Solr permite declarar las dos cadenas por separado, y con razón: la expansión de sinónimos y la generación de EdgeNGram suelen aplicarse solo al indexar, para que la consulta siga siendo un único término. La pantalla Analysis de la consola de administración muestra ambos flujos de tokens uno al lado del otro y responde a «¿por qué no coincide?» más rápido que cualquier razonamiento.
Esquema y tipos de campo
Un esquema de Solr declara tipos de campo (una cadena de análisis más opciones de almacenamiento) y los campos que los usan. Solr 9 utiliza un esquema gestionado que se edita mediante la Schema API, aunque la forma XML es la que deja más clara la estructura:
<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"/>
Cuatro atributos concentran el significado. indexed hace que un campo sea buscable. stored permite devolver su valor original. docValues construye una estructura orientada a columnas que se usa para ordenar, facetar y en function queries: actívalo en todo lo que facetes u ordenes, y así podrás desactivar stored en los textos grandes que nunca muestras. Y el tipo string no se analiza: coincide de forma exacta, que es justo lo que quieres para identificadores, códigos de categoría y valores de faceta, y justo lo que no quieres para texto en prosa.
Los campos por los que filtras de forma estructural deben ser string; los campos en los que las personas escriben palabras deben ser un TextField. Equivocarse en esta distinción es el error de esquema más habitual.
Indexar y consultar
Los documentos se envían como JSON por HTTP, de uno en uno o por lotes. Usa lotes, siempre: los commits por documento son la causa clásica de un 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" }
]'
Usa commitWithin (o la configuración de autoCommit en solrconfig.xml) en lugar de hacer commits explícitos: un hard commit vuelca los segmentos a disco, un soft commit hace visibles los documentos para los searchers, y lanzar cualquiera de los dos por cada documento hunde el rendimiento. La búsqueda casi en tiempo real significa un soft commit cada uno o dos segundos, no en cada escritura.
Las consultas van al handler select. El parser edismax es la opción por defecto adecuada para lo que escribe el usuario: nunca lanza un error de sintaxis por una puntuación suelta y reparte la consulta entre varios campos con pesos independientes.
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 se gana su sitio. qf enumera los campos en los que buscar y sus boosts. pf premia los documentos en los que los términos de la consulta aparecen como frase, y así «estuary birds» supera a un documento que menciona ambas palabras en párrafos sin relación. mm (minimum should match) controla cuántos términos deben estar presentes: 2<-1 significa «con dos términos, exige ambos; a partir de ahí, permite que falte uno». Las filter queries fq son booleanas, no afectan al score y se cachean por separado, así que las restricciones estructurales van ahí y no en q. Añadir debugQuery=true devuelve la consulta interpretada y una explicación completa del score de cada documento; es, con diferencia, el diagnóstico más útil que ofrece Solr.
Facetas, resaltado y sugerencias
Una caja de búsqueda por sí sola no es una experiencia de búsqueda. Tres funcionalidades convierten los resultados en navegación:
Las facetas cuentan los documentos coincidentes por valor y alimentan la barra lateral de filtros. La JSON Facet API maneja anidamiento y estadísticas que los parámetros antiguos no permiten:
{
"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 }
}
}
El resaltado devuelve fragmentos con los términos encontrados marcados, para que el usuario vea por qué coincidió un resultado. El highlighter unified es el predeterminado actual y funciona mejor con storeOffsetsWithPositions activado en el campo.
Sugerencias y ortografía. El componente suggester ofrece autocompletado a partir de un diccionario construido sobre un campo; el componente spellcheck propone correcciones del tipo «quizá quisiste decir» a partir de los propios términos del índice. Ambos se configuran en solrconfig.xml y hay que reconstruirlos a medida que cambia el contenido.
Ejecutar Solr en clúster
SolrCloud es el modo distribuido de Solr. Una colección es un índice lógico dividido en shards; cada shard tiene una o más réplicas repartidas en nodos distintos, y una réplica por shard se elige líder y recibe el tráfico de indexación. El estado del clúster y la configuración residen en Apache ZooKeeper, que también se encarga de elegir al líder. La configuración se sube como un configset, de modo que todos los nodos ejecutan el mismo esquema y el mismo 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'
El dimensionamiento sigue unas pocas reglas fiables. Se hace sharding por tamaño de índice y rendimiento de indexación, y se replica por rendimiento de consultas y disponibilidad; una consulta se distribuye a todos los shards y luego se fusionan los resultados, así que tener más shards de los necesarios añade latencia en lugar de quitarla. Mantén un heap de la JVM moderado y deja mucha RAM para la caché de páginas del sistema operativo, porque Lucene se apoya en ficheros mapeados en memoria. Un Solr de un solo nodo en un contenedor sirve para desarrollo —consulta los fundamentos de Docker—, pero en producción hacen falta al menos tres nodos de ZooKeeper.
Ajustar la relevancia
El trabajo de relevancia es empírico. Parte de un conjunto de consultas reales cuyos buenos resultados ya estén acordados, cambia una sola cosa y vuelve a medir.
- Los boosts de campo (
qf) expresan que una coincidencia en el título vale más que una en el cuerpo. Mantén proporciones moderadas; los boosts extremos vuelven irrelevante todo lo demás. - Los boosts de frase (
pf,pf2,pf3) premian la proximidad y suelen ser el cambio individual que más aporta. - Las function queries incorporan al score señales no textuales:
boost=recip(ms(NOW,published),3.16e-11,1,1)favorece los documentos recientes,bf=sqrt(popularity)los populares. Prefiere elboostmultiplicativo albfaditivo para que una señal fuerte no pueda aplastar el score textual. - Los sinónimos y las stop words corrigen el desajuste de vocabulario entre cómo escribes tú y cómo buscan los usuarios. Mantén el fichero de sinónimos como un activo del producto: es donde vive el conocimiento del dominio.
- El learning to rank reordena los N primeros resultados con un modelo entrenado. Solo compensa cuando ya has agotado lo básico y dispones de datos de clics.
Solr no es la herramienta adecuada para todos los problemas con forma de búsqueda. Los grandes volúmenes de datos generados por máquinas con retención basada en el tiempo pertenecen a una plataforma de logs —consulta la gestión centralizada de logs con el stack de Elastic—, y una simple búsqueda por palabra clave dentro de documentos que ya almacenas puede resolverse con los índices de texto de MongoDB, tratados en la guía de MongoDB.
Puntos clave
- Un índice invertido asocia términos con listas de postings que incluyen frecuencias y posiciones, y eso es lo que hace posible la recuperación ordenada por relevancia; BM25 premia la frecuencia del término, su rareza y los campos cortos.
- La cadena de análisis (filtros de caracteres, tokenizer, filtros de tokens) define qué se puede buscar, y las cadenas de indexación y de consulta deben producir tokens compatibles.
- Usa
stringpara valores estructurales exactos y unTextFieldpara la prosa; activadocValuesen todo lo que ordenes, facetes o uses en una función. - Indexa por lotes con
commitWithiny soft commits; los hard commits por documento son el error de rendimiento típico. - Consulta lo que escribe el usuario con edismax, coloca las restricciones estructurales en filtros
fqcacheados y diagnostica el scoring condebugQuery=true. - Ajusta la relevancia con boosts de campo y de frase y function queries multiplicativas, midiendo contra un conjunto fijo de consultas evaluadas y no por intuición.
Fuentes y lecturas recomendadas
Preguntas frecuentes
- ¿Puedo usar la búsqueda full text de mi base de datos en lugar de Solr?
- Para una caja de búsqueda sobre unos cientos de miles de filas y con necesidades de relevancia modestas, la búsqueda full text de PostgreSQL o los índices de texto de MongoDB son más que suficientes y te ahorran un servicio que operar. Pasa a Solr cuando necesites análisis configurable por idioma, navegación por facetas, resaltado, sugerencias, ajuste de relevancia con boosts de campo y de frase, o un volumen de consultas que afectaría a tu base de datos transaccional.
- ¿Debería usar Solr como almacén de datos principal?
- No. Solr no tiene transacciones, ni claves foráneas, ni actualización in situ —una actualización reescribe el documento entero—, y reindexar tras un cambio de esquema es una operación rutinaria que da por hecho que los datos existen en otro sitio. Mantén el sistema de referencia en una base de datos y trata el índice de Solr como una proyección derivada de ella, que se puede reconstruir.
- ¿Por qué mi frase exacta no encuentra nada?
- Casi siempre se debe a un desajuste de análisis. Las cadenas de análisis de indexación y de consulta tienen que producir tokens comparables, así que un stemmer, un filtro de minúsculas o un filtro de sinónimos aplicado solo en un lado rompe la coincidencia sin avisar. Abre la pantalla Analysis de la consola de administración de Solr, pega el valor indexado y la consulta, y compara los flujos de tokens paso a paso.
- ¿En qué se diferencia Solr de Elasticsearch?
- Ambos se basan en Apache Lucene y comparten la misma estructura de índice, los mismos conceptos de análisis y el scoring BM25, así que su comportamiento de búsqueda es, en líneas generales, comparable. Difieren en las API, en el clustering (SolrCloud basado en ZooKeeper frente a una capa de coordinación integrada), en la licencia y en el ecosistema. Solr sigue con licencia Apache y es fuerte en la búsqueda clásica de sitios y catálogos; Elasticsearch domina las cargas de logs y observabilidad.