Class: Decidim::ResourceSearch

Inherits:
Searchlight::Search
  • Object
show all
Defined in:
app/services/decidim/resource_search.rb

Overview

This is the base class to be used by other search services. Searchlight documentation: github.com/nathanl/searchlight

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scope, options = {}) ⇒ ResourceSearch

Initialize the Searchlight::Search base class with the options provided.

scope - The scope used to create the base query options - A hash of options to modify the search. These options will be

converted to methods by SearchLight so they can be used on filter
methods. (Default {})


15
16
17
18
19
20
21
# File 'app/services/decidim/resource_search.rb', line 15

def initialize(scope, options = {})
  super(options)
  @scope = scope
  @user = options[:current_user] || options[:user]
  @component = options[:component]
  @organization = options[:organization] || component&.organization
end

Instance Attribute Details

#componentObject (readonly)

Returns the value of attribute component.



7
8
9
# File 'app/services/decidim/resource_search.rb', line 7

def component
  @component
end

#organizationObject (readonly)

Returns the value of attribute organization.



7
8
9
# File 'app/services/decidim/resource_search.rb', line 7

def organization
  @organization
end

#userObject (readonly)

Returns the value of attribute user.



7
8
9
# File 'app/services/decidim/resource_search.rb', line 7

def user
  @user
end

Class Method Details

.text_search_fields(*fields) ⇒ Object

Public: Companion method to ‘search_search_text` which defines the attributes where we should search for text values in a model.



25
26
27
28
# File 'app/services/decidim/resource_search.rb', line 25

def self.text_search_fields(*fields)
  @text_search_fields = fields if fields.any?
  @text_search_fields
end

Instance Method Details

#base_queryObject

Creates the SearchLight base query. Check if the option component was provided.



47
48
49
50
51
# File 'app/services/decidim/resource_search.rb', line 47

def base_query
  raise "Missing component" unless component

  @scope.where(component: component)
end

#resultsObject

We overwrite the ‘results` method to ensure we only return unique results. We can’t use ‘#uniq` because it returns an Array and we’re adding scopes in the controller, and ‘#distinct` doesn’t work here because in the later scopes we’re ordering by ‘RANDOM()` in a DB level, and `SELECT DISTINCT` doesn’t work with ‘RANDOM()` sorting, so we need to perform two queries.

The correct behaviour is backed by tests.



97
98
99
# File 'app/services/decidim/resource_search.rb', line 97

def results
  base_query.model.where(id: super.pluck(:id))
end

#search_category_idObject

Handle the category_id filter



54
55
56
57
58
59
60
# File 'app/services/decidim/resource_search.rb', line 54

def search_category_id
  return query if category_ids.include?("all")

  query
    .includes(:categorization)
    .where(decidim_categorizations: { decidim_category_id: all_category_ids })
end

#search_originObject

Handle the origin filter.



82
83
84
85
86
87
# File 'app/services/decidim/resource_search.rb', line 82

def search_origin
  renamed_origin = Array(origin).map do |search_value|
    "#{search_value}_origin"
  end
  apply_scopes(%w(official_origin citizens_origin user_group_origin meeting_origin), renamed_origin)
end

#search_scope_idObject

Handles the scope_ids filter. When we want to show only those that do not have a scope_ids set, we cannot pass an empty String or nil because Searchlight will automatically filter out these params, so the method will not be used. Instead, we need to pass a fake ID and then convert it inside. In this case, in order to select those elements that do not have a scope_ids set we use ‘“global”` as parameter, and in the method we do the needed changes to search properly.



69
70
71
72
73
74
75
76
77
78
79
# File 'app/services/decidim/resource_search.rb', line 69

def search_scope_id
  return query if scope_ids.include?("all")

  clean_scope_ids = scope_ids

  conditions = []
  conditions << "#{query.model_name.plural}.decidim_scope_id IS NULL" if clean_scope_ids.delete("global")
  conditions.concat(["? = ANY(decidim_scopes.part_of)"] * clean_scope_ids.count) if clean_scope_ids.any?

  query.includes(:scope).references(:decidim_scopes).where(conditions.join(" OR "), *clean_scope_ids.map(&:to_i))
end

#search_search_textObject

Handle the search_text filter. We have to cast the JSONB columns into a ‘text` type so that we can search.



32
33
34
35
36
37
38
39
40
41
42
43
# File 'app/services/decidim/resource_search.rb', line 32

def search_search_text
  return query unless self.class.text_search_fields.any?

  fields = self.class.text_search_fields.dup

  text_query = query.where(localized_search_text_in("#{query.model_name.plural}.#{fields.shift}"), text: "%#{search_text}%")

  fields.each do |field|
    text_query = text_query.or(query.where(localized_search_text_in("#{query.model_name.plural}.#{field}"), text: "%#{search_text}%"))
  end
  text_query
end