Apache Solr es un motor de búsqueda de código abierto construido sobre Apache Lucene. Ofrece búsqueda de texto completo, filtros, facetas, resaltado y búsqueda geoespacial a través de una API HTTP. En este tutorial instalarás Solr 10 como servicio systemd en Ubuntu 24.04, crearás una colección de productos con un esquema definido por ti, indexarás documentos JSON y ejecutarás consultas con relevancia ponderada y facetas.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Al menos 2 GB de RAM (4 GB o más si vas a indexar volúmenes grandes) y 10 GB libres en disco.
  • curl y wget, que vienen instalados por defecto en Ubuntu 24.04.

Solr 10 requiere Java 21 o superior. En esta guía Solr solo escucha en 127.0.0.1, así que no hace falta abrir ningún puerto: accederás al panel web mediante un túnel SSH.

Paso 1: Instalar Java 21

Ubuntu 24.04 incluye OpenJDK 21 en sus repositorios. Basta con el entorno de ejecución sin interfaz gráfica:

sudo apt update
sudo apt install openjdk-21-jre-headless

Comprueba la versión instalada:

java -version
openjdk version "21.0.8" 2025-07-15
OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing)

El número de parche puede ser distinto; lo importante es que empiece por 21.

Paso 2: Descargar y verificar Solr

Descarga la última versión estable desde la CDN de Apache y su suma de comprobación desde el servidor principal. En el momento de escribir esta guía es la 10.0.0; consulta solr.apache.org/downloads.html y ajusta la variable si hay una más reciente:

SOLR_VERSION=10.0.0
cd /tmp
wget "https://dlcdn.apache.org/solr/solr/${SOLR_VERSION}/solr-${SOLR_VERSION}.tgz"
wget "https://downloads.apache.org/solr/solr/${SOLR_VERSION}/solr-${SOLR_VERSION}.tgz.sha512"

Verifica que el archivo no está corrupto:

sha512sum -c "solr-${SOLR_VERSION}.tgz.sha512"
solr-10.0.0.tgz: OK

Paso 3: Instalar Solr como servicio

El paquete incluye install_solr_service.sh, el instalador oficial para Linux. Crea el usuario solr, extrae el programa en /opt/solr-10.0.0 (con el enlace /opt/solr), guarda datos y logs en /var/solr e instala una unidad systemd. Extrae solo ese script:

tar xzf "solr-${SOLR_VERSION}.tgz" "solr-${SOLR_VERSION}/bin/install_solr_service.sh" --strip-components=2

Ejecútalo pasándole el archivo descargado:

sudo bash ./install_solr_service.sh "solr-${SOLR_VERSION}.tgz"

El script habilita e inicia el servicio al terminar. Comprueba su estado:

sudo systemctl status solr
● solr.service - Apache Solr
     Loaded: loaded (/etc/systemd/system/solr.service; enabled; preset: enabled)
     Active: active (running) since ...

Por defecto Solr 10 arranca en modo SolrCloud con un ZooKeeper embebido, lo que permite usar colecciones y la API de colecciones aunque solo tengas un nodo.

Paso 4: Ajustar memoria y red

La configuración de arranque está en /etc/default/solr.in.sh. Ábrela:

sudo nano /etc/default/solr.in.sh

Busca la línea #SOLR_HEAP="512m", descoméntala y asigna aproximadamente la mitad de la RAM disponible al heap de Java (el resto lo usa el sistema como caché de disco para los índices). Para un servidor de 4 GB:

SOLR_HEAP="2g"

Deja SOLR_HOST_BIND comentado: su valor por defecto es 127.0.0.1, de modo que Solr no queda expuesto a Internet. Guarda el archivo y reinicia el servicio:

sudo systemctl restart solr

Comprueba que responde y que la versión es la esperada:

curl -s "http://127.0.0.1:8983/solr/admin/info/system" | python3 -m json.tool | grep solr-spec-version
        "solr-spec-version": "10.0.0",

Para abrir el panel de administración desde tu equipo, crea un túnel SSH sustituyendo your_user y your_server_ip:

ssh -L 8983:127.0.0.1:8983 your_user@your_server_ip

Con el túnel abierto, visita http://localhost:8983/solr/ en tu navegador.

Paso 5: Crear una colección

Una colección es un índice lógico con su propio esquema y configuración. Crea una llamada productos a partir del conjunto de configuración _default:

sudo -u solr /opt/solr/bin/solr create -c productos
WARNING: Using _default configset. Data driven schema functionality is enabled by default, which is
         NOT RECOMMENDED for production use.
...
Created collection 'productos' with 1 shard(s), 1 replica(s) with config-set 'productos'

La advertencia se refiere al modo sin esquema: Solr crea campos automáticamente según lo que recibe, y a menudo adivina mal el tipo. Desactívalo para que solo se acepten los campos que tú definas:

curl -s http://127.0.0.1:8983/solr/productos/config \
  -H 'Content-Type: application/json' \
  -d '{"set-user-property": {"update.autoCreateFields": "false"}}'

La respuesta incluye "status":0 si el cambio se aplicó.

Paso 6: Definir el esquema

Añade los campos con la Schema API. text_es aplica análisis en español (minúsculas, palabras vacías y stemming), mientras que string guarda el valor exacto, ideal para filtros y facetas. docValues acelera la ordenación y las facetas:

curl -s -X POST http://127.0.0.1:8983/solr/productos/schema \
  -H 'Content-Type: application/json' \
  -d '{
    "add-field": [
      {"name": "nombre",      "type": "text_es", "stored": true},
      {"name": "descripcion", "type": "text_es", "stored": true},
      {"name": "categoria",   "type": "string",  "stored": true, "docValues": true},
      {"name": "marca",       "type": "string",  "stored": true, "docValues": true},
      {"name": "precio",      "type": "pfloat",  "stored": true, "docValues": true},
      {"name": "stock",       "type": "pint",    "stored": true, "docValues": true},
      {"name": "activo",      "type": "boolean", "stored": true},
      {"name": "etiquetas",   "type": "string",  "stored": true, "docValues": true, "multiValued": true}
    ]
  }'

Comprueba que el campo precio existe con el tipo correcto:

curl -s http://127.0.0.1:8983/solr/productos/schema/fields/precio
{
  "responseHeader":{
    "status":0,
    "QTime":1
  },
  "field":{
    "name":"precio",
    "type":"pfloat",
    "docValues":true,
    "stored":true
  }
}

Estos son los tipos que más vas a usar del conjunto _default:

TipoUso
text_generalTexto con análisis genérico, sin reglas de idioma
text_esTexto en español con stemming y palabras vacías
stringValor exacto para filtros, facetas y ordenación
pint, plong, pfloat, pdoubleNúmeros
pdateFechas en formato ISO 8601 (2026-01-15T00:00:00Z)
booleanVerdadero o falso
locationCoordenadas latitud,longitud para búsqueda geoespacial

Paso 7: Indexar documentos

Envía varios productos en formato JSON. El parámetro commit=true hace que sean visibles de inmediato; en cargas masivas es mejor omitirlo y dejar que Solr confirme los cambios de forma periódica:

curl -s -X POST "http://127.0.0.1:8983/solr/productos/update?commit=true" \
  -H 'Content-Type: application/json' \
  -d '[
    {"id": "prod-001", "nombre": "Camiseta básica de algodón", "descripcion": "Camiseta unisex de algodón 100% en varios colores",
     "categoria": "Ropa", "marca": "BasicWear", "precio": 19.99, "stock": 150, "activo": true, "etiquetas": ["casual", "algodón"]},
    {"id": "prod-002", "nombre": "Zapatillas de running", "descripcion": "Zapatillas ligeras para correr con amortiguación",
     "categoria": "Calzado", "marca": "SpeedRun", "precio": 89.99, "stock": 45, "activo": true, "etiquetas": ["deporte", "running"]},
    {"id": "prod-003", "nombre": "Zapatillas de montaña", "descripcion": "Calzado resistente para senderismo y trail",
     "categoria": "Calzado", "marca": "TrailPro", "precio": 119.00, "stock": 12, "activo": true, "etiquetas": ["deporte", "montaña"]}
  ]'

Comprueba cuántos documentos hay en el índice:

curl -s "http://127.0.0.1:8983/solr/productos/select?q=*:*&rows=0"
{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":2,
    "params":{
      "q":"*:*",
      "rows":"0"
    }
  },
  "response":{
    "numFound":3,
    "start":0,
    "numFoundExact":true,
    "docs":[ ]
  }
}

Para cambiar solo algunos campos de un documento sin reenviarlo entero, usa una actualización atómica. Este ejemplo fija un nuevo precio y suma 50 unidades al stock:

curl -s -X POST "http://127.0.0.1:8983/solr/productos/update?commit=true" \
  -H 'Content-Type: application/json' \
  -d '[{"id": "prod-001", "precio": {"set": 17.99}, "stock": {"inc": 50}}]'

Para borrar un documento, envía su id:

curl -s -X POST "http://127.0.0.1:8983/solr/productos/update?commit=true" \
  -H 'Content-Type: application/json' \
  -d '{"delete": {"id": "prod-003"}}'

Paso 8: Buscar con relevancia, filtros y facetas

El analizador de consultas edismax busca en varios campos a la vez y permite dar más peso a unos que a otros. Aquí una coincidencia en nombre cuenta el triple que en descripcion, y los filtros fq limitan el resultado sin afectar a la puntuación. La opción -G hace que curl envíe los parámetros codificados en la URL:

curl -s -G "http://127.0.0.1:8983/solr/productos/select" \
  --data-urlencode "q=zapatillas correr" \
  --data-urlencode "defType=edismax" \
  --data-urlencode "qf=nombre^3 descripcion etiquetas" \
  --data-urlencode "fq=activo:true" \
  --data-urlencode "fq=precio:[0 TO 100]" \
  --data-urlencode "fl=id,nombre,precio,score"
  "response":{
    "numFound":1,
    "start":0,
    "maxScore":1.4208,
    "numFoundExact":true,
    "docs":[{
      "id":"prod-002",
      "nombre":"Zapatillas de running",
      "precio":89.99,
      "score":1.4208
    }]
  }

El documento aparece porque "zapatillas" coincide en nombre (el campo con más peso) y "correr" en descripcion. Con el operador por defecto (OR) basta con que coincida uno de los términos, pero los documentos que contienen ambos obtienen más puntuación.

Las facetas devuelven cuántos documentos hay por cada valor de un campo, que es lo que alimenta los filtros laterales de una tienda online. Esta consulta cuenta productos por categoría y por marca, y agrupa los precios en tramos de 50:

curl -s -G "http://127.0.0.1:8983/solr/productos/select" \
  --data-urlencode "q=*:*" \
  --data-urlencode "rows=0" \
  --data-urlencode "facet=true" \
  --data-urlencode "facet.field=categoria" \
  --data-urlencode "facet.field=marca" \
  --data-urlencode "facet.mincount=1" \
  --data-urlencode "facet.range=precio" \
  --data-urlencode "facet.range.start=0" \
  --data-urlencode "facet.range.end=200" \
  --data-urlencode "facet.range.gap=50"
  "facet_counts":{
    "facet_fields":{
      "categoria":["Calzado",1,"Ropa",1],
      "marca":["BasicWear",1,"SpeedRun",1]
    },
    "facet_ranges":{
      "precio":{
        "counts":["0.0",1,"50.0",1,"100.0",0,"150.0",0],
        ...

Solución de problemas

El servicio no arranca. Revisa el log de Solr y el de systemd:

sudo tail -n 50 /var/solr/logs/solr.log
sudo journalctl -u solr -n 50 --no-pager

Un error Solr requires java o UnsupportedClassVersionError indica que falta Java 21. Si el log muestra OutOfMemoryError, aumenta SOLR_HEAP en /etc/default/solr.in.sh sin superar la mitad de la RAM.

Error undefined field al indexar. Has desactivado la creación automática de campos y el documento contiene un campo que no existe en el esquema. Añádelo con add-field como en el paso 6 o elimínalo del documento.

Una consulta no devuelve lo esperado. Añade debugQuery=true a la consulta para ver cómo Solr ha interpretado los términos y cómo ha calculado la puntuación de cada documento.

Conclusión

Tienes Solr 10 funcionando como servicio en Ubuntu 24.04, con una colección de esquema explícito, análisis de texto en español y consultas con filtros, relevancia ponderada y facetas. Como siguientes pasos, puedes activar la autenticación básica con bin/solr auth antes de exponer Solr a otras máquinas, programar copias de seguridad de la colección con la acción BACKUP de la API de colecciones, o añadir más nodos y un ensemble externo de ZooKeeper para pasar a un clúster SolrCloud con réplicas.