Inicio / backend / Cómo añadir búsqueda de texto completo en Postgres con TIN

Cómo añadir búsqueda de texto completo en Postgres con TIN

Tarjeta del articulo sobre busqueda de texto completo en Postgres con TIN

Montar una búsqueda de texto completo en Postgres tiene dos caminos: sacar los datos a un motor aparte —Elasticsearch, OpenSearch— y mantener dos sistemas sincronizados, o resolverlo dentro de la propia base de datos con un índice de texto. El segundo es más simple y no añade infraestructura, pero su opción clásica, el índice GIN, se cae en cuanto las consultas se complican. TIN es una extensión nueva que ataca justo eso. Aquí está cómo se usa, qué dicen sus números y en qué casos no te conviene.

El problema con el índice de toda la vida

Postgres trae búsqueda de texto completo de serie, y su índice estándar es GIN. Funciona bien para una consulta simple y se comporta mal cuando la cosa se pone interesante: en las pruebas que acompañan a TIN, GIN directamente se queda sin memoria en las búsquedas por disyunción y no llega a terminar la prueba. Cuando una consulta tiene que combinar varios términos con «o», el índice deja de servir.

Las alternativas que han ido apareciendo —ParadeDB, pg_textsearch— atacan el problema por lados distintos, y cada una paga un precio: ParadeDB sacrifica latencia de lectura para poder aceptar escrituras, y pg_textsearch solo resuelve disyunciones y no sabe contar.

Cómo se usa TIN para búsqueda de texto completo en Postgres

La extensión está disponible en todas las bases de Postgres y de Neki, y su interfaz es tan corta que se explica en tres líneas. Primero el índice:

CREATE INDEX an_index_name ON table_name USING tin(text_column_name);

Y la consulta, con un operador propio:

SELECT * FROM table_name
  WHERE text_column_name ==> 'some words';

A partir de ahí, lo que necesites. Un buscador ordenado por relevancia, en el ejemplo de comercio electrónico que da PlanetScale:

SELECT * FROM products
  WHERE description ==> 'stretch denim jeans'
  ORDER BY tin.score(ctid) DESC
  LIMIT 10;

Los corchetes expresan «uno o más» de los términos, sin ordenar —el caso de la revisión de documentos legales:

SELECT * FROM emails
  WHERE body ==> '[insider trading conspiracy]';

Y las comillas dobles marcan una frase exacta, que además se puede contar:

SELECT COUNT(*) FROM photos
  WHERE tags ==> '"san francisco"';

El diseño tiene un detalle que explica su rendimiento. TIN usa los valores ctid de Postgres directamente como identificadores de documento, en vez de mantener un mapa propio entre sus identificadores internos y las filas. Es trabajo que las otras extensiones sí tienen que hacer y esta no:

-- sin recorrer nada: búsqueda instantánea de la fila
SELECT * FROM books WHERE ctid = '(190, 17)';

Los números, con la letra pequeña

Estos son los resultados que publica PlanetScale, y conviene leerlos sabiendo que son suyos. El montaje: Postgres 18.6 en un contenedor con 8 vCPU y 32 GB de memoria, sobre un corpus de 85 GB y 150 millones de documentos exportados de Stack Exchange, con 1.719 consultas generadas.

Construir el índice, que es donde más se nota:

  • TIN: 8 minutos 10 segundos, 50,7 GB, con 32 GB de memoria.
  • ParadeDB: 19 minutos 20 segundos, con el doble de memoria.
  • pg_textsearch: 26 minutos 49 segundos, con cuatro veces más memoria.
  • GIN: 2 horas 9 minutos, con el doble de memoria.

En consultas mixtas ordenadas por relevancia, TIN sostiene 199 consultas por segundo con latencias p99 de 256 ms, frente a las 7,9 por segundo y 6.765 ms de ParadeDB. En conjunciones y frases la distancia se abre más: 242 frente a 24 de ParadeDB y 0,4 de GIN. Y en el corpus pequeño de Wikipedia, con el índice entero en memoria, TIN llega a 10.260 consultas por segundo con 2 ms, contra 291 de ParadeDB y 1,4 de GIN.

PlanetScale resume todo eso en una frase: al menos 8 veces más rápido en todas las pruebas. Es su propio resumen, sobre sus propias pruebas, con su propio montaje. Tómalo como una señal de por dónde va, no como un veredicto sobre tu caso.

Criterios para decidir, no una respuesta única

Si tu búsqueda es simple —un término, sin combinar, sobre una tabla modesta— no te hace falta nada de esto. El índice GIN que ya tienes va bien y no añade una dependencia.

Si tus consultas combinan términos con «o», ordenan por relevancia o necesitan contar, ahí es donde GIN se cae y donde TIN se justifica.

Si ya estás en Neki o en Postgres de PlanetScale, la extensión viene con la casa y probarla cuesta una línea.

Si tu base pesa mucho y tu memoria es poca, mira los números de construcción: TIN pide 32 GB para su corpus de 85 GB, pero produce un índice de 50,7 GB — el más grande de los cuatro. GIN construye el índice más pequeño y paga por ello con dos horas de reloj.

Si el texto no está en Postgres y llevas años con Elasticsearch funcionando, la pregunta correcta no es si TIN es mejor, sino si te ahorras un sistema que mantener. Ahí la respuesta depende de cuánto te cueste mantener el segundo sistema.

Cómo comprobarlo por tu cuenta

Nada de decidir con los números de otro:

  • Monta TIN en una réplica o en una base de pruebas y construye el índice con tus datos. El tiempo de construcción es la primera sorpresa, para bien o para mal.
  • Corre tus consultas peores —las que combinan términos, las que cuentan, las que ordenan— contra TIN y contra tu índice actual, y mide latencia y memoria, no solo tiempo total.
  • Mira el tamaño del índice resultante: en un servidor con el disco justo, un índice más grande que el corpus pesa.

Y si lo que mueves son datos y no texto: cómo migrar a dbt v2 sin romper producción es la otra mitad de la historia.

Fuentes: el anuncio de TIN en el blog de PlanetScale, de donde salen la sintaxis, el montaje de las pruebas y todos los números de este artículo. La guía de inicio está en la documentación de PlanetScale.

Etiquetado:

Un Comentario

Deje un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *