2010-03-25 17 views
11

Dopo una domanda postata su come aumentare la velocità su uno dei miei metodi di ricerca SQL, mi è stato consigliato di aggiornare la mia tabella per utilizzare la ricerca di testo completo. Questo è quello che ho fatto ora, usando gli indici Gist per rendere la ricerca più veloce. In alcune delle "semplici" domande ho notato un notevole aumento di cui sono molto felice.PostgreSQL: ricerca testo completo - Come cercare parole parziali?

Tuttavia, sto riscontrando difficoltà nella ricerca di parole parziali. Ad esempio, ho diversi record che contengono la parola Squire (454) e ho diversi record che contengono Squirrel (173). Ora, se cerco Squire, restituisce solo i 454 record, ma voglio anche che restituisca anche i record di scoiattolo.

La mia domanda si presenta così

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

ho pensato che avrei potuto fare to_tsquery('squire%') ma che non funziona.
Come ottengo la ricerca di corrispondenze parziali?

Inoltre, nel mio database ho registrazioni che sono film e altre che sono solo programmi TV. Questi sono differenziati dal "" sopra il nome, quindi "Munsters" è uno show televisivo, mentre The Munsters è il film dello spettacolo. Quello che voglio essere in grado di fare è cercare solo la serie TV E solo i film. Qualche idea su come posso ottenere questo?

saluti Anthoni

+0

Se si ha la chiave di ricerca 'scudietta 'ma si desidera ottenere il risultato' scoiattolo', potrebbe essere necessario specificare ulteriori vincoli. Perché altrimenti si potrebbe obiettare che avevano la chiave di ricerca "mamma" ma volevano il risultato "coniglio". Quindi forse potresti voler dividere la tua chiave di ricerca e trasformare 'scudiero 'in' s | sq | squ | squi | squir | scudiero ... Questo algoritmo più elaborato ti procurerebbe lo "scoiattolo".Penso che la risposta di @Joshua Burns contenga una soluzione più generica della mia, se vuoi essere generico. –

risposta

4

anche utilizzando LIKE non sarà in grado di ottenere 'scoiattolo' dal squire% perche 'scoiattolo' ha due 'r. Per ottenere Squire e lo scoiattolo è possibile eseguire la seguente query:

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

di distinguere tra film e show televisivi si dovrebbe aggiungere una colonna al database. Tuttavia, ci sono molti modi per pelle questo gatto. È possibile utilizzare una sottoquery per forzare postgres a trovare prima i film corrispondenti a "squire" e "scoiattolo" e quindi cercare quel sottoinsieme per trovare i titoli che iniziano con un "". È possibile creare indici da utilizzare nelle ricerche LIKE '"%...' .

senza esplorare altre possibilità di indicizzazione si potrebbe anche correre questi - pasticciare con loro per trovare quale è più veloce:

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 che può funzionare è rompere la parola stai cercando in più piccolo p arti. Quindi potresti cercare cose che hanno squi o quir o scudieri o altro ... Non sono sicuro di quanto sarebbe efficiente, ma potrebbe essere d'aiuto.

Quando si cerca il film o il film si può provare a inserire il testo nella citazione singola. quindi sarebbe "show" o "" show "". Penso che potrebbe funzionare anche

27

prova,

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

questo funziona su PostgreSQL 8.4+

+2

Hai specificato un lexeme con la corrispondenza del prefisso, ma non risolverà il problema: manca ancora una "r". Probabilmente dovresti eliminare questa risposta. –

+2

@RichardMichael Non sono d'accordo perché questo metodo funziona. L'OP sta cercando di ottenere 2 parole che non sono simili. "scudiero" è ** non ** un parziale della parola "scoiattolo". Ha chiesto una corrispondenza parziale e questa risposta lo fa. Dovrebbe essere upvoted. –

+0

Grazie per questo, aiutato in un caso d'uso che ho. +1 –

25

Anthoni,

Supponendo che si pensa di utilizzare solo la codifica ASCII (potrebbe essere difficile, io sono consapevole), una valida opzione può essere il modulo Trigram (pg_trgm): http://www.postgresql.org/docs/9.0/interactive/pgtrgm.html

Trigram utilizza metodi di indicizzazione incorporati come Gist e Gin. L'unica modifica che devi effettuare è quando definisci il tuo indice, specifica una classe operatore di gist_trgm_ops o gin_trgm_ops.

Se non sono già installati i moduli contrib, in Ubuntu è come facile ed eseguendo il comando seguente dalla shell:

# sudo apt-get install postgresql-contrib 

Dopo che i moduli contrib sono messo a disposizione, è necessario installare l'estensione pg_trgm in il database in questione. A tale scopo, eseguendo la seguente query PostgreSQL sul database che si desidera installare il modulo in:

CREATE EXTENSION pg_trgm; 

Dopo l'estensione pg_trgm è stato installato, siamo pronti a divertirsi un po '!

-- 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

Perché questa non è la risposta accettata? È di gran lunga il migliore :) – jperelli

+0

la somiglianza nel tuo esempio usa la parola perfetta e non la parola errata usata nella tua clausola where. selezionare somiglianza ('Frist Entry', 'First Entry') => 0.5 –

+0

good point, typo on my end. risolto. grazie per l'avviso :) –

4

@ alexander-mera soluzione funziona alla grande!

Nota: Assicurarsi inoltre di convertire gli spazi in +. Ad esempio, se si sta cercando squire knight.

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

L'uso del '+' non funziona per me su PosgreSQL 9.4.1. Se invece uso "&", funziona come un incantesimo. – facundofarias

0

L'ampia soluzione a questo è utilizzare la funzione ts_rewrite di PG per impostare una tabella alias che funziona per partite alternativi (vedi Query Rewriting). Questo copre casi come il tuo sopra mentre si gestiscono anche casi completamente diversi come la ricerca di tree rat e ottenere risultati per squirrel, ecc.

Dettagli completi e spiegazione a quel collegamento, ma l'essenza di esso è che è possibile impostare una tabella di alias con 2 colonne ts_query e passare una query di tale tabella a con la ricerca, in questo modo:

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'); 

conseguente una domanda finale che assomiglia di più:

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

Questo è simile all'impostazione del thesaurus in PG ma funziona senza richiedere una reindicizzazione completa ogni volta che aggiungi qualcosa. Quando ti imbatti in piccole variazioni ortografiche e casi di "quando cerco questo aspetto mi aspetto risultati come questo" è molto facile aggiungerli al tavolo molto velocemente. È possibile aggiungere più colonne a quella tabella e finché la query basata su ts_rewrite restituisce le 2 colonne to_tsquery previste.

Quando si esamina la documentazione, vengono visualizzati esempi suggeriti per l'ottimizzazione delle prestazioni. C'è un equilibrio tra l'uso del trigramma per la velocità pura e l'uso di vettori/query/riscrittura per la robustezza.

Problemi correlati