2017-07-15 2 views
1

Je suis désolé de poser une telle question Noob, mais le postgres documentation sur les vues est clairsemée, et j'ai eu du mal à trouver une bonne réponse. J'essaye d'implémenter Full-Text-Search sur Postgres pour trois tables. Plus précisément, la requête de recherche de l'utilisateur renverrait 1) d'autres noms d'utilisateur correspondants, 2) un message, 3) des sujets.Une vue de plusieurs tables peut-elle être utilisée pour la recherche en texte intégral?

Je suis préoccupé par le fait que l'utilisation d'une vue pour cela pourrait ne pas bien évoluer car il combine trois tables en une seule. Est-ce une préoccupation légitime? Sinon, comment pourrais-je aborder cela autrement?

+0

Vous pouvez faire une telle chose. Assurez-vous que les colonnes (ou expressions) nécessaires sur les tables d'origine ont les bons index. Testez également les plans d'exécution produits lorsque vous interrogez la vue, pour vous assurer qu'ils affichent l'utilisation de l'index. Sinon, ça va marcher, mais vraiment très lentement. Alternativement, vous pouvez avoir une * vue matérialisée *, et avoir les index directement dessus; et assurez-vous de le mettre à jour assez fréquemment. – joanolo

Répondre

2

Ce que vous demandez peut être fait. Pour avoir un exemple pratique (avec seulement deux tables), vous pourriez avoir:

CREATE TABLE users 
(
    user_id SERIAL PRIMARY KEY, 
    username text 
) ; 

-- Index to find usernames 
CREATE INDEX idx_users_username_full_text 
    ON users 
    USING GIN (to_tsvector('english', username)) ;   

CREATE TABLE topics 
(
    topic_id SERIAL PRIMARY KEY, 
    topic text 
) ; 

-- Index to find topics 
CREATE INDEX idx_topics_topic_full_text 
    ON topics 
    USING GIN (to_tsvector('english', topic)) ; 

Voir docs PostgreSQL. sur Controlling Text Search pour une explication de to_tsvector.

... alimenter les tables

INSERT INTO users 
    (username) 
VALUES 
    ('Alice Cooper'), 
    ('Boo Geldorf'), 
    ('Carol Burnet'), 
    ('Daniel Dafoe') ; 

INSERT INTO topics 
    (topic) 
VALUES 
    ('Full text search'), 
    ('Fear of void'), 
    ('Alice in Wonderland essays') ; 

... créer une vue qui combine les valeurs des deux tables

CREATE VIEW search_items AS 
SELECT 
    text 'users' AS origin_table, user_id AS id, to_tsvector('english', username) AS searchable_element 
FROM 
    users 
UNION ALL 
SELECT 
    text 'topics' AS origin_table, topic_id AS id, to_tsvector('english', topic) AS searchable_item 
FROM 
    topics ; 

Nous recherche ce point de vue:

SELECT 
    * 
FROM 
    search_items 
WHERE 
    plainto_tsquery('english', 'alice') @@ searchable_element 

.. et obtenez la réponse suivante (vous devriez ignorer le searchable_element). Vous êtes principalement intéressé par les origin_table et id.

 
origin_table | id | searchable_element    
:----------- | -: | :-------------------------------- 
users  | 1 | 'alic':1 'cooper':2    
topics  | 3 | 'alic':1 'essay':4 'wonderland':3 

Voir Parsing requêtes pour une explication de plainto_tsquery fonction, et aussi @@ operator.


Pour réaliser des index sûrs sont utilisés:

EXPLAIN ANALYZE 
SELECT 
    * 
FROM 
    search_items 
WHERE 
    plainto_tsquery('english', 'alice') @@ searchable_element 
 
| QUERY PLAN                                 | 
| :----------------------------------------------------------------------------------------------------------------------------------------- | 
| Append (cost=12.05..49.04 rows=12 width=68) (actual time=0.017..0.031 rows=2 loops=1)              | 
| -> Bitmap Heap Scan on users (cost=12.05..24.52 rows=6 width=68) (actual time=0.017..0.018 rows=1 loops=1)        | 
|   Recheck Cond: ('''alic'''::tsquery @@ to_tsvector('english'::regconfig, username))             | 
|   Heap Blocks: exact=1                            | 
|   -> Bitmap Index Scan on idx_users_username_full_text (cost=0.00..12.05 rows=6 width=0) (actual time=0.005..0.005 rows=1 loops=1) | 
|    Index Cond: ('''alic'''::tsquery @@ to_tsvector('english'::regconfig, username))            | 
| -> Bitmap Heap Scan on topics (cost=12.05..24.52 rows=6 width=68) (actual time=0.012..0.012 rows=1 loops=1)       | 
|   Recheck Cond: ('''alic'''::tsquery @@ to_tsvector('english'::regconfig, topic))             | 
|   Heap Blocks: exact=1                            | 
|   -> Bitmap Index Scan on idx_topics_topic_full_text (cost=0.00..12.05 rows=6 width=0) (actual time=0.002..0.002 rows=1 loops=1) | 
|    Index Cond: ('''alic'''::tsquery @@ to_tsvector('english'::regconfig, topic))            | 
| Planning time: 0.098 ms                             | 
| Execution time: 0.055 ms                             | 

Les index sont vraiment utilisés (voir Bitmap Index Scan on idx_topics_topic_full_text et Bitmap Index Scan on idx_users_username_full_text).

Vous pouvez vérifier tout à dbfiddle here


REMARQUE: 'english' est le text search configuration choisi d'indexer et de requête. Choisissez le bon pour votre cas. Vous pouvez créer le vôtre si les existants ne répondent pas à vos besoins.

+0

Merci, c'est vraiment utile. La vitesse est une préoccupation cependant. Avez-vous une expérience de la performance avec un grand nombre d'enregistrements, c'est-à-dire 2M +? Dans le cas de la vue matérialisée, en raison de ses limites de mise à jour, est-il sûr de dire que ce n'est pas pratique à utiliser pour la recherche? – dmr07

+0

Il semble que la meilleure solution consiste à créer une table et à déclencher son contenu dérivé tiré d'autres tables. – dmr07

+0

J'ai de l'expérience dans la gamme de 100.000. La recherche est rapide. Le problème consiste normalement à * trier * (en utilisant 'ts_rank') si vous avez des résultats de 100 ou de 1000, car cette fonction demande beaucoup de ressources. Si vous utilisez le déclencheur, je vous suggère de stocker uniquement le résultat 'to_tsvector' dans cette table et de l'indexer. Je ne pense pas que ce sera beaucoup plus rapide, sauf si vous avez plus de 5 tables. – joanolo