Class: MultiSolr::SingleCoreHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/multi_solr/single_core_handler.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(solr_url, core_name, options = nil) ⇒ SingleCoreHandler

Erzeugen neuen Core-Handler Parameter:

solr_url :               String mit der Base-Solr-Url (ohne Core-Anteil!)
core_name :              der zu verwendende SOLR-Core
options: optionaler Hash mit folgenden Werten:
  :result_class :           die zu nutzende Klasse für das Suchergebnis, default ist MultiSolr:SearchResult
  :facet_enum_fields :      Array mit den Namen der Felder,
                            die die Facet-Enum-Methode für Facets nutzen
                            (Felder mit kleinen Wertebereichen)
  :default_search_options : Hash mit weiteren Optionen für Query Anfragen:
      :fieldlist            Field-List (siehe Solr-Doku fl-Parameter), default ist alle Felder
      :facets_only          wenn true, dann nur Facets ermittlen
      :without_facets       wenn true, dann auf Facets verzichten
      :result_format        Rückgabe-Format (json, xml),
                            wenn nicht gesetzt wird "ruby" geliefert;
                            Ist es gesetzt wird das Ergebnis nicht geparst (also raw als String zurückgegeben)


30
31
32
33
34
35
36
37
38
39
40
# File 'lib/multi_solr/single_core_handler.rb', line 30

def initialize solr_url, core_name, options=nil
  @solr_url = solr_url || raise("No 'solr_url' given!")
  @core_name = core_name
  if options
    options.each do |k,v|
      self.send("#{k}=", v)
    end
  end
  @result_class ||= MultiSolr::SearchResult
  @default_search_options ||= {}
end

Instance Attribute Details

#core_nameObject

der zu verwendende SOLR-Core



8
9
10
# File 'lib/multi_solr/single_core_handler.rb', line 8

def core_name
  @core_name
end

#default_search_optionsObject

Hash mit weiteren Optionen für Query Anfragen (fieldlist,..) siehe params bei der Methode search



11
12
13
# File 'lib/multi_solr/single_core_handler.rb', line 11

def default_search_options
  @default_search_options
end

#facet_enum_fieldsObject

Array mit den Namen der Felder, die die Facet-Enum-Methode für Facets nutzen (Felder mit kleinen Wertebereichen)



10
11
12
# File 'lib/multi_solr/single_core_handler.rb', line 10

def facet_enum_fields
  @facet_enum_fields
end

#result_classObject

die zu nutzende Klasse für das Suchergebnis



9
10
11
# File 'lib/multi_solr/single_core_handler.rb', line 9

def result_class
  @result_class
end

#solr_urlObject (readonly)

String mit der Base-Solr-Url (ohne Core-Anteil!)



7
8
9
# File 'lib/multi_solr/single_core_handler.rb', line 7

def solr_url
  @solr_url
end

Instance Method Details

#build_solr_params(solr_search_request, options) ⇒ Object

Bilden der SOLR-Parameter für eine SOLR-Anfrage params:

solr_search_request: die Suchanfrage als SolrSearchRequest-Instance
options: siehe search

returns: Hash mit den SOLR-Parametern



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/multi_solr/single_core_handler.rb', line 243

def build_solr_params solr_search_request, options
  config = options[:config]
  solr_params = {}
  solr_params['stats.field'] = []

  fq_string = self.force_query_params options[:context]
  fq_string = join_params(fq_string) if fq_string.is_a?(Hash) # wegen abwärtskompatibilität (Früher lieferte die Funktion force_query_params eine Hash)
  solr_params[:fq] = fq_string if fq_string

  q = solr_search_request.build_query config
  q = "*:*" if q.blank? # wenn keine Query angegeben ist, dann nach allem Suchen
  solr_params[:q] = q

  # Fieldlist
  if q['_val_:']
    # dann enthält die Query eine query-function
    # Der Wert dieser wird immer im field "score" abgelegt
    # daher dieses Feld zur Feldliste hinzufügen
    if options[:fieldlist]
      options[:fieldlist] << ',score' unless self.fieldlist[',score']
    else
      options[:fieldlist] = '*,score'
    end
  end
  solr_params[:fl] = options[:fieldlist] if options.key? :fieldlist

  # Sortierung
  if solr_search_request.sorts && !solr_search_request.sorts.empty?
    solr_search_request.sorts.delete_if{|s| s.blank?}
    solr_params[:sort] = solr_search_request.sorts.map{|s| s =~ /\s(asc|desc)$/ ? s : "#{s} asc"}.join(',')
  end

  # Facets
  if !options[:without_facets] && solr_search_request.facets
    parse_facets solr_search_request, solr_params
  end

  # Stats
  if solr_search_request.stats_fields && !solr_search_request.stats_fields.empty?
    s_fields = solr_search_request.stats_fields.select{|f| !f.blank?}
    unless s_fields.empty?
      solr_params[:stats] = true
      solr_params['stats.field'] += s_fields
    end
  end
  solr_params['stats.field'].map!{|f| f.to_s}.uniq!

  # Gruppierung
  if !solr_search_request.group_field.blank?
    solr_params[:group] = true
    solr_params['group.field'] = solr_search_request.group_field
    solr_params['group.ngroups'] = true
    solr_params['group.limit'] = solr_search_request.group_size || 1
    solr_params['group.truncate'] = true if solr_search_request.group_truncate
  end


  # Paginierung
  if options[:facets_only]
    solr_params[:rows] = 0
  else
    solr_params[:rows] = solr_search_request.page_size
    solr_params[:start] = (solr_search_request.page-1) * solr_search_request.page_size
  end

  # Ausgabe-Format
  solr_params[:wt] = options[:result_format] if options.key? :result_format

  if MultiSolr.logger.debug?
    MultiSolr.logger.debug "SolrSearch#build_solr_params: #{self.inspect}\n\tSEARCH-REQUEST=#{solr_search_request.inspect}\n\t=> SOLR_PARAMS=#{solr_params.inspect}\n"
  end
  solr_params
end

#cached_list_possible_values(fieldname, context = nil, config = nil, expires_in = 3.hours) ⇒ Object

Ermittelt und cached die möglichen Werte der als pre_cache_value_fields definierten Felder Diese werden im Rails-Cache zwischengespeichert Parameter:

fieldname: Name des Feldes (als Symbol)
context:   optionales Object mit weiteren Context-Informationen, dieser wird an den Force-Query-Builder übergeben
config:    Config-Data, used as optional parameter for query-build
expires_in:  optional, Gültigkeit des Cache-Wertes in Sekunden, default ist 3 Stunden


138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/multi_solr/single_core_handler.rb', line 138

def cached_list_possible_values fieldname, context=nil, config=nil, expires_in=3.hours
  cache_key = "solr-#{self.core_name}-#{context}-#{fieldname}-values"
  values = MultiSolr.cache.read(cache_key)
  if values.nil?
    # dann sind noch gar keine Werte gecached => diese holen
    values = list_possible_values fieldname, context, nil, config
    # und nun im Cache ablegen
    MultiSolr.logger.debug "#{self.class.name}.cached_list_possible_values: write in cache '#{cache_key}' => #{values.inspect}" if MultiSolr.logger.debug?
    MultiSolr.cache.write(cache_key, values, :expires_in => expires_in)
  end
  values
end

#cached_list_possible_values_with_count(fieldname, context = nil, config = nil, expires_in = 3.hours) ⇒ Object

Ermittelt und cached die möglichen Werte mit Anzahl der als pre_cache_value_fields definierten Felder Diese werden im Rails-Cache zwischengespeichert Parameter:

fieldname: Name des Feldes (als Symbol)
context:   optionales Object mit weiteren Context-Informationen, dieser wird an den Force-Query-Builder übergeben
config:    Config-Data, used as optional parameter for query-build
expires_in:  optional, Gültigkeit des Cache-Wertes in Sekunden, default ist 3 Stunden


159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/multi_solr/single_core_handler.rb', line 159

def cached_list_possible_values_with_count fieldname, context=nil, config=nil, expires_in=3.hours
  cache_key = "solr-#{self.core_name}-#{context}-#{fieldname}-values-with-count"
  values = MultiSolr.cache.read(cache_key)
  if values.nil?
    # dann sind noch gar keine Werte gecached => diese holen
    values = list_possible_values_with_count fieldname, context, nil, config
    # und nun im Cache ablegen
    MultiSolr.logger.debug "#{self.class.name}.cached_list_possible_values_with_count: write in cache '#{cache_key}' => #{values.inspect}" if MultiSolr.logger.debug?
    MultiSolr.cache.write(cache_key, values, :expires_in => expires_in)
  end
  values
end

#get_doc_by_id(id, id_field_name = :id) ⇒ Object

liefert einzelnes Solr-Dokument an Hand der Id Parameter:

id:             die gewünschte Id (Integer oder String)
id_field_name : optional, der Name des ID-Fields, default ist 'id'


200
201
202
203
204
205
# File 'lib/multi_solr/single_core_handler.rb', line 200

def get_doc_by_id(id, id_field_name=:id)
  solr_result = solr_connection.get 'select', :params => {:q => "#{id_field_name}:#{id}", :rows => 1}
  docs = solr_result['response']['docs']
  return nil if docs.nil? || docs.empty?
  docs.first
end

#import_status(solr_core_import_handler_propfile_path) ⇒ Object

ermitteln Zeitstempel des letzten Datenimports



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/multi_solr/single_core_handler.rb', line 174

def import_status solr_core_import_handler_propfile_path
  MultiSolr.cache.fetch("Solr.import_status.#{self.core_name}.#{solr_core_import_handler_propfile_path}", :expires_in => 1.hours) do
    result = I18n.t(:unknown, :default => 'n.a.')
    begin
      raise("Solr-Import-Status-Propertiesfile not exist") unless File.exist?(solr_core_import_handler_propfile_path)
      matcher = /^last_index_time=(.*)$/
      data = File.read(solr_core_import_handler_propfile_path)
      match = matcher.match(data)
      if match
        result = match[1]
        result.gsub!('\\', '') # Zeit enthält \ vor :
        result = result[0...-3] # Sekunden entfernen
      end
    rescue => ex
      MultiSolr.logger.warn "SolrSearch.import_status: source=#{solr_core_import_handler_propfile_path}, error=#{ex.message}\n\t"+ex.backtrace.join("\n\t")
    end
    MultiSolr.logger.info "SolrSearch.import_status: #{solr_core_import_handler_propfile_path}, result=#{result}"
    result
  end
end

#list_possible_values(fieldname, context = nil, search_request = nil, config = nil) ⇒ Object

Liefert zu dem spezifizierten Kontext und Feld die möglichen Werte Parameter:

fieldname: Name des Feldes (als Symbol)
context:   optionales Object mit weiteren Context-Informationen, dieser wird an den Force-Query-Builder übergeben
search_request: optional bestehendes SearchRequest (für DrillDown-Funktionalität)
config:    Config-Data, used as optional parameter for query-build

Beispiel:

searcher.list_possible_values :lkz, :whs_id => 5


73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/multi_solr/single_core_handler.rb', line 73

def list_possible_values fieldname, context=nil, search_request=nil, config=nil
  value_pair_data = list_possible_values_with_count fieldname, context, search_request, config
  values = []
  if value_pair_data
    # es werden nur die Werte benötigt und deshalb aus den [value,count]-Paaren entnommen
    value_pair_data.each do |value_count_pair|
      values << value_count_pair[0]
    end
  end
  # Ergebnis ist ein Array mit Werten
  values
end

#list_possible_values_with_count(fieldname, context = nil, search_request = nil, config = nil) ⇒ Object

Liefert zu dem spezifizierten Kontext und Feld die möglichen Werte und die dazugehörige Anzahl an Treffern. Parameter:

fieldname: Name des Feldes (als Symbol)
context:   optionales Object mit weiteren Context-Informationen, dieser wird an den Force-Query-Builder übergeben
search_request: optional bestehendes SearchRequest (für DrillDown-Funktionalität)
config:    Config-Data, used as optional parameter for query-build

Beispiel:

searcher.list_possible_values :lkz, :whs_id => 5


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/multi_solr/single_core_handler.rb', line 95

def list_possible_values_with_count fieldname, context=nil, search_request=nil, config=nil
  search_request ||= MultiSolr::SearchRequest.new
  search_request.facets = [fieldname]
  result = search(search_request, :context => context, :facets_only => true, :config => config)
  facet_counts = result.facet_counts
  values = {}
  if MultiSolr.logger.debug?
    MultiSolr.logger.debug "#{self.class.name}.list_possible_values_with_count(#{fieldname}, #{context.inspect}, #{search_request.inspect}): facet_counts=#{facet_counts.inspect}"
  end
  if facet_counts
    value_pairs = facet_counts[fieldname.to_s]
    if value_pairs && !value_pairs.empty?
      # das value_pairs besteht Paar-weise aus Value und Anzahl
      # Es werden hier nur die Paare gebraucht, wo die Anzahl >0 ist
      only_int = true
      value_pairs.each do |value_count_pair|
        val, count = value_count_pair
        if val && count > 0
          only_int = false if only_int && !val.match(/^[0-9]+$/)
          # Value und Anzahl in die Ergebnis-Hash schreiben
          values[val] = count
        end
      end
      # wenn val nur aus Zahlen besteht, werden die keys nach Integer konvertiert (wegen der Sortierung)
      values = Hash[values.map {|k, v| [k.to_i, v] }] if only_int
      value_pair_data = values.sort_by { |value, count| value }
    end
  end
  if MultiSolr.logger.debug?
    MultiSolr.logger.debug "#{self.class.name}.list_possible_values_with_count(#{fieldname}, #{context.inspect}) => #{values.inspect}"
  end
  # Ergebnis ist ein Array mit Arrays bestehend aus [value,count]-Paaren: [[value, count], [value,count],..]
  value_pair_data
end

#pingObject

sendet Ping an SOLR-Instance returns true if ok



58
59
60
# File 'lib/multi_solr/single_core_handler.rb', line 58

def ping
  self.solr_connection.head("admin/ping").response[:status] == 200
end

#search(solr_search_request, options = nil) ⇒ Object

Ausführen der Suche / Recherche params:

solr_search_request : die Suchanfrage als SolrSearchRequest-Instance
options :     Hash mit weiteren Optionen für Query Anfragen:
  :context              Hash mit weiteren Context-Informationen, dieser wird u.a. an den Force-Query-Builder übergeben
  :config               Config-Data, uses as optional parameter for query-build
  :fieldlist            Field-List (siehe Solr-Doku fl-Parameter), default ist *
  :facets_only          wenn true, dann nur Facets ermittlen
  :without_facets       wenn true, dann auf Facets verzichten
  :result_format        Rückgabe-Format (json, xml),
                        wenn nicht gesetzt wird "ruby" geliefert;
                        Ist es gesetzt wird das Ergebnis nicht geparst (also raw als String zurückgegeben)


222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/multi_solr/single_core_handler.rb', line 222

def search solr_search_request, options=nil
  used_options = @default_search_options.clone
  used_options.merge! options if options

  solr_params = build_solr_params solr_search_request, used_options
  solr_result = solr_connection.post 'select', :params => solr_params

  # RAW-Result liefern wenn Result ein String ist
  return solr_result if solr_result.is_a? String

  # Parsen des Ergebnisses
  result = self.result_class.new solr_result, solr_search_request, used_options[:context]
  result
end

#solr_connection(options = nil) ⇒ Object

Liefert RSolr-Connection (RSolr.connect) zum konfigurierten SOLR-Server(see solr_url) und konfiguriertem Core (see core_name) Params:

options: optionale Connect-Parameters, see Rsolr.connect


47
48
49
50
51
52
53
# File 'lib/multi_solr/single_core_handler.rb', line 47

def solr_connection options=nil
  url = "#{@solr_url}/#{@core_name}"
  MultiSolr.logger.debug("solr_connection: url==#{url}") if MultiSolr.logger.debug?
  connect_params = {:url => url}
  connect_params.merge!(options) if options
  RSolr.connect connect_params
end