Crear un índice de texto
Los índices de texto pueden usarse con cualquier versión de ClickHouse >= 26.2, independientemente de la configuración compatibility.
Query
- String y FixedString,
- Array(String) y Array(FixedString),
- Map (mediante las funciones mapKeys y mapValues), y
- JSON (mediante las funciones JSONAllPaths y
JSONAllValues).
Array(Nullable(String or FixedString)).
Como alternativa, para añadir un índice de texto a una tabla existente:
Query
Query
Query
tokenizer especifica el tokenizador:
splitByNonAlphadivide cadenas por caracteres ASCII no alfanuméricos (consulte la función splitByNonAlpha).splitByString(S)divide cadenas usando determinadas cadenas separadorasSdefinidas por el usuario (consulte la función splitByString). Los separadores pueden especificarse mediante un parámetro opcional; por ejemplo,tokenizer = splitByString([', ', '; ', '\n', '\\']). Tenga en cuenta que cada cadena puede constar de varios caracteres (', 'en el ejemplo). La lista de separadores predeterminada, si no se especifica explícitamente (por ejemplo,tokenizer = splitByString), es un único espacio en blanco[' '].asciiCJKdivide cadenas en tokens usando reglas de límites de palabra de Unicode (similares a Unicode Text Segmentation (UAX #29)). Los caracteres ASCII alfanuméricos y los guiones bajos forman tokens con conectores (ASCII:para letras,.y'para caracteres del mismo tipo). Los caracteres Unicode no ASCII, incluidos los caracteres CJK, se convierten en tokens de un solo carácter.ngrams(N)divide cadenas enN-grams del mismo tamaño (consulte la función ngrams). La longitud del ngram puede especificarse mediante un parámetro entero opcional entre 1 y 8; por ejemplo,tokenizer = ngrams(3). El tamaño predeterminado del ngram, si no se especifica explícitamente (por ejemplo,tokenizer = ngrams), es 3.sparseGrams(min_length, max_length, min_cutoff_length)divide cadenas en n-grams de longitud variable de al menosmin_lengthy como máximomax_lengthcaracteres (inclusive) (consulte la función sparseGrams). A menos que se especifique explícitamente,min_lengthymax_lengthtoman los valores predeterminados 3 y 100. Si se proporciona el parámetromin_cutoff_length, solo se devuelven n-grams con una longitud mayor o igual quemin_cutoff_length. En comparación conngrams(N), el tokenizadorsparseGramsproduce N-grams de longitud variable, lo que permite una representación más flexible del texto original. Por ejemplo,tokenizer = sparseGrams(3, 5, 4)genera internamente 3-, 4- y 5-grams a partir de la cadena de entrada, pero solo se devuelven los 4- y 5-grams.arrayno realiza tokenización; es decir, cada valor de fila es un token (consulte la función array).
El tokenizador
splitByString aplica los separadores de división de izquierda a derecha.
Esto puede crear ambigüedades.
Por ejemplo, las cadenas separadoras ['%21', '%'] harán que %21abc se tokenice como ['abc'], mientras que, si se intercambia el orden de ambas cadenas separadoras por ['%', '%21'], la salida será ['21abc'].
En la mayoría de los casos, conviene que la coincidencia dé prioridad a los separadores más largos.
Por lo general, esto puede lograrse pasando las cadenas separadoras en orden descendente de longitud.
Si las cadenas separadoras forman un código prefijo, pueden pasarse en cualquier orden.Query
Response
asciiCJK, ya que gestiona correctamente los límites de palabras de Unicode, incluidos los caracteres CJK.
:::
Argumento de preprocesador (opcional). El preprocesador se refiere a una expresión que se aplica a la cadena de entrada antes de la tokenización.
Los casos de uso típicos del argumento de preprocesador incluyen
- Conversión a minúsculas o mayúsculas, o case folding para habilitar la coincidencia sin distinción entre mayúsculas y minúsculas, p. ej., lower, lowerUTF8, caseFoldUTF8.
- Normalización en UTF-8, p. ej. normalizeUTF8NFC, normalizeUTF8NFD, normalizeUTF8NFKC, normalizeUTF8NFKD, normalizeUTF8NFKCCasefold, toValidUTF8.
- Eliminación o transformación de caracteres o subcadenas no deseados, como los acentos, p. ej. extractTextFromHTML, substring, idnaEncode, translate, removeDiacriticsUTF8.
Nullable(T) o LowCardinality(T), la expresión del preprocesador debe aceptar valores anulables o de baja cardinalidad (es decir, no debe lanzar una excepción).
Ejemplos:
INDEX idx col TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = lower(col))INDEX idx col TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = substringIndex(col, '\n', 1))INDEX idx col TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = lower(extractTextFromHTML(col)))INDEX idx col TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = removeDiacriticsUTF8(caseFoldUTF8(col)))
INDEX idx lower(col) TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = upper(lower(col)))INDEX idx lower(col) TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = concat(lower(col), lower(col)))- No permitido:
INDEX idx lower(col) TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = concat(col, col))
Los preprocesadores son, en principio, equivalentes a envolver la columna o expresión del índice con la expresión del preprocesador.
Por ejemplo, el preprocesador
lower en INDEX idx col TYPE text(tokenizer = 'splitByNonAlpha', preprocessor = lower(col)) puede emularse con INDEX idx lower(col) TYPE text(tokenizer = 'splitByNonAlpha').
Esta última forma tiene la desventaja de que el preprocesador emulado solo se aplica si coincide con la condición de filtro de la cláusula WHERE.
Por ejemplo, WHERE hasAllTokens(lower(col), [...]) coincide, mientras que WHERE hasAllTokens(col, [...]) no.
Por lo tanto, para una experiencia de usuario óptima, recomendamos usar expresiones de preprocesador.SETTINGS use_skip_indexes = 0).
Por ejemplo,
Query
Query
Query
Query
Query
Response
Uso de un índice de texto
Recomendamos usar las funciones
hasAnyTokens y hasAllTokens para buscar en el índice de texto; consulta más abajo.
Estas funciones funcionan con todos los tokenizadores disponibles y con todas las expresiones de preprocesador posibles.
Como las demás funciones compatibles son históricamente anteriores al índice de texto, en muchos casos tuvieron que conservar su comportamiento heredado (p. ej., sin compatibilidad con preprocesadores).Funciones compatibles
WHERE o en las cláusulas PREWHERE:
=
= (equals) coincide con el término de búsqueda completo.
Ejemplo:
IN
IN (in) es similar a equals, pero coincide con cualquiera de los términos de búsqueda.
Ejemplo:
NOT IN (notIn) no es compatible con el índice de texto.LIKE and match
Actualmente, estas funciones usan el índice de texto para filtrar solo si el tokenizador del índice es
splitByNonAlpha, ngrams o sparseGrams.NOT LIKE (notLike) no es compatible con el índice de texto.LIKE (like) y la función match con índices de texto, ClickHouse debe poder extraer tokens completos del término de búsqueda.
En el caso del índice con el tokenizador ngrams, esto ocurre si la longitud de las cadenas buscadas entre comodines es igual o mayor que la longitud del ngram.
Ejemplo de índice de texto con el tokenizador splitByNonAlpha:
support en el ejemplo podría coincidir con support, supports, supporting, etc.
Este tipo de consulta es una consulta por subcadena y un índice de texto no puede acelerarla.
Para aprovechar un índice de texto en consultas LIKE, el patrón LIKE debe reescribirse de la siguiente manera:
support garantizan que el término pueda extraerse como token.
Afortunadamente, hay un caso especial en el que ClickHouse puede aprovechar el índice invertido para acelerar significativamente las consultas LIKE.
Consulta la sección sobre optimización del rendimiento de LIKE/ILIKE para obtener más información.
multiSearchAny and multiMatchAny
LIKE y match (véase arriba): ClickHouse debe poder extraer tokens completos de cada subcadena buscada, y la lista de subcadenas buscadas debe ser constante.
Se lee un gránulo si alguna subcadena buscada puede estar presente en él.
En multiMatchAny, si un patrón no puede reducirse a un requisito de token (por ejemplo, .*, que coincide con cualquier documento), no se puede usar el índice de texto y la consulta recurre a un escaneo completo.
Al igual que con LIKE y match, la búsqueda de subcadenas y expresiones regulares funciona mejor con los tokenizadores ngrams y sparseGrams.
Estos tokenizadores indexan n-grams de caracteres superpuestos, por lo que una subcadena buscada se descompone en n-grams presentes en el índice allí donde aparece como subcadena, independientemente de si empieza o termina en medio de una palabra.
Por tanto, una subcadena buscada puede usarse tal cual, siempre que tenga al menos la misma longitud que el tamaño del n-gram.
Ejemplo del índice de texto con el tokenizador ngrams:
splitByNonAlpha, en cambio, solo indexa tokens completos (palabras enteras).
Como una cadena de búsqueda puede empezar o terminar en medio de una palabra, ClickHouse descarta los tokens inicial y final de cada cadena de búsqueda, de modo que el índice solo pueda descartar gránulos usando tokens completos.
Para que la búsqueda por subcadenas y expresiones regulares use el índice con splitByNonAlpha, rodea cada cadena de búsqueda con caracteres separadores (como espacios) para que forme uno o más tokens completos.
Ejemplo del índice de texto con el tokenizador splitByNonAlpha:
startsWith and endsWith
LIKE, las funciones startsWith y endsWith solo pueden usar un índice de texto si se pueden extraer tokens completos del término de búsqueda.
En el caso del índice con el tokenizador ngrams, esto sucede si la longitud de las cadenas buscadas entre comodines es igual o mayor que la longitud del ngram.
Ejemplo del índice de texto con el tokenizador splitByNonAlpha:
clickhouse se considera un token.
support no es un token, porque puede coincidir con support, supports, supporting, etc.
Para encontrar todas las filas que comienzan con clickhouse supports, termine el patrón de búsqueda con un espacio final:
endsWith debe usarse con un espacio inicial:
hasToken and hasTokenOrNull
La función
hasToken parece fácil de usar, pero presenta ciertos inconvenientes con tokenizadores no predeterminados y expresiones de preprocesador.
Recomendamos usar en su lugar las funciones hasAnyTokens y hasAllTokens.hasAnyTokens and hasAllTokens
hasPhrase
hasAllTokens, que solo requiere que todos los tokens estén presentes en algún lugar, hasPhrase exige que aparezcan como una secuencia consecutiva.
La frase de búsqueda se tokeniza con el mismo tokenizador configurado para la columna de índice.
Ten en cuenta que la función requiere uno de estos tokenizadores: splitByNonAlpha, splitByString, ngrams o asciiCJK.
Ejemplo:
has
hasAny y hasAll
mapContains
mapContainsKey) busca coincidencias entre los tokens extraídos de la cadena buscada y las claves de un mapa.
El comportamiento es similar al de la función equals con una columna String.
El índice de texto solo se utiliza si se creó sobre una expresión mapKeys(map).
Ejemplo:
mapContainsValue
equals con una columna String.
El índice de texto solo se utiliza si se creó sobre una expresión mapValues(map).
Ejemplo:
mapContainsKeyLike and mapContainsValueLike
operator[]
mapKeys(map) o mapValues(map), o sobre ambas.
Ejemplo:
Array(T) y Map(K, V) con el índice de texto.
Indexación de columnas Array(String)
clickhouse) obliga a escanear todas las entradas:
keywords de cada fila.
Para solucionar este problema de rendimiento, definimos un índice de texto para la columna keywords:
Indexación de columnas de tipo Map
Indexación de columnas JSON
JSON de tres formas:
- Índices en subcolumnas específicas — crea un índice de texto en una ruta JSON conocida, igual que en una columna normal. Esto indexa los valores de esa ruta.
- Índices basados en rutas con JSONAllPaths — indexan todas las rutas presentes en cada gránulo para omitir los gránulos que no pueden contener la ruta consultada. Es similar a las columnas
Map. - Índices basados en valores con JSONAllValues — indexan todos los valores de todas las rutas JSON para acelerar la búsqueda de texto completo en cualquier subcolumna JSON con un solo índice.
Índices sobre subcolumnas específicas
- Ruta tipada declarada en la indicación de tipo de JSON: acceda directamente por nombre:
json.a. - Ruta dinámica con conversión explícita: use la sintaxis de conversión
:::json.b::String.
Query
Query
Response
Query
Response
Índices basados en rutas con JSONAllPaths
Map, se pueden crear índices de texto en columnas JSON mediante JSONAllPaths.
El índice almacena el conjunto de rutas JSON presentes en cada gránulo y las utiliza para omitir los gránulos en los que no está presente la ruta consultada.
Definición de ejemplo del índice:
Query
EXPLAIN indexes = 1 para verificar que se está utilizando el índice de omisión.
Cuando una ruta existe solo en una parte, el índice se salta la otra parte.
Ejemplo:
Query
Response
Query
Response
IS NOT NULL también usa el índice: omite los gránulos en los que la ruta no existe (ya que el valor sería NULL):
Ejemplo:
Query
Response
Índices basados en valores con JSONAllValues
JSONAllValues.
JSONAllValues devuelve todos los valores de una columna JSON como Array(String).
Los valores de tipos de datos que no son cadenas (p. ej., enteros y arrays) se convierten a su representación textual.
Un índice de texto creado con JSONAllValues indexa estas representaciones textuales en todas las rutas JSON de cada fila.
Este índice puede acelerar después las consultas que filtran por subcolumnas JSON individuales.
Cuando una consulta filtra por una subcolumna específica (p. ej., data.user_name = 'alice'), el índice de texto puede descartar rápidamente las filas (y los gránulos) que no contienen los tokens de búsqueda en ninguno de sus valores JSON.
El índice puede producir falsos positivos cuando distintas rutas JSON contienen los mismos tokens.
Por ejemplo, si la fila 1 tiene
{"a": "hello", "b": "world"} y una consulta busca data.a = 'world', el índice de texto no puede distinguir que world pertenece a la ruta b, no a a.
En esos casos, el índice no descartará la fila, y el filtro sobre los datos reales de la columna se encargará de la evaluación final.
Este es el mismo comportamiento que en otros casos de uso de índices de texto, donde el índice actúa como un prefiltro rápido.Creación del índice
Patrones de consulta admitidos
String y la función equals para todas las columnas.
Acceso a las subcolumnas:
CAST explícito:
IN:
Búsqueda de frases
hasPhrase.
Todos los tokens de la frase deben aparecer de forma consecutiva y en el mismo orden en el documento.
El índice de texto acelera la búsqueda de frases mediante la intersección de las posting lists de todos los tokens de la frase para identificar gránulos candidatos.
Dentro de esos gránulos, ClickHouse luego verifica la adyacencia exacta de los tokens.
hasPhrase es compatible con los tokenizadores splitByNonAlpha, splitByString, ngrams y asciiCJK.
La cadena de la frase se tokeniza con el tokenizador configurado en el índice.
Los caracteres separadores del tokenizador en la frase se ignoran: hasPhrase(text, 'quick+brown') es equivalente a hasPhrase(text, 'quick brown') para el tokenizador splitByNonAlpha.
Ejemplo
Query
Query
Response
'New weather in York') no coincide porque los tokens no están en el orden correcto.
La fila 3 ('weather in New Orleans') no coincide porque no contiene el token 'York'.
Optimización del rendimiento
Lectura directa
- El ajuste query_plan_direct_read_from_text_index (
truede forma predeterminada) especifica si lectura directa está habilitado en general. - El ajuste use_skip_indexes_on_data_read era un requisito previo para lectura directa en las versiones de ClickHouse < 26.4.
hasToken, hasAllTokens y hasAnyTokens.
Si el índice de texto se define con un tokenizador array, lectura directa también es compatible con las funciones equals, has, hasAny, hasAll, mapContainsKey y mapContainsValue.
Estas funciones también pueden combinarse mediante los operadores AND, OR y NOT.
Las cláusulas WHERE o PREWHERE también pueden contener filtros adicionales distintos de las funciones de búsqueda de texto (para columnas de texto u otras columnas); en ese caso, la optimización de lectura directa seguirá utilizándose, pero será menos eficaz (solo se aplica a las funciones de búsqueda de texto compatibles).
Para comprobar si una consulta utiliza lectura directa, ejecute la consulta con EXPLAIN PLAN actions = 1.
Por ejemplo, una consulta con lectura directa deshabilitado
query_plan_direct_read_from_text_index = 1
__text_index_<index_name>_<function_name>_<id>.
Si esta columna está presente, significa que se usa lectura directa.
Si la cláusula de filtro WHERE solo contiene funciones de búsqueda de texto, la consulta puede evitar por completo leer los datos de la columna y obtener el mayor beneficio de rendimiento con lectura directa.
Sin embargo, incluso si se accede a la columna de texto en otra parte de la consulta, lectura directa seguirá aportando una mejora del rendimiento.
Lectura directa como pista
Lectura directa como pista se basa en los mismos principios que lectura directa normal, pero añade un filtro adicional generado a partir de los datos del índice de texto sin dejar de usar la columna de texto subyacente.
Se utiliza en funciones para las que leer solo desde el índice de texto produciría falsos positivos.
Las funciones compatibles son: like, startsWith, endsWith, equals, has, hasPhrase, mapContainsKey y mapContainsValue.
El filtro adicional puede aportar más selectividad y, en combinación con otros filtros, restringir aún más el conjunto de resultados, lo que ayuda a reducir la cantidad de datos leídos de otras columnas.
Lectura directa como pista se controla mediante la configuración query_plan_text_index_add_hint (habilitada de forma predeterminada).
Ejemplo de consulta sin pista:
query_plan_text_index_add_hint = 1
__text_index_...) a la condición del filtro.
Gracias a la optimización PREWHERE, la condición del filtro se descompone en tres conjunciones independientes, que se aplican en orden de complejidad computacional creciente.
Para esta consulta, el orden de aplicación es __text_index_..., luego greaterOrEquals(...) y, por último, like(...).
Este orden permite omitir todavía más gránulos de datos que los que ya omiten el índice de texto y el filtro original, antes de leer las columnas pesadas utilizadas en la consulta tras la cláusula WHERE, lo que reduce aún más la cantidad de datos que hay que leer.
Consultas LIKE/ILIKE
%<alpha-numeric-characters-without-spaces>% y el tokenizador del índice de texto es splitByNonAlpha o array, ClickHouse aprovecha el índice invertido para acelerar significativamente las consultas LIKE/ILIKE. Para ello, ClickHouse examina el diccionario del índice invertido en lugar de hacer un escaneo completo de la tabla para encontrar el patrón coincidente.
Cuando la optimización está habilitada, las consultas LIKE/ILIKE deberían ser significativamente más rápidas que un escaneo completo de la tabla. Sin embargo, cuando el patrón coincide con la mayoría de los tokens del diccionario, el rendimiento puede ser peor que con un escaneo completo de la tabla. Afortunadamente, existe un mecanismo de fallback para evitarlo.
La optimización se controla mediante una configuración:
El mecanismo de fallback se controla mediante dos configuraciones:
Esta optimización solo admite las funciones like e ilike.
Almacenamiento en caché
Configuración de la caché de tokens
Configuración de la caché de encabezados
Configuración de la caché de listas de postings
Limitaciones
- La materialización de índices de texto con un número elevado de tokens (p. ej., 10 mil millones de tokens) puede consumir cantidades significativas de memoria. La
materialización de índices de texto puede producirse directamente (
ALTER TABLE <table> MATERIALIZE INDEX <index>) o indirectamente durante las fusiones de partes. - No es posible materializar índices de texto en partes con más de 4.294.967.296 (= 2^32 = aprox. 4,2 mil millones) filas. Sin un índice de texto materializado, las consultas recurren a una búsqueda exhaustiva lenta dentro de la parte. Como estimación del peor caso, suponga que una parte contiene una única columna de tipo String y que la configuración de MergeTree
max_bytes_to_merge_at_max_space_in_pool(valor predeterminado: 150 GB) no se ha modificado. En este caso, esto ocurre si la columna contiene, de media, menos de 29,5 caracteres por fila. En la práctica, las tablas también contienen otras columnas y el umbral es varias veces menor (según el número, el tipo y el tamaño de las demás columnas).
Índices de texto frente a índices basados en filtros Bloom
bloom_filter, ngrambf_v1, tokenbf_v1, sparse_grams), pero ambos difieren fundamentalmente en su diseño y en los casos de uso a los que están destinados:
Índices de filtros Bloom
- Se basan en estructuras de datos probabilísticas que pueden producir falsos positivos.
- Solo pueden responder preguntas de pertenencia a conjuntos; es decir, si la columna puede contener el token X o si definitivamente no lo contiene.
- Almacenan información a nivel de gránulo para permitir omitir rangos amplios durante la ejecución de consultas.
- Son difíciles de ajustar correctamente (consulta aquí un ejemplo).
- Son bastante compactos (unos pocos kilobytes o megabytes por parte).
- Construyen un índice invertido determinista sobre tokens. El propio índice no puede producir falsos positivos.
- Están optimizados específicamente para cargas de trabajo de búsqueda de texto.
- Almacenan información a nivel de fila, lo que permite una búsqueda eficiente de términos.
- Son bastante grandes (de decenas a cientos de megabytes por parte).
- No admiten tokenización ni preprocesamiento avanzados.
- No admiten búsquedas con varios tokens.
- No ofrecen las características de rendimiento esperadas de un índice invertido.
- Proporcionan tokenización y preprocesamiento
- Ofrecen soporte eficiente para
hasAllTokens,LIKE,matchy funciones similares de búsqueda de texto. - Tienen una escalabilidad significativamente mayor para grandes corpus de texto.
Detalles de implementación
- un diccionario que asigna cada token a una lista de postings, y
- un conjunto de listas de postings, cada una de las cuales representa un conjunto de números de fila.
dictionary_block_size).
Un archivo de bloques de diccionario (.dct) consta de todos los bloques de diccionario de todos los gránulos de índice de una parte.
Archivo de encabezado del índice (.idx)
El archivo de encabezado del índice contiene, para cada bloque de diccionario, el primer token del bloque y su desplazamiento relativo dentro del archivo de bloques de diccionario.
Esta estructura de índice disperso es similar al índice de clave primaria disperso) de ClickHouse.
Archivo de listas de postings (.pst)
Las listas de postings de todos los tokens se almacenan secuencialmente en el archivo de listas de postings.
Para ahorrar espacio y, al mismo tiempo, permitir operaciones rápidas de intersección y unión, las listas de postings se almacenan como bitmaps Roaring.
Si la lista de postings es más grande que posting_list_block_size, se divide en varios bloques que se almacenan secuencialmente en el archivo de listas de postings.
Fusión de índices de texto
Cuando se fusionan partes de datos, no es necesario reconstruir el índice de texto desde cero; en su lugar, puede fusionarse eficientemente en una etapa independiente del proceso de fusión.
Durante esta etapa, los diccionarios ordenados de los índices de texto de cada parte de entrada se leen y se combinan en un nuevo diccionario unificado.
Los números de fila de las listas de postings también se recalculan para reflejar sus nuevas posiciones en la parte de datos fusionada, usando una correspondencia entre los números de fila antiguos y los nuevos que se crea durante la fase inicial de la fusión.
Este método de fusionar índices de texto es similar a cómo se fusionan las projections con la columna _part_offset.
Si el índice no está materializado en la parte de origen, se construye, se escribe en un archivo temporal y luego se fusiona junto con los índices de las otras partes y de otros archivos de índice temporales.
Depuración
La table function mergeTreeTextIndex puede usarse para inspeccionar índices de texto.
Ejemplo: conjunto de datos de Hacker News
hackernews:
ALTER TABLE para añadir un índice de texto en la columna comment y luego materializarlo:
hasToken, hasAnyTokens y hasAllTokens.
Los siguientes ejemplos mostrarán la gran diferencia de rendimiento entre un análisis estándar del índice y la optimización de lectura directa.
1. Uso de hasToken
hasToken comprueba si el texto contiene un token específico.
Buscaremos el token sensible a mayúsculas y minúsculas ‘ClickHouse’.
Lectura directa deshabilitada (escaneo estándar)
De forma predeterminada, ClickHouse usa el índice de omisión para filtrar gránulos y luego lee los datos de la columna de esos gránulos.
Podemos simular este comportamiento deshabilitando la lectura directa.
2. Uso de hasAnyTokens
hasAnyTokens comprueba si el texto contiene al menos uno de los tokens indicados.
Buscaremos comentarios que contengan ‘love’ o ‘ClickHouse’.
Direct read desactivado (Exploración estándar)
3. Uso de hasAllTokens
hasAllTokens comprueba si el texto contiene todos los tokens indicados.
Buscaremos comentarios que contengan tanto ‘love’ como ‘ClickHouse’.
Lectura directa deshabilitada (escaneo estándar)
Incluso con lectura directa deshabilitada, el índice de omisión estándar sigue siendo eficaz.
Reduce las 28.7M filas a solo 147.46K filas, pero aun así debe leer 57.03 MB de la columna.
4. Búsqueda compuesta: OR, AND, NOT, …
hasAnyTokens(comment, ['ClickHouse', 'clickhouse']) sería la sintaxis recomendada y más eficiente.
- Blog: Anuncio de la disponibilidad general de la búsqueda de texto completo de ClickHouse
- Blog: Cómo crear una búsqueda de texto completo de alto rendimiento para almacenamiento de objetos
- Video: Introducción a la búsqueda de texto completo en ClickHouse
- Video: Entre bastidores: la búsqueda de texto completo a la escala y velocidad de ClickHouse
- Presentation: Por dentro de la búsqueda de texto completo en ClickHouse: rápida, nativa y columnar
- Presentation: Índices invertidos de bases de datos: el porqué, el qué y el cómo, FOSDEM 2026