<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>baurine (sparkle)</title>
    <link>https://ruby-china.org/baurine</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>在 Rails 中使用 PostgreSQL 的全文搜索功能搜索百万条记录的表的正确姿势</title>
      <description>&lt;p&gt;&amp;nbsp;请教大家一个问题，我现在的 rails 项目中，使用的是 PostgreSQL，有一张表，有一百万条记录，假设表名叫 movies，有 title 和 description 两列。现在我使用全文搜索功能，通过 title 或 description 搜索得到相应的记录。&lt;/p&gt;

&lt;p&gt;第一步我使用 pg_search (&lt;a href="https://github.com/Casecommons/pg_search" rel="nofollow" target="_blank"&gt;https://github.com/Casecommons/pg_search&lt;/a&gt;) 这个 gem，给 Movie model 加上相应的 pg_search_scope:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;PgSearch&lt;/span&gt;
 &lt;span class="n"&gt;pg_search_scope&lt;/span&gt; &lt;span class="ss"&gt;:search_by_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;against: :title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="ss"&gt;tsearch: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="p"&gt;}&lt;/span&gt;
                 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="n"&gt;pg_search_scope&lt;/span&gt; &lt;span class="ss"&gt;:search_by_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;against: :description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="ss"&gt;tsearch: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                     &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="p"&gt;}&lt;/span&gt;
                 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 Movie.search_by_title(search_value) 或 Movie.search_by_desc(search_value)，需要几十秒甚至数分钟钟才能得到结果，如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/f11f2c1d-630e-4b03-9bc2-e829eb28b34c.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;我明白这是由于没有建立索引的缘故，于是我按照 pg_search wiki 上的指南 (&lt;a href="https://github.com/Casecommons/pg_search/wiki/Building-indexes" rel="nofollow" target="_blank"&gt;https://github.com/Casecommons/pg_search/wiki/Building-indexes&lt;/a&gt;)，为这两列建立全文搜索的列及索引。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
  &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tsv_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tsvector&lt;/span&gt;
  &lt;span class="n"&gt;add_index&lt;/span&gt;  &lt;span class="ss"&gt;:movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tsv_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="s1"&gt;'gin'&lt;/span&gt;

  &lt;span class="n"&gt;say_with_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Adding trigger function on movies for updating tsv_title column"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;MIGRATION&lt;/span&gt;&lt;span class="sh"&gt;
      CREATE TRIGGER tsv_for_ep_title BEFORE INSERT OR UPDATE
      ON movies FOR EACH ROW EXECUTE PROCEDURE
      tsvector_update_trigger(tsv_title, 'pg_catalog.simple', title);
&lt;/span&gt;&lt;span class="no"&gt;    MIGRATION&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;add_column&lt;/span&gt; &lt;span class="ss"&gt;:movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tsv_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tsvector&lt;/span&gt;
  &lt;span class="n"&gt;add_index&lt;/span&gt;  &lt;span class="ss"&gt;:movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:tsv_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="s1"&gt;'gin'&lt;/span&gt;

  &lt;span class="n"&gt;say_with_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Adding trigger function on movies for updating tsv_description column"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;MIGRATION&lt;/span&gt;&lt;span class="sh"&gt;
      CREATE TRIGGER tsv_for_ep_description BEFORE INSERT OR UPDATE
      ON movies FOR EACH ROW EXECUTE PROCEDURE
      tsvector_update_trigger(tsv_description, 'pg_catalog.simple', description);
&lt;/span&gt;&lt;span class="no"&gt;    MIGRATION&lt;/span&gt;
    &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改 pg_search_scope:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;PgSearch&lt;/span&gt;
&lt;span class="n"&gt;pg_search_scope&lt;/span&gt; &lt;span class="ss"&gt;:search_by_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;against: :title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="ss"&gt;tsearch: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="ss"&gt;tsvector_column: &lt;/span&gt;&lt;span class="s1"&gt;'tsv_title'&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;pg_search_scope&lt;/span&gt; &lt;span class="ss"&gt;:search_by_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;against: :description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="ss"&gt;tsearch: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="ss"&gt;tsvector_column: &lt;/span&gt;&lt;span class="s1"&gt;'tsv_description'&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如此操作之后，搜索速度大为改进，一般情况下几秒钟可以返回结果，好的时候几百毫秒可以返回结果。如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/a14a992a-0251-448e-b4f2-cbd337ebd1f0.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;那么问题是什么呢，现在我要同时通过 title 和 description 搜索，按照 pg_search 的文档，pg_search_scope 是这么写的：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;pg_search_scope&lt;/span&gt; &lt;span class="ss"&gt;:search_by_title_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="ss"&gt;against: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="ss"&gt;using: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="ss"&gt;tsearch: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="ss"&gt;prefix: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="ss"&gt;tsvector_column: &lt;/span&gt;&lt;span class="sx"&gt;%w(tsv_title tsv_description)&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特别之处在于 tsvector_column 时要声明 tsv_title 和 tsv_description 两列。&lt;/p&gt;

&lt;p&gt;同时通过 title 和 description 搜索后，搜索速度又骤降，需要几十秒才能返回结果。如下图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2019/20f8e398-105a-47a8-b045-84e0fdc54d66.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;所以请求一下大家，针对这种百万级别的表，对多列同时进行全文搜索的正确方法是什么？预期是希望在数秒内得到结果。&lt;/p&gt;

&lt;p&gt;在上例中，我是需要同时对这两列创建一个新的 tsvector 的列并加索引吗？&lt;/p&gt;

&lt;p&gt;谢谢！&lt;/p&gt;</description>
      <author>baurine</author>
      <pubDate>Tue, 26 Feb 2019 17:53:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/38152</link>
      <guid>https://ruby-china.org/topics/38152</guid>
    </item>
    <item>
      <title>借助 Turbolinks 实现不间断的网页音乐播放器</title>
      <description>&lt;p&gt;&lt;a href="http://baurine.github.io/2018/10/02/uninterrupted-audio-player-turbolinks.html" rel="nofollow" target="_blank" title=""&gt;文章链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/baurine/uninterrupted-audio-player" rel="nofollow" target="_blank" title=""&gt;Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://baurine.github.io/assets/images/uninterrupted-audio-player-turbolinks/uninterrupt-audio-player.gif" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://baurine.github.io/assets/images/uninterrupted-audio-player-turbolinks/form-post.gif" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>baurine</author>
      <pubDate>Wed, 10 Oct 2018 09:12:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/37606</link>
      <guid>https://ruby-china.org/topics/37606</guid>
    </item>
    <item>
      <title>在 Rails 中使用 React 并实现 SSR 的一种实践</title>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/baurine/react-in-rails-practice/blob/master/notes/note.md" rel="nofollow" target="_blank" title=""&gt;在 Rails 中使用 React 并实现 SSR 的一种实践&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>baurine</author>
      <pubDate>Sun, 22 Apr 2018 11:33:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/35505</link>
      <guid>https://ruby-china.org/topics/35505</guid>
    </item>
    <item>
      <title>请教如何实现一个 Model 的修改需要 Admin 审核通过才能生效？</title>
      <description>&lt;p&gt;是这样的，现在遇到这样的一个比较常见的需求，在后台管理系统中，有一张表，管理员可以自己修改，也可以授权给别人修改，但别人的修改，不能直接生效，必须经过管理员的审核，管理员需要知道哪些列的内容发生了变化，新旧内容的对比，如果审核通过，就会用新值覆盖旧值。(不需要保存历史版本)&lt;/p&gt;

&lt;p&gt;我想这种需求应该是蛮普遍的吧，那么业界呢有没有比较通行的做法了呢？有没有什么 Gem 可以帮我们做这件事情？&lt;/p&gt;

&lt;p&gt;谢谢！&lt;/p&gt;</description>
      <author>baurine</author>
      <pubDate>Tue, 30 Jan 2018 14:40:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/34973</link>
      <guid>https://ruby-china.org/topics/34973</guid>
    </item>
  </channel>
</rss>
