# File lib/tire/index.rb, line 8 def initialize(name, &block) @name = name block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given? end
# File lib/tire/index.rb, line 45 def add_alias(alias_name, configuration={}) Alias.create(configuration.merge( :name => alias_name, :index => @name ) ) end
# File lib/tire/index.rb, line 53 def aliases(alias_name=nil) alias_name ? Alias.all(@name).select { |a| a.name == alias_name }.first : Alias.all(@name) end
# File lib/tire/index.rb, line 418 def analyze(text, options={}) options = {:pretty => true}.update(options) params = options.to_param @response = Configuration.client.get "#{url}/_analyze?#{params}", text @response.success? ? MultiJson.decode(@response.body) : false ensure curl = %Qcurl -X GET "#{url}/_analyze?#{params}" -d '#{text}'| logged('_analyze', curl) end
Performs a [bulk](www.elasticsearch.org/guide/reference/api/bulk.html) request
@myindex.bulk :index, [ {id: 1, title: 'One'}, { id: 2, title: 'Two', _version: 3 } ], refresh: true
Pass the action (`index`, `create`, `delete`, `update`) as the first argument, the collection of documents as the second argument, and URL parameters as the last option.
Any meta information contained in documents (such as `_routing` or `_parent`) is extracted and added to the "header" line.
Shortcut methods `#bulk_store`, `#bulk_delete`, `#bulk_create`, and `#bulk_update` are available.
# File lib/tire/index.rb, line 165 def bulk(action, documents, options={}) return false if documents.empty? # TODO: A more Ruby-like DSL notation should be supported: # # Tire.index('myindex').bulk do # create id: 1, title: 'bar', _routing: 'abc' # delete id: 1 # # ... # end payload = documents.map do |document| type = get_type_from_document(document, :escape => false) # Do not URL-escape the _type id = get_id_from_document(document) if ENV['DEBUG'] STDERR.puts "[ERROR] Document #{document.inspect} does not have ID" unless id end if action.to_sym == :update raise ArgumentError, "Cannot update without document type" unless type raise ArgumentError, "Cannot update without document ID" unless id raise ArgumentError, "Update requires a hash document" unless document.respond_to?(:to_hash) document = document.to_hash raise ArgumentError, "Update requires either a script or a partial document" unless document[:script] || document[:doc] end header = { action.to_sym => { :_index => name, :_type => type, :_id => id } } header[action.to_sym].update({:_retry_on_conflict => options[:retry_on_conflict]}) if options[:retry_on_conflict] if document.respond_to?(:to_hash) && doc_hash = document.to_hash meta = doc_hash.select do |k,v| [ :_parent, :_percolate, :_retry_on_conflict, :_routing, :_timestamp, :_ttl, :_version, :_version_type].include?(k) end # Normalize Ruby 1.8 and Ruby 1.9 Hash#select behaviour meta = Hash[meta] unless meta.is_a?(Hash) # meta = SUPPORTED_META_PARAMS_FOR_BULK.inject({}) { |hash, param| # value = doc_hash.delete(param) # hash[param] = value unless !value || value.empty? # hash # } header[action.to_sym].update(meta) end if action.to_sym == :update document.keep_if do |k,_| [ :doc, :upsert, :doc_as_upsert, :script, :params, :lang ].include?(k) end end output = [] output << MultiJson.encode(header) output << convert_document_to_json(document) unless action.to_s == 'delete' output.join("\n") end payload << "" tries = 5 count = 0 begin params = {} params[:consistency] = options.delete(:consistency) params[:refresh] = options.delete(:refresh) params = params.reject { |name,value| !value } params_encoded = params.empty? ? '' : "?#{params.to_param}" @response = Configuration.client.post("#{url}/_bulk#{params_encoded}", payload.join("\n")) raise RuntimeError, "#{@response.code} > #{@response.body}" if @response && @response.failure? @response rescue StandardError => error if count < tries count += 1 STDERR.puts "[ERROR] #{error.message}, retrying (#{count})..." retry else STDERR.puts "[ERROR] Too many exceptions occured, giving up. The HTTP response was: #{error.message}" raise if options[:raise] end ensure data = Configuration.logger && Configuration.logger.level.to_s == 'verbose' ? payload.join("\n") : '... data omitted ...' curl = %Qcurl -X POST "#{url}/_bulk" --data-binary '#{data}'| logged('_bulk', curl) end end
# File lib/tire/index.rb, line 268 def bulk_create(documents, options={}) bulk :create, documents, options end
# File lib/tire/index.rb, line 276 def bulk_delete(documents, options={}) bulk :delete, documents, options end
# File lib/tire/index.rb, line 272 def bulk_store(documents, options={}) bulk :index, documents, options end
# File lib/tire/index.rb, line 280 def bulk_update(documents, options={}) bulk :update, documents, options end
# File lib/tire/index.rb, line 409 def close(options={}) @response = Configuration.client.post "#{url}/_close", MultiJson.encode(options) MultiJson.decode(@response.body)['ok'] ensure curl = %Qcurl -X POST "#{url}/_close"| logged('_close', curl) end
# File lib/tire/index.rb, line 523 def convert_document_to_json(document) document = case when document.is_a?(String) if ENV['DEBUG'] Tire.warn "Passing the document as JSON string has been deprecated, " + "please pass an object which responds to `to_indexed_json` or a plain Hash." end document when document.respond_to?(:to_indexed_json) then document.to_indexed_json else raise ArgumentError, "Please pass a JSON string or object with a 'to_indexed_json' method," + "'#{document.class}' given." end end
# File lib/tire/index.rb, line 35 def create(options={}) @options = options @response = Configuration.client.post url, MultiJson.encode(options) @response.success? ? @response : false ensure curl = %Qcurl -X POST #{url} -d '#{MultiJson.encode(options, :pretty => Configuration.pretty)}'| logged('CREATE', curl) end
# File lib/tire/index.rb, line 26 def delete @response = Configuration.client.delete url @response.success? ensure curl = %Qcurl -X DELETE #{url}| logged('DELETE', curl) end
# File lib/tire/index.rb, line 108 def delete_mapping(type) url = "#{self.url}/#{type}" @response = Configuration.client.delete(url) @response.success? ensure curl = %Qcurl -X DELETE "#{url}"| logged("DELETE MAPPING #{type}", curl) end
# File lib/tire/index.rb, line 17 def exists? @response = Configuration.client.head("#{url}") @response.success? ensure curl = %Qcurl -I "#{url}"| logged('HEAD', curl) end
# File lib/tire/index.rb, line 511 def get_id_from_document(document) old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings id = case when document.is_a?(Hash) document[:_id] || document['_id'] || document[:id] || document['id'] when document.respond_to?(:id) && document.id != document.object_id document.id.to_s end $VERBOSE = old_verbose id end
# File lib/tire/index.rb, line 80 def get_mapping @response = Configuration.client.get("#{url}/_mapping") result = MultiJson.decode(@response.body)[@name] @response.success? ? result : false ensure curl = %Qcurl -X GET "#{url}/_mapping?pretty"| logged("GET MAPPING", curl) end
# File lib/tire/index.rb, line 491 def get_type_from_document(document, options={}) options = {:escape => true}.merge(options) old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#type deprecation warnings type = case when document.respond_to?(:document_type) document.document_type when document.is_a?(Hash) document[:_type] || document['_type'] || document[:type] || document['type'] when document.respond_to?(:_type) document._type when document.respond_to?(:type) && document.type != document.class document.type end $VERBOSE = old_verbose type ||= 'document' options[:escape] ? Utils.escape(type) : type end
# File lib/tire/index.rb, line 284 def import(klass_or_collection, options={}) case when method = options.delete(:method) options = {:page => 1, :per_page => 1000}.merge options while (documents = klass_or_collection.send(method.to_sym, options.merge(:page => options[:page]))) && documents.to_a.length > 0 documents = yield documents if block_given? bulk_store documents, options GC.start options[:page] += 1 end when klass_or_collection.respond_to?(:map) documents = block_given? ? yield(klass_or_collection) : klass_or_collection bulk_store documents, options else raise ArgumentError, "Please pass either an Enumerable compatible class, or a collection object " + "with a method for fetching records in batches (such as 'paginate')." end end
# File lib/tire/index.rb, line 468 def logged(endpoint='/', curl='') if Configuration.logger error = $! Configuration.logger.log_request endpoint, @name, curl code = @response ? @response.code : error.class rescue 'N/A' if Configuration.logger.level.to_s == 'debug' body = if @response && @response.body && !@response.body.to_s.empty? MultiJson.encode( MultiJson.load(@response.body), :pretty => Configuration.pretty) elsif error && error.message && !error.message.to_s.empty? MultiJson.encode( MultiJson.load(error.message), :pretty => Configuration.pretty) rescue '' else '' end else body = '' end Configuration.logger.log_response code, nil, body end end
Get or update the index mapping
Without arguments, returns the index mapping as a Hash
When passed arguments, attempts to update the index mapping:
index.mapping 'article', properties: { body: { type: "string" } }
You can pass the `ignore_conflicts` option as a part of the Hash:
index.mapping 'article', properties: { body: { type: "string" } }, ignore_conflicts: true
# File lib/tire/index.rb, line 69 def mapping(*args) args.empty? ? get_mapping : put_mapping(*args) end
Raises an exception for unsuccessful responses
# File lib/tire/index.rb, line 75 def mapping!(*args) mapping(*args) raise RuntimeError, response.body unless response.success? end
# File lib/tire/index.rb, line 399 def open(options={}) # TODO: Remove the duplication in the execute > rescue > ensure chain @response = Configuration.client.post "#{url}/_open", MultiJson.encode(options) MultiJson.decode(@response.body)['ok'] ensure curl = %Qcurl -X POST "#{url}/_open"| logged('_open', curl) end
# File lib/tire/index.rb, line 449 def percolate(*args, &block) document = args.shift type = get_type_from_document(document) document = MultiJson.decode convert_document_to_json(document) query = Search::Query.new(&block).to_hash if block_given? payload = { :doc => document } payload.update( :query => query ) if query @response = Configuration.client.get "#{url}/#{type}/_percolate", MultiJson.encode(payload) MultiJson.decode(@response.body)['matches'] ensure curl = %Qcurl -X GET "#{url}/#{type}/_percolate?pretty" -d '#{MultiJson.encode(payload, :pretty => Configuration.pretty)}'| logged('_percolate', curl) end
# File lib/tire/index.rb, line 89 def put_mapping(type, mapping) params = {} if ignore_conflicts = mapping.delete(:ignore_conflicts) || mapping.delete("ignore_conflicts") params[:ignore_conflicts] = ignore_conflicts end url = "#{self.url}/#{type}/_mapping" url += "?#{params.to_param}" unless params.empty? payload = { type => mapping }.to_json @response = Configuration.client.put url, payload result = MultiJson.decode(@response.body) @response.success? ? result : false ensure curl = %Qcurl -X PUT "#{url}" -d '#{payload}'| logged("PUT MAPPING #{type}", curl) end
# File lib/tire/index.rb, line 391 def refresh @response = Configuration.client.post "#{url}/_refresh", '' ensure curl = %Qcurl -X POST "#{url}/_refresh"| logged('_refresh', curl) end
# File lib/tire/index.rb, line 429 def register_percolator_query(name, options={}, &block) options[:query] = Search::Query.new(&block).to_hash if block_given? @response = Configuration.client.put "#{Configuration.url}/_percolator/#{@name}/#{name}", MultiJson.encode(options) MultiJson.decode(@response.body)['ok'] ensure curl = %Qcurl -X PUT "#{Configuration.url}/_percolator/#{@name}/#{name}?pretty" -d '#{MultiJson.encode(options, :pretty => Configuration.pretty)}'| logged('_percolator', curl) end
# File lib/tire/index.rb, line 308 def reindex(name, options={}, &block) new_index = Index.new(name) new_index.create(options) unless new_index.exists? transform = options.delete(:transform) Search::Scan.new(self.name, &block).each do |results| documents = results.map do |document| document = document.to_hash.except(:type, :_index, :_explanation, :_score, :_version, :highlight, :sort) document = transform.call(document) if transform document end new_index.bulk_store documents end end
# File lib/tire/index.rb, line 326 def remove(*args) if args.size > 1 type, document = args type = Utils.escape(type) id = get_id_from_document(document) || document else document = args.pop type = get_type_from_document(document) id = get_id_from_document(document) || document end raise ArgumentError, "Please pass a document ID" unless id url = "#{self.url}/#{type}/#{Utils.escape(id)}" result = Configuration.client.delete url MultiJson.decode(result.body) if result.success? ensure curl = %Qcurl -X DELETE "#{url}"| logged("#{type}/#{id}", curl) end
# File lib/tire/index.rb, line 49 def remove_alias(alias_name) Alias.find(alias_name) { |a| a.indices.delete @name }.save end
# File lib/tire/index.rb, line 347 def retrieve(type, id, options={}) raise ArgumentError, "Please pass a document ID" unless id url = "#{self.url}/#{Utils.escape(type)}/#{Utils.escape(id)}" params = {} params[:routing] = options[:routing] if options[:routing] params[:fields] = options[:fields] if options[:fields] params[:preference] = options[:preference] if options[:preference] params_encoded = params.empty? ? '' : "?#{params.to_param}" @response = Configuration.client.get "#{url}#{params_encoded}" h = MultiJson.decode(@response.body) wrapper = options[:wrapper] || Configuration.wrapper if wrapper == Hash then h else return nil if h['exists'] == false document = h['_source'] || h['fields'] || {} document.update('id' => h['_id'], '_type' => h['_type'], '_index' => h['_index'], '_version' => h['_version']) wrapper.new(document) end ensure curl = %Qcurl -X GET "#{url}"| logged("#{type}/#{id}", curl) end
# File lib/tire/index.rb, line 117 def settings @response = Configuration.client.get("#{url}/_settings") MultiJson.decode(@response.body)[@name]['settings'] end
# File lib/tire/index.rb, line 122 def store(*args) document, options = args id = get_id_from_document(document) type = get_type_from_document(document) document = convert_document_to_json(document) options ||= {} params = {} if options[:percolate] params[:percolate] = options[:percolate] params[:percolate] = "*" if params[:percolate] === true end params[:parent] = options[:parent] if options[:parent] params[:routing] = options[:routing] if options[:routing] params[:replication] = options[:replication] if options[:replication] params[:version] = options[:version] if options[:version] params_encoded = params.empty? ? '' : "?#{params.to_param}" url = id ? "#{self.url}/#{type}/#{Utils.escape(id)}#{params_encoded}" : "#{self.url}/#{type}/#{params_encoded}" @response = Configuration.client.post url, document MultiJson.decode(@response.body) ensure curl = %Qcurl -X POST "#{url}" -d '#{document}'| logged([type, id].join('/'), curl) end
# File lib/tire/index.rb, line 440 def unregister_percolator_query(name) @response = Configuration.client.delete "#{Configuration.url}/_percolator/#{@name}/#{name}" MultiJson.decode(@response.body)['ok'] ensure curl = %Qcurl -X DELETE "#{Configuration.url}/_percolator/#{@name}"| logged('_percolator', curl) end
# File lib/tire/index.rb, line 375 def update(type, id, payload={}, options={}) raise ArgumentError, "Please pass a document type" unless type raise ArgumentError, "Please pass a document ID" unless id raise ArgumentError, "Please pass a script or partial document in the payload hash" unless payload[:script] || payload[:doc] type = Utils.escape(type) url = "#{self.url}/#{type}/#{Utils.escape(id)}/_update" url += "?#{options.to_param}" unless options.keys.empty? @response = Configuration.client.post url, MultiJson.encode(payload) MultiJson.decode(@response.body) ensure curl = %Qcurl -X POST "#{url}" -d '#{MultiJson.encode(payload, :pretty => Configuration.pretty)}'| logged(id, curl) end
# File lib/tire/index.rb, line 13 def url "#{Configuration.url}/#{@name}" end