The state of full text search in PostgreSQL 12
fosdem.org
fosdem.org
Slides link: https://fosdem.org/2020/schedule/event/postgresql_the_state_...
Slideshare: https://www.slideshare.net/vyruss000/the-state-of-full-text-...
Video will appear under these URLs after mirrors are synced:
MP4: https://video.fosdem.org/2020/H.2214/postgresql_the_state_of...
WebM: https://video.fosdem.org/2020/H.2214/postgresql_the_state_of...
For the many simple cases where a grep-like search is enough, my little experience with pg was far from satisfying. MySQL is simple to use for this thanks to the awfully named `utf8_mb4_unicode_ci`. With Postgres, searching for "étranger" won't find "Etranger" and "Étranger" unless you put some hard work into your database search system.
Unfortunately, it seems that PG12 won't change much in that aspect. From the link above, you have to create the collation (which is already a heavy restriction) and then "certain operations are not possible with nondeterministic collations, such as pattern matching operations." Looks like uncharted territory.
On a side note, I've always seen Solr, SphinxSearch and such specialized tools used instead of the DB's FTS. The main drawback is keeping the data in sync, but it has many pros.
begin; CREATE OR REPLACE FUNCTION public.insensitive_query(text) RETURNS text AS $func$ SELECT lower(public.unaccent('public.unaccent', CAST($1 AS text))) $func$ LANGUAGE sql IMMUTABLE; ALTER FUNCTION public.insensitive_query(text) OWNER TO [your user that has create rights on the database]; commit;
To use in a query:
WHERE insensitive_query(some_table.name) LIKE insensitive_query('%Étranger%')
What you can do is storing the unaccented, simplified version of a word. Which I'm sure pg already does in a way.
[0] https://www.postgresql.org/docs/12/indexes-expressional.html
CREATE INDEX index_name ON some_table (public.insensitive_query(NAME));
Thank you and I feel a little sheepish I didn't do it first!
strange mix of assumptions there, since searching text strings is Job One of computer search since 'forever' .. if Lucene has done some (search) things well, there is no reason that others do not have a chance to also do those things well. In fact, encourage it, foster it, engage with the process that brings new software capacities ..
Those who wanted Unicode-compliant collation support had already installed it years ago.
https://fosdem.org/2020/schedule/event/postgresql_the_state_...
From my personal experience working with dozens of California tech companies of varying sizes, I'd say around 80% of the time I see ElasticSearch being used when the product could have just used their existing PG cluster (and existing team experience). I.e. so many companies pick up ElasticSearch only because they lack of full-text search capability awareness of PG. Of course, ES does have its advantages, but I feel it's in very specific use-cases.
- having one data store instead of two
- no data store synchronization and related consistency headaches
- the ability to easily join/restrict based on business logic (especially access control)
- easier deployments
- easier qa/local setups
- fewer failure scenarios
- although non-standard, you're dealing with sql and can explain/analyze your queries with tools one already knows if one understands postgres
- the ability to extend my existing (Spring Boot) integration tests to test new searching logic without having to worry about a separate test db.
I've been building a travel blogging platform as a side project that has about ~250 users. Through a couple of choice materialized views to create search documents from a number of different tables, Postgres FTS, and some GIN indices, I was able to make a lighting fast search with intelligent suggestions(ie using user specific restrictions such as follower/following, places a user has posted, etc).
Is my search perfect? No. I could do a better job normalizing diacritics, stemming could be improved, etc. But it was dead simple to implement well enough in a very short amount of time. I'm very happy with my current solution. There's a time and place for a dedicated search solution but it's certainly not in fledgling systems without massive load.
However, that problem sounds nearly intractable to me.
In basis it sounds like you just 'canonicalize' everything. You store, alongside 'Müller', also the string 'mueller' (the rule being: first transform all characters into their asciification, with the asciification of ü being defined as 'ue', then lowercase that.
The problem is, of course.. that searching for 'MULLER' should also find this person. And now we're more into trying to get this done by first deconstructing the unicode into ascii + accent marks and then just wiping out the accent marks.
But once we state that 'mueller', 'muller', and 'müller' need all be 'equal' to each other, where does it end? A last name can be from any of a thousand languages, each with their own unique conventions on how these things are asciified, and any given asciification must match any other. Sounds like a combinatorial explosion.
One could disregard this notion and say that 'muller' does not match 'müller'. However, note that generally, Sjögren's syndrome tends to be asciified as 'Sjogren's syndrome', probably because Sjögren was norwegian and I assume that the norwegian asciification of ö, is o. The german asciification of ö is oe however; and I can't tell from a last name if it is germanic in origin, norwegian in origin, or from the land of fairytales and flying pandas, such as Haägen-Dazs.
Just by thinking about this for a while I have concluded that it's hopeless, but perhaps I'm missing some fancy trick to at least try to make this problem dealable. So far my attempts to look for solutions tend to come down to advice to use unicode normalization and then get rid of all accents, then lowercase it, which obviously doesn't work at all (with that strategy, mueller would not find müller), or heuristic solutions (if a lot matches, good enough), which are their own can of worms.
Is there a solution to this to me seemingly impossible problem?
Your specific example of:
> So, a search for 'mueller' should also find 'Müller'.
Is discussed here:
https://discuss.elastic.co/t/u-umlaut-search-indexing-user-n...
If your question is specifically "How do I use a feature that PostgreSQL 12 doesn't have?" then you've got a different problem.
[EDIT] Refer the modules mentioned on slide 26 - these may help with your indexing challenges within PostgreSQL
The "fancy trick" is basically that search still works pretty well if you just throw away all the vowel sounds - your examples of "mueller" and "Müller" both encode as: MLR
As you said, the problem is fuzzy, so you do... a fuzzy search, return a bunch of results, and let the user pick. If the dataset is too large, then you just let the search include more details. "Mueller from Berlin" or "Muller born on july 7th" are much better queries to find a specific person than a perfectly written "Müller".
In fact, if you were to crack the problem of searching by considering all applicable rules, the system would still be useless because people make mistakes all the time and the Müller guy actually wrote "Miller" while signing up ;P
So it is unreasonable for me to think that I can do a name search (I have exactly the same feature in my product) in a perfect way from scratch just by thinking hard, just because name search is narrower, hence simpler domain (lol).
So I implemented something, got a round of feedback, implemented other forms that users used. My search will display Sjögurd even if user typed in Sjoegurd, despite that the name is Norwegian? Probably, and what’s the downside?
My search will work imperfectly for some lesser known languages, like Georgian? Most certainly, and users from these cultures expect that, they will sigh and deal with it. I know, because I too come from a country with non-latin alphabet and we have lots of different transcriptions into it. It’s ugly, yes. Because the world is ugly :)
- https://www.php.net/manual/en/function.soundex.php
- https://www.php.net/manual/en/function.metaphone.php
PostgreSQL has an extension, fuzzystrmatch, that uses the same algorithms:
- https://www.postgresql.org/docs/12/fuzzystrmatch.html
But it warns that the functions "do not work well with multibyte encodings (such as UTF-8)." So maybe first translate any such characters to ASCII?
Levenshtein distance can be calculated using a trie[2] so it's fast enough for most purposes.
If you wanted to get fancy you could modify the Levenshtein to reduce the distance penalty on accented characters, and/or only apply the Levenshtein calculation within 1 character of an accented character or the equivalent ASCII substitution sequence.
Not at all impossible, you're just not familiar with Unicode. Just use a software that implements UTS #10/UCA. Reply if you want to see code examples for the problems you stated.
I'd definitely give it another, proper go if I got the chance.
ZDB exposes darn near everything ES supports.
Bm25 or tfidf would be welcome