2010-03-25 41 views
11

Después de una pregunta publicada aquí acerca de cómo puedo aumentar la velocidad en uno de mis métodos de búsqueda SQL, me recomendaron actualizar mi tabla para hacer uso de la búsqueda de texto completo. Esto es lo que he hecho ahora, usando índices Gist para hacer búsquedas más rápido. En algunas de las consultas "simples" he notado un aumento marcado del cual estoy muy contento.PostgreSQL: Búsqueda de texto completo - ¿Cómo buscar palabras parciales?

Sin embargo, estoy teniendo dificultad en la búsqueda de palabras parciales. Por ejemplo, tengo varios registros que contienen la palabra Squire (454) y tengo varios registros que contienen Squirrel (173). Ahora, si busco Squire, solo devuelve los 454 registros, pero también quiero que devuelva los registros de Squirrel.

Mi consulta es el siguiente

SELECT title 
FROM movies 
WHERE vectors @@ to_tsoquery('squire'); 

pensé que podía hacer to_tsquery('squire%') pero eso no funciona.
¿Cómo hago para buscar coincidencias parciales?

Además, en mi base de datos tengo registros que son películas y otros que son simplemente programas de televisión. Estos se diferencian por el "sobre el nombre, así que como" Munsters "es un programa de televisión, mientras que The Munsters es la película del espectáculo. Lo que quiero hacer es buscar solo el programa de televisión Y solo las películas. ¿Alguna idea sobre cómo puedo lograr esto?

Saludos Anthoni

+0

Si tiene la clave de búsqueda 'squire' pero quiere obtener el resultado' squirrel', es posible que deba especificar restricciones adicionales. Porque de lo contrario uno podría argumentar que tenían la clave de búsqueda 'mama' pero querían el resultado' rabbit'. Entonces quizás quieras cortar la clave de búsqueda y convertir 'squire' en' s | sq | squ | squi | Squir | squire' ... Este o más algoritmos le darán la 'ardilla'.Creo que la respuesta de @Joshua Burns contiene una solución más genérica que la mía, si quieres ser genérico. –

Respuesta

4

incluso utilizando LIKE usted no será capaz de obtener 'ardilla' de squire% porque 'ardilla' tiene dos 'r. Para llegar Squire y la ardilla podría ejecutar la siguiente consulta:

SELECT title FROM movies WHERE vectors @@ to_tsquery('squire|squirrel'); 

para diferenciar entre las películas y programas de televisión que debe agregar una columna a la base de datos. Sin embargo, hay muchas maneras de despellejar a este gato. Podría usar una subconsulta para forzar a postgres a encontrar primero las películas que coincidan con 'squire' y 'squirrel' y luego buscar ese subconjunto para buscar títulos que comiencen con '' ''. Es posible crear índices para usar en las búsquedas LIKE '"%...' .

sin explorar otras posibilidades de indexación también se puede ejecutar estos - perder el tiempo con ellos para encontrar que es más rápido:

SELECT title 
FROM (
    SELECT * 
    FROM movies 
    WHERE vectors @@ to_tsquery('squire|squirrel') 
) t 
WHERE title ILIKE '"%'; 

o

SELECT title 
FROM movies 
WHERE vectors @@ to_tsquery('squire|squirrel') 
    AND title ILIKE '"%'; 
0

Una cosa que puede trabajar es romper la palabra estás buscando en una p más pequeña letras. Así que podrías buscar cosas que tengan squi o quir o squire o etc ... No estoy seguro de cuán eficiente sería eso, pero podría ser útil.

Cuando se busca la película o película que podría intentar colocar el texto en la comilla simple. entonces sería 'show' o '"show"'. Creo que eso también podría funcionar.

27

intento,

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire:*') 

Esto funciona en PostgreSQL 8.4+

+2

Has especificado un lexema con coincidencia de prefijo, pero no resolverá el problema: todavía falta una 'r'. Probablemente deberías eliminar esta respuesta. –

+2

@RichardMichael No estoy de acuerdo porque este método funciona. El OP intenta obtener 2 palabras que no son similares. 'squire' es ** no ** una parte de la palabra' ardilla'. Pidió una coincidencia parcial y esta respuesta lo hace. Debería ser votado. –

+0

Gracias por esto, me ayudó en un caso de uso que tengo. +1 –

25

Anthoni,

Asumiendo que planea usar solamente la codificación ASCII (podría ser difícil, soy consciente), una opción muy viable puede ser la (pg_trgm) Módulo trigrama: http://www.postgresql.org/docs/9.0/interactive/pgtrgm.html

trigrama utiliza métodos de indexación incorporados como Gist y Gin. La única modificación que debe realizar es al definir su índice, especifique una clase de operador de gist_trgm_ops o gin_trgm_ops.

Si los módulos contrib no están ya instalados, en Ubuntu es tan fácil y ejecutar el siguiente comando desde el shell:

# sudo apt-get install postgresql-contrib 

Después de los módulos contrib se ponen a disposición, debe instalar la extensión pg_trgm en la base de datos en cuestión. Esto se hace mediante la ejecución de la siguiente consulta de base de datos PostgreSQL en el que desea instalar el módulo en:

CREATE EXTENSION pg_trgm; 

Después de la extensión pg_trgm se ha instalado, estamos listos para pasar un buen rato!

-- Create a test table. 
CREATE TABLE test (my_column text) 
-- Create a Trigram index. 
CREATE INDEX test_my_colun_trgm_idx ON test USING gist (my_column gist_trgm_ops); 
-- Add a couple records 
INSERT INTO test (my_Column) VALUES ('First Entry'), ('Second Entry'), ('Third Entry') 
-- Query using our new index -- 
SELECT my_column, similarity(my_column, 'Frist Entry') AS similarity FROM test WHERE my_column % 'Frist Entry' ORDER BY similarity DESC 
+2

¿Por qué no es esta la respuesta aceptada? Está lejos el mejor :) – jperelli

+0

la similitud en su ejemplo usa la palabra perfecta y no la palabra mal escrita que se usa en su cláusula where. seleccione similitud ('Frist Entry', 'First Entry') => 0.5 –

+0

buen punto, error tipográfico en mi extremo. resuelto. gracias por el aviso :) –

4

@ alexander-mera solution works great!

Nota: También asegúrese de convertir espacios a +. Por ejemplo, si está buscando squire knight.

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire+knight:*') 
+0

Usar el '+' no funciona para mí en PosgreSQL 9.4.1. Si, en cambio, uso '&', funciona como un amuleto. – facundofarias

0

La amplia solución a esto es utilizar la función de ts_rewrite PG para configurar una tabla de alias que trabaja para partidos alternos (ver Query Rewriting). Esto abarca casos como el suyo por encima y al mismo tiempo de manipulación completamente diferentes casos como la búsqueda de tree rat y obtener resultados para squirrel, etc.

Todos los detalles y explicaciones en ese enlace, pero el quid de la cuestión es que se puede configurar una tabla de alias con 2 columnas ts_query y pasar a una consulta de esa mesa en su búsqueda, así:

CREATE TABLE aliases (t tsquery primary key, s tsquery); 
INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn')); 

SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); 

resultantes en una consulta final que se parece más a:

WHERE vectors @@ ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases') 

Esto es similar a la configuración del tesauro dentro de PG, pero funciona sin requerir un reindex total cada vez que agrega algo. Cuando te encuentras con pequeñas variaciones de ortografía y casos de "cuando busco esto espero resultados como este", es muy fácil agregarlos a la mesa muy rápido. Puede agregar más columnas a esa tabla, siempre y cuando la consulta basada en ts_rewrite devuelva las 2 columnas to_tsquery esperadas.

Cuando profundice en esa documentación, verá ejemplos sugeridos para la optimización del rendimiento también. Existe un equilibrio entre usar trigram para velocidad pura y usar vector/query/rewrite para mayor robustez.

Cuestiones relacionadas