Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to append prefix match to tsquery in PostgreSQL

I'm trying to utilize the full text search feature of PostgreSQL, particularly when user types in some search text, I would like to display him results with an assumption that the last word is incomplete.

For that purpose the "*" wildcard character needs to be attached to the last tsquery lexeme. E.g. if the user types in "The fat ra" the tsquery should be 'fat' & 'ra':*.

If I append the wildcard to the input string and parse it with plainto_tsquery function then the wildcard is removed plainto_tsquery("The fat ra" || ":*") => 'fat' & 'ra'.

Constructing a tsquery manually with to_tsquery function requires a lot modifications to the string (such as trim spaces and other special characters, replace spaces with the ampersand character) to make the function accept it.

Is there an easier way to do that?

like image 823
yaugenka Avatar asked Feb 07 '19 14:02

yaugenka


1 Answers

You can make the last lexeme in a tsquery a prefix match by casting it to a string, appending ':*', then casting it back to a tsquery:

=> SELECT ((to_tsquery('foo <-> bar')::text || ':*')::tsquery);
      tsquery      
-------------------
 'foo' <-> 'bar':*

For your usecase, you'll want to use <-> instead of & to require the words to be next to each other. Here's a demonstration of how they're different:

=> SELECT 'foo bar baz' @@ tsquery('foo & baz');
 ?column? 
----------
 t
(1 row)

=> SELECT 'foo bar baz' @@ tsquery('foo <-> baz');
 ?column? 
----------
 f
(1 row)

phraseto_tsquery makes it easy to have specify many words that have to be next to each other:

=> SELECT phraseto_tsquery('foo baz');
 phraseto_tsquery 
------------------
 'foo' <-> 'baz'

Putting it all together:

=> SELECT (phraseto_tsquery('The fat ra')::text || ':*')::tsquery;
     tsquery      
------------------
 'fat' <-> 'ra':*

Depending on your needs, a simpler way might be to build a tsquery directly with a string then a cast:

=> SELECT $$'fat' <-> 'ra':*$$::tsquery;
     tsquery      
------------------
 'fat' <-> 'ra':*
like image 183
Michael Brown Avatar answered Sep 23 '22 06:09

Michael Brown