Class: Search

Inherits:
Struct
  • Object
show all
Defined in:
backend/app/model/search.rb,
frontend/app/models/search.rb,
public/app/models/search.rb

Constant Summary collapse

@@BooleanOpts =
[]
@@SortOpts =

we create all the possible sort options here, then refine them according to what’s being sorted

{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ Search

We take params either as a Hash or ActionController::Parameters object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'public/app/models/search.rb', line 32

def initialize(params = {})
#    Rails.logger.debug("Initializing: #{params}")
  %w(q op field from_year to_year filter_fields filter_values filter_q recordtypes ).each do |f|
    if params.is_a?(Hash)
      self[f.to_sym] = params[f.to_sym] || []
    else
      self[f.to_sym] = params.fetch(f.to_sym, [])
    end
  end
  %w(limit filter_from_year filter_to_year).each do |f|
    if params.is_a?(Hash)
      self[f.to_sym] = params[f.to_sym] || ''
    else
      self[f.to_sym] = params.fetch(f.to_sym, '')
    end
  end
  self[:q].each_with_index do |q, i|
    self[:q][i] = '*' if q.blank?
  end
  self[:sort] = params.fetch('sort', nil)
  self[:dates_searched] = have_contents?(from_year) || have_contents?(to_year)
  self[:dates_within] = self[:text_within] = false
end

Instance Attribute Details

#dates_searchedObject

Returns the value of attribute dates_searched

Returns:

  • (Object)

    the current value of dates_searched



1
2
3
# File 'public/app/models/search.rb', line 1

def dates_searched
  @dates_searched
end

#dates_withinObject

Returns the value of attribute dates_within

Returns:

  • (Object)

    the current value of dates_within



1
2
3
# File 'public/app/models/search.rb', line 1

def dates_within
  @dates_within
end

#fieldObject

Returns the value of attribute field

Returns:

  • (Object)

    the current value of field



1
2
3
# File 'public/app/models/search.rb', line 1

def field
  @field
end

#filter_fieldsObject

Returns the value of attribute filter_fields

Returns:

  • (Object)

    the current value of filter_fields



1
2
3
# File 'public/app/models/search.rb', line 1

def filter_fields
  @filter_fields
end

#filter_from_yearObject

Returns the value of attribute filter_from_year

Returns:

  • (Object)

    the current value of filter_from_year



1
2
3
# File 'public/app/models/search.rb', line 1

def filter_from_year
  @filter_from_year
end

#filter_qObject

Returns the value of attribute filter_q

Returns:

  • (Object)

    the current value of filter_q



1
2
3
# File 'public/app/models/search.rb', line 1

def filter_q
  @filter_q
end

#filter_to_yearObject

Returns the value of attribute filter_to_year

Returns:

  • (Object)

    the current value of filter_to_year



1
2
3
# File 'public/app/models/search.rb', line 1

def filter_to_year
  @filter_to_year
end

#filter_valuesObject

Returns the value of attribute filter_values

Returns:

  • (Object)

    the current value of filter_values



1
2
3
# File 'public/app/models/search.rb', line 1

def filter_values
  @filter_values
end

#from_yearObject

Returns the value of attribute from_year

Returns:

  • (Object)

    the current value of from_year



1
2
3
# File 'public/app/models/search.rb', line 1

def from_year
  @from_year
end

#limitObject

Returns the value of attribute limit

Returns:

  • (Object)

    the current value of limit



1
2
3
# File 'public/app/models/search.rb', line 1

def limit
  @limit
end

#opObject

Returns the value of attribute op

Returns:

  • (Object)

    the current value of op



1
2
3
# File 'public/app/models/search.rb', line 1

def op
  @op
end

#qObject

Returns the value of attribute q

Returns:

  • (Object)

    the current value of q



1
2
3
# File 'public/app/models/search.rb', line 1

def q
  @q
end

#recordtypesObject

Returns the value of attribute recordtypes

Returns:

  • (Object)

    the current value of recordtypes



1
2
3
# File 'public/app/models/search.rb', line 1

def recordtypes
  @recordtypes
end

#search_statementObject

Returns the value of attribute search_statement

Returns:

  • (Object)

    the current value of search_statement



1
2
3
# File 'public/app/models/search.rb', line 1

def search_statement
  @search_statement
end

#sortObject

Returns the value of attribute sort

Returns:

  • (Object)

    the current value of sort



1
2
3
# File 'public/app/models/search.rb', line 1

def sort
  @sort
end

#text_withinObject

Returns the value of attribute text_within

Returns:

  • (Object)

    the current value of text_within



1
2
3
# File 'public/app/models/search.rb', line 1

def text_within
  @text_within
end

#to_yearObject

Returns the value of attribute to_year

Returns:

  • (Object)

    the current value of to_year



1
2
3
# File 'public/app/models/search.rb', line 1

def to_year
  @to_year
end

Class Method Details

.all(repo_id, criteria, context_criteria = {}) ⇒ Object

:context_criteria - added to criteria for building the query, but hidden from user :sorting - ignore repo preferences and use provided sort for results (i.e. for typeaheads)



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'frontend/app/models/search.rb', line 14

def self.all(repo_id, criteria, context_criteria = {})
  context_criteria.each do |k, v|
    if v.is_a? Array
      criteria[k] ||= []
      criteria[k] += v
    else
      criteria[k] = v
    end
  end

  build_filters(criteria)
  criteria["page"] = 1 if not criteria.has_key?("page")
  criteria["sort"] ||= sort(criteria["type[]"] || (criteria["filter_term[]"] || []).collect { |term| ASUtils.json_parse(term)['primary_type'] }.compact)

  search_data = JSONModel::HTTP::get_json("/repositories/#{repo_id}/search", criteria)

  #If the criteria contains a 'blank_facet_query_fields' field,
  #we want to add a facet to filter on items WITHOUT an entry in the facet
  if (criteria.has_key?("blank_facet_query_fields"))
    blank_facet_query = ""
    criteria["blank_facet_query_fields"].each {|query_field|
      blank_facet_query = "-" + query_field + ":*"
      sub_criteria = criteria.clone
      if (sub_criteria.has_key?("q") && sub_criteria["q"] != blank_facet_query)
        sub_criteria["q"] = criteria["q"] + " AND " + blank_facet_query
      else
        sub_criteria["q"] = blank_facet_query
      end

      search_data_with_blank_facet = JSONModel::HTTP::get_json("/repositories/#{repo_id}/search", sub_criteria)
      if (!search_data["facets"]["facet_fields"].has_key?(query_field))
        search_data["facets"]["facet_fields"][query_field] = ["none", search_data_with_blank_facet["total_hits"]]
      else
        search_data["facets"]["facet_fields"][query_field] << "none"
        search_data["facets"]["facet_fields"][query_field] << search_data_with_blank_facet["total_hits"]
      end
    }

  end

  SearchResultData.new(search_data, criteria, context_criteria)
end

.for_type(repo_id, type, criteria) ⇒ Object



6
7
8
9
10
# File 'frontend/app/models/search.rb', line 6

def self.for_type(repo_id, type, criteria)
  criteria['type[]'] = Array(type)

  Search.all(repo_id, criteria)
end

.get_boolean_optsObject



5
6
7
8
9
10
11
12
# File 'public/app/models/search.rb', line 5

def Search.get_boolean_opts
  if @@BooleanOpts.empty?
    %w(AND OR NOT).each do |opt|
      @@BooleanOpts.push([ I18n.t("advanced_search.operator.#{opt}"), opt])
    end
  end
  @@BooleanOpts
end

.get_sort_optsObject



17
18
19
20
21
22
23
24
25
26
27
28
# File 'public/app/models/search.rb', line 17

def Search.get_sort_opts
  if @@SortOpts.empty?
    @@SortOpts['relevance'] = [I18n.t('search_sorting.relevance'), ""]
    # the things we do for I18n!
    %w(title_sort year_sort identifier).each do |type|
      %w(asc desc).each do |dir|
        @@SortOpts["#{type}_#{dir}"] = [I18n.t('search_sorting.sorting', :type => I18n.t("search_sorting.#{type}"), :direction => I18n.t("search_sorting.#{dir}")), "#{type} #{dir}"]
      end
    end
  end
  @@SortOpts
end

.global(criteria, type) ⇒ Object



58
59
60
61
62
63
64
65
66
# File 'frontend/app/models/search.rb', line 58

def self.global(criteria, type)
  build_filters(criteria)

  criteria["page"] = 1 if not criteria.has_key?("page")

  search_data = JSONModel::HTTP::get_json("/search/#{type}", criteria)
  search_data[:type] = type
  SearchResultData.new(search_data, criteria)
end

.record_type_counts(record_types, for_repo_uri = nil) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'backend/app/model/search.rb', line 108

def self.record_type_counts(record_types, for_repo_uri = nil)
  show_suppressed = !RequestContext.get(:enforce_suppression)
  show_published_only = RequestContext.get(:current_username) === User.PUBLIC_USERNAME

  result = {}

  repos_of_interest = if for_repo_uri
                        [for_repo_uri]
                      else
                        Repository.filter(:hidden => 0).select(:id).map do |row|
                          repo_id = row[:id]
                          JSONModel.JSONModel(:repository).uri_for(repo_id)
                        end
                      end

  repos_of_interest.each do |repo_uri|
    result[repo_uri] ||= {}

    record_types.each do |record_type|
      boolean_query = JSONModel.JSONModel(:boolean_query)
                      .from_hash('op' => 'AND',
                                 'subqueries' => [
                                   JSONModel.JSONModel(:boolean_query).from_hash('op' => 'OR',
                                                                                 'subqueries' => [
                                                                                   JSONModel.JSONModel(:field_query)
                                                                                   .from_hash('field' => 'used_within_repository',
                                                                                              'value' => repo_uri,
                                                                                              'literal' => true).to_hash,
                                                                                   JSONModel.JSONModel(:field_query)
                                                                                   .from_hash('field' => 'repository',
                                                                                              'value' => repo_uri,
                                                                                              'literal' => true).to_hash
                                                                                 ]),
                                   JSONModel.JSONModel(:field_query)
                                     .from_hash('field' => 'types',
                                                'value' => record_type,
                                                'literal' => true).to_hash,
                                   JSONModel.JSONModel(:field_query)
                                     .from_hash('field' => 'published',
                                                'value' => 'true',
                                                'literal' => true).to_hash

                                 ])

      query = Solr::Query.create_advanced_search(JSONModel.JSONModel(:advanced_query).from_hash('query' => boolean_query))
      query.pagination(1, 1).
            show_suppressed(show_suppressed).
            show_published_only(show_published_only)

      hits = Solr.search(query)

      result[repo_uri][record_type] = hits['total_hits']
    end
  end

  if for_repo_uri
    # We're just targeting a single repo
    result.values.first
  else
    result
  end
end

.records_for_uris(uris, resolve = []) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'backend/app/model/search.rb', line 76

def self.records_for_uris(uris, resolve = [])
  show_suppressed = !RequestContext.get(:enforce_suppression)
  show_published_only = RequestContext.get(:current_username) === User.PUBLIC_USERNAME

  boolean_query = JSONModel.JSONModel(:boolean_query)
                  .from_hash('op' => 'OR',
                             'subqueries' => uris.map {|uri|
                               field = uri.end_with?('#pui') ? 'id' : 'uri'
                               JSONModel.JSONModel(:field_query)
                                 .from_hash('field' => field,
                                            'value' => uri,
                                            'literal' => true)
                                 .to_hash
                             })

  query = Solr::Query.create_advanced_search(JSONModel.JSONModel(:advanced_query).from_hash('query' => boolean_query))

  query.pagination(1, uris.length).
        show_suppressed(show_suppressed).
        show_published_only(show_published_only)

  results = Solr.search(query)

  resolver = SearchResolver.new(resolve)
  resolver.resolve(results)

  # Keep the ordering that we were passed in our list of URIs
  results['results'].sort_by! {|result| uris.index(result['uri'])}

  results
end

.search(params, repo_id) ⇒ Object



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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'backend/app/model/search.rb', line 5

def self.search(params, repo_id)
  show_suppressed = !RequestContext.get(:enforce_suppression)
  show_published_only = RequestContext.get(:current_username) === User.PUBLIC_USERNAME

  Log.debug("backend search received params: #{params.inspect}")

  query = if params[:q]
            Solr::Query.create_keyword_search(params[:q])
          elsif params[:aq] && params[:aq]['query']
            Solr::Query.create_advanced_search(params[:aq], protect_unpublished: show_published_only)
          else
            Solr::Query.create_match_all_query
          end

  query.pagination(params[:page], params[:page_size]).
    set_repo_id(repo_id).
    set_record_types(params[:type]).
    show_suppressed(show_suppressed).
    show_published_only(show_published_only).
    set_excluded_ids(params[:exclude]).
    set_filter(params[:filter]).
    set_filter_queries(params[:filter_query]).
    set_facets(params[:facet], (params[:facet_mincount] || 0)).
    set_sort(params[:sort]).
    set_root_record(params[:root_record]).
    highlighting(params[:hl]).
    set_writer_type( params[:dt] || "json" )

  query.remove_csv_header if ( params[:dt] == "csv" and params[:no_csv_header] )
  query.limit_fields_to(params[:fields]) if params[:fields] && (AppConfig[:limit_csv_fields] || params[:dt] != "csv")

  results = Solr.search(query)

  if params[:resolve]
    # As with the ArchivesSpace API, resolving a field gives a way of
    # returning linked records without having to make multiple queries.
    #
    # In the case of searching, a resolve parameter like:
    #
    #      &resolve[]=repository:id
    #
    # will take the (stored) field value for "repository" and search for
    # that value in the "id" field of other Solr documents.  Any document(s)
    # returned will be inserted into the search response under the key
    # "_resolved_repository".
    #
    # Since you might want to resolve a multi-valued field, we'll use the
    # following format:
    #
    #      "_resolved_myfield": {
    #          "/stored/value/1": [{... matched record 1...}, {... matched record 2...}],
    #          "/stored/value/2": [{... matched record 1...}, {... matched record 2...}]
    #      }
    #
    # To avoid the inlined resolved records being unreasonably large, you can
    # also specify a custom resolver to be used when rendering the record.
    # For example, the query:
    #
    #      &resolve[]=resource:id@compact_resource
    #
    # will use the "compact_resource" resolver to render the inlined resource
    # records.  This is defined by `search_resolver_compact_resource.rb`.  You
    # can define as many of these classes as needed, and they'll be available
    # via the API in this same way.
    resolver = SearchResolver.new(params[:resolve])
    resolver.resolve(results)
  end

  results
end

.search_csv(params, repo_id) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'backend/app/model/search.rb', line 171

def self.search_csv( params, repo_id )
  # first let's get a json response with the number of pages
  p = params.dup
  p[:dt] = "json"
  result = search(p, repo_id)
  total_pages = result["last_page"].to_i || 2
  page = 2 # we start on the second page bc the first will have headers

  Enumerator.new do |y|
    # we get page 1 of csv w headers
    y << search(params, repo_id)
    params[:no_csv_header] = true
    while page <= total_pages
      params[:page] = page
      y << search(params, repo_id)
      page +=1
    end
  end
end

Instance Method Details

#allow_dates?Boolean

Returns:

  • (Boolean)


74
75
76
77
78
79
80
81
# File 'public/app/models/search.rb', line 74

def allow_dates?
  allow = true
  limit.split(",").each do |type|
    allow = false if type == 'subject'
    allow = false if type.start_with?('agent')
  end
  allow
end

#filters_blank?Boolean

Returns:

  • (Boolean)


56
57
58
# File 'public/app/models/search.rb', line 56

def filters_blank?
  filter_from_year.blank? && filter_to_year.blank? && filter_q.all?(&:blank?)
end

#get_filter_q_arr(url = nil) ⇒ Object



96
97
98
99
100
101
102
103
104
# File 'public/app/models/search.rb', line 96

def get_filter_q_arr(url = nil)
  fqa = []
  self[:filter_q].each do |v|
    Rails.logger.debug("v: #{v} CGI-escaped: #{CGI.escape(v)}")
    uri = (url)? url.sub("&filter_q[]=#{CGI.escape(v)}", "") : ''
    fqa.push({'v' => v, 'uri' => uri})
  end
  fqa
end

#get_filter_q_paramsObject



88
89
90
91
92
93
94
# File 'public/app/models/search.rb', line 88

def get_filter_q_params
  params = ''
  self[:filter_q].each do |v|
    params += "&filter_q[]=#{CGI.escape(v)}"
  end
  params
end

#has_query?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'public/app/models/search.rb', line 60

def has_query?
  have_contents?(q)
end

#have_contents?(year_array) ⇒ Boolean

Returns:

  • (Boolean)


64
65
66
67
68
69
70
71
72
# File 'public/app/models/search.rb', line 64

def have_contents?(year_array)
  have = false
  year_array.each do |year|
    unless year.strip == ''
      have = true
    end
  end
  have
end

#search_dates_within?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'public/app/models/search.rb', line 83

def search_dates_within?
  dates_within && !dates_searched && filter_from_year.empty? && filter_to_year.empty?
end