Pierre-Louis Le Portz
Développeur chez BAM
github: pleportz // twitter: @leportzp
"tarte"
SELECT ...
recettes
recettes
'Délicieuse tarte' ILIKE '%TARTE%'
Ce n'est pas robuste si l'utilisateur emploie :
De plus c'est LENT !
'Tarte à la tomate' ILIKE '%tarte%'
'Délicieuse tarte' ILIKE '%tartes%'
'Tarte préparée avec amour' ILIKE '%amoureusement%'
SELECT * FROM recipe WHERE title ILIKE '%Tarte%';true
false
true
false
'Tarte & à & la & tomate'
'tart' & 'tomat'
to_tsquery('french', X)
'Tarte | à | la | tomate'
'tart' | 'tomat'
Transforme une chaîne de caractère en une text search query.
On s'affranchit :
Plus généralement, on peut matcher 2 mots qui ont la même racine.
'Tarte à la tomate'
.replace(/ /g, ' & ')
'Amoureusement'
Transforme une chaîne de caractère en un text search vector.
Même comportement que to_tsquery concernant la casse, la racine des mots et les mots inutiles.
'Tarte à la tomate'
'tart':1 'tomat':4
'amour':1
'avec amour'
'amour':2
to_tsvector('french', 'Tarte à la tomate')
to_tsquery('french', 'Tarte & poireau') @@ to_tsvector('french', 'Tarte à la tomate')
'tart':1 'tomat':4
to_tsquery('french', 'Tarte') @@ to_tsvector('french', 'Tarte à la tomate')
true
false
true
to_tsquery('french, 'Tarte | poireau') @@ to_tsvector('french', 'Tarte à la tomate')
'tart'
SELECT title FROM recipe
WHERE to_tsquery('french', 'Tarte & à & la & tomate')
@@
to_tsvector('french', title);SELECT
ts_rank(
to_tsvector('french', 'Tarte à la tomate'),
to_tsquery('french', 'Tarte')
);
= 0.061
SELECT
ts_rank(
to_tsvector('french', 'Tarte à la tomate'),
to_tsquery('french', 'Tarte & à & la & tomate')
);
= 0.097
SELECT
ts_rank(
setweight(to_tsvector('french', 'Tarte à la tomate'), 'A'),
to_tsquery('french', 'Tarte & à & la & tomate')
);
= 0.934
SELECT * FROM recipe
WHERE
to_tsquery('french', 'Tarte & à & la & tomate') @@ to_tsvector('french', title)
ORDER BY
ts_rank(to_tsvector('french', title), to_tsquery('french', 'Tarte & à & la & tomate')) DESC;Table "movie"
Exemple
On veut la liste des films dont le titre ou le résumé contient "eat"
SELECT title FROM movie
WHERE title ILIKE '%eat%' OR overview ILIKE '%eat%';Plein de résultats qui n'ont rien à voir avec "manger" et de plus :
SELECT title FROM movie
WHERE
to_tsquery('english', 'eat')
@@
(to_tsvector('english', title) || to_tsvector('english', overview))
ORDER BY
ts_rank(
setweight(to_tsvector('english', title), 'A')
|| setweight(to_tsvector('english', overview), 'B'),
to_tsquery('english', 'eat')
) DESC;Des résultats pertinents et ordonnés mais
SELECT title FROM movie
WHERE text_search_vector @@ to_tsquery('english', 'eat')
ORDER BY ts_rank(text_search_vector, to_tsquery('english', 'eat')) DESC;UPDATE movie
SET text_search_vector =
setweight(to_tsvector('english', coalesce(title, '')), 'A')
|| setweight(to_tsvector('english', coalesce(overview, '')), 'B');ALTER TABLE movie ADD COLUMN text_search_vector TSVECTOR;Solutions alternatives
Le full-text search de PostgreSQL, c'est :
Vous n'avez pas forcément besoin de sortir l'artillerie lourde.
Avez-vous des questions ?
Ressources utiles :
- Mon repo avec toutes les expérimentations : https://github.com/pleportz/postgresql-sandbox
- Doc PostgreSQL sur les index à utiliser : https://www.postgresql.org/docs/9.1/textsearch-indexes.html
- "Postgres full-text search is Good Enough!" : http://rachbelaid.com/postgres-full-text-search-is-good-enough/
- Pour aller plus loin :
- les MATERIALIZED VIEWS : https://www.postgresql.org/docs/current/rules-materializedviews.html
- zombodb - Making Postgres and Elasticsearch work together : https://github.com/zombodb/zombodb
Pierre-Louis Le Portz
Développeur chez BAM
github: pleportz // twitter: @leportzp