Writing a short article like this is not too good to mention, but the two adjustments when searching with Ransack here are always discrete instructions on Stackoverflow so I wrote this article. I hope you share more of the case you have to edit when using Ransack gem so I can add more in the article.
Create a Rails project and prepare the steps
In this step run the rails new
command, prepare the models and add the Ransack gem. Because I am also lazy, I would like to skip this step. However, I will upload the tables in the database that I will use in this article.
Then you do a form search with filters: search keywords, tags, number of comments. And we get to problem 1.
Problem 1: Search for tags separated by commas
The request you receive is in the tag search form, you can type multiple tags, each separated by a comma. The result returned must contain 1 of those tags.
The analysis here is that we use the relationship as OR. And we will have a way of affecting Ruby’s split()
input.
So the code will be written as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">PostsController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> <span class="token keyword">def</span> index params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:combinator</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"or"</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:groupings</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> custom_words <span class="token operator">=</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:tags_name_cont_any</span> <span class="token punctuation">]</span> custom_words <span class="token punctuation">.</span> <span class="token function">split</span> <span class="token punctuation">(</span> <span class="token string">","</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> each_with_index <span class="token keyword">do</span> <span class="token operator">|</span> word <span class="token punctuation">,</span> index <span class="token operator">|</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:groupings</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> index <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span> tags_name_cont_any <span class="token punctuation">:</span> word <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token variable">@q</span> <span class="token operator">=</span> <span class="token constant">Post</span> <span class="token punctuation">.</span> <span class="token function">ransack</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token variable">@pre_posts</span> <span class="token operator">=</span> <span class="token variable">@q</span> <span class="token punctuation">.</span> result <span class="token punctuation">.</span> <span class="token function">includes</span> <span class="token punctuation">(</span> <span class="token symbol">:tags</span> <span class="token punctuation">)</span> <span class="token variable">@posts</span> <span class="token operator">=</span> <span class="token keyword">if</span> params <span class="token punctuation">[</span> <span class="token symbol">:tag</span> <span class="token punctuation">]</span> <span class="token variable">@posts</span> <span class="token punctuation">.</span> <span class="token function">tagged_with</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:tag</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">page</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:page</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">per</span> <span class="token punctuation">(</span> <span class="token constant">Settings</span> <span class="token punctuation">.</span> max_post_number_per_page <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">@posts</span> <span class="token punctuation">.</span> <span class="token function">page</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:page</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">per</span> <span class="token punctuation">(</span> <span class="token constant">Settings</span> <span class="token punctuation">.</span> max_post_number_per_page <span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
And in the view you set
1 2 3 4 5 | <span class="token erb language-erb"><span class="token delimiter punctuation"><%=</span> search_form_for <span class="token variable">@q</span> <span class="token keyword">do</span> <span class="token operator">|</span> f <span class="token operator">|</span> <span class="token delimiter punctuation">%></span></span> ... <span class="token erb language-erb"><span class="token delimiter punctuation"><%=</span> f <span class="token punctuation">.</span> search_field <span class="token symbol">:tags_name_cont_any</span> <span class="token delimiter punctuation">%></span></span> <span class="token erb language-erb"><span class="token delimiter punctuation"><%</span> <span class="token keyword">end</span> <span class="token delimiter punctuation">%></span></span> |
The search was satisfactory, but looking at the size of the index
looked horrible. So I rewrote the code as follows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <span class="token keyword">class</span> <span class="token class-name">PostsController</span> <span class="token operator"><</span> <span class="token constant">ApplicationController</span> before_action <span class="token symbol">:after_enter_tags_field</span> <span class="token punctuation">,</span> only <span class="token punctuation">:</span> <span class="token symbol">:index</span> <span class="token keyword">def</span> index <span class="token variable">@q</span> <span class="token operator">=</span> <span class="token constant">Post</span> <span class="token punctuation">.</span> <span class="token function">ransack</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token variable">@pre_posts</span> <span class="token operator">=</span> <span class="token variable">@q</span> <span class="token punctuation">.</span> result <span class="token punctuation">.</span> <span class="token function">includes</span> <span class="token punctuation">(</span> <span class="token symbol">:tags</span> <span class="token punctuation">)</span> <span class="token variable">@posts</span> <span class="token operator">=</span> <span class="token keyword">if</span> params <span class="token punctuation">[</span> <span class="token symbol">:tag</span> <span class="token punctuation">]</span> <span class="token variable">@pre_posts</span> <span class="token punctuation">.</span> <span class="token function">tagged_with</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:tag</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">page</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:page</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">per</span> <span class="token punctuation">(</span> <span class="token constant">Settings</span> <span class="token punctuation">.</span> max_post_number_per_page <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token variable">@pre_posts</span> <span class="token punctuation">.</span> <span class="token function">page</span> <span class="token punctuation">(</span> params <span class="token punctuation">[</span> <span class="token symbol">:page</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">per</span> <span class="token punctuation">(</span> <span class="token constant">Settings</span> <span class="token punctuation">.</span> max_post_number_per_page <span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">private</span> <span class="token keyword">def</span> after_enter_tags_field <span class="token keyword">return</span> <span class="token keyword">unless</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:combinator</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"or"</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:groupings</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> custom_words <span class="token operator">=</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:tags_name_cont_any</span> <span class="token punctuation">]</span> custom_words <span class="token punctuation">.</span> <span class="token function">split</span> <span class="token punctuation">(</span> <span class="token string">","</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> each_with_index <span class="token keyword">do</span> <span class="token operator">|</span> word <span class="token punctuation">,</span> index <span class="token operator">|</span> params <span class="token punctuation">[</span> <span class="token symbol">:q</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token symbol">:groupings</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> index <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span> tags_name_cont_any <span class="token punctuation">:</span> word <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
So it looks less scary, although it still seems not very neat
Problem 2: Search based on the number of comments
The next problem is to search and return results of posts that have more comments than a certain number. Now we will use the secondary query and Arel gem
With the Arel gem, we have to add the following:
1 2 | gem <span class="token string">"arel"</span> <span class="token punctuation">,</span> git <span class="token punctuation">:</span> <span class="token string">"https://github.com/rails/arel"</span> |
Then, on the model, we add the following
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">class</span> <span class="token class-name">Post</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> has_many <span class="token symbol">:comments</span> <span class="token punctuation">,</span> dependent <span class="token punctuation">:</span> <span class="token symbol">:destroy</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> ransacker <span class="token symbol">:comments_count</span> <span class="token keyword">do</span> query <span class="token operator">=</span> " <span class="token punctuation">(</span> <span class="token constant">SELECT</span> <span class="token function">COUNT</span> <span class="token punctuation">(</span> comments <span class="token punctuation">.</span> post_id <span class="token punctuation">)</span> <span class="token constant">FROM</span> comments <span class="token constant">WHERE</span> comments <span class="token punctuation">.</span> post_id <span class="token operator">=</span> posts <span class="token punctuation">.</span> id <span class="token constant">GROUP</span> <span class="token constant">BY</span> comments <span class="token punctuation">.</span> post_id <span class="token punctuation">)</span> " <span class="token constant">Arel</span> <span class="token punctuation">.</span> <span class="token function">sql</span> <span class="token punctuation">(</span> query <span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
And we have a property named comments_count
. In ransack there is also _gteq
to compare greater or equal (Greater or Equal). So we just need to combine it
1 2 3 4 5 6 | <%= search_form_for @q do |f| %> ... <%= f.search_field :tags_name_cont_any %> <%= f.number_field :spot_reviews_count_gteq %> <% end %> |
Refer
https://stackoverflow.com/questions/53652254/multi-term-ransack-search-in-same-field-not-working