<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Search on Adam Vu</title><link>https://vutg.me/tags/search/</link><description>Recent content in Search on Adam Vu</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 09 Sep 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://vutg.me/tags/search/index.xml" rel="self" type="application/rss+xml"/><item><title>PostgreSQL Full-Text Search and Elasticsearch</title><link>https://vutg.me/posts/postgresql-full-text-search-and-elasticsearch/</link><pubDate>Mon, 09 Sep 2024 00:00:00 +0000</pubDate><guid>https://vutg.me/posts/postgresql-full-text-search-and-elasticsearch/</guid><description>&lt;p&gt;Search is one of the important features users interact with a system. A user types &amp;ldquo;Taylor Swift&amp;rdquo; and expects results in under a second. At small scale this is trivial. At millions of events, it becomes one of the harder problems to get right.&lt;/p&gt;
&lt;p&gt;The naive implementation reaches for a &lt;code&gt;LIKE&lt;/code&gt; query:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;FROM&lt;/span&gt; events
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;WHERE&lt;/span&gt; name &lt;span style="color:#66d9ef"&gt;LIKE&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;%Taylor%&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;OR&lt;/span&gt; description &lt;span style="color:#66d9ef"&gt;LIKE&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;%Taylor%&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works correctly and is easy to write. The problem is performance. The wildcard on both sides of the search term — &lt;code&gt;%Taylor%&lt;/code&gt; — means no standard index can help. The database has no choice but to read every single row in the table, check each one against the pattern, and discard the ones that do not match. This is called a &lt;strong&gt;full table scan&lt;/strong&gt;, and its cost grows linearly with the number of rows. At 10,000 events it is imperceptible. At 10 million events it can take several seconds, and under concurrent load it can saturate the database entirely.&lt;/p&gt;</description></item></channel></rss>