112 lines
3.3 KiB
Ruby
112 lines
3.3 KiB
Ruby
module Elasticsearch
|
||
module Model
|
||
module Adapter
|
||
|
||
# An adapter to be used for deserializing results from multiple models,
|
||
# retrieved through `Elasticsearch::Model.search`
|
||
#
|
||
# @see Elasticsearch::Model.search
|
||
#
|
||
module Multiple
|
||
Adapter.register self, lambda { |klass| klass.is_a? Multimodel }
|
||
|
||
module Records
|
||
# Returns a collection of model instances, possibly of different classes (ActiveRecord, Mongoid, ...)
|
||
#
|
||
# @note The order of results in the Elasticsearch response is preserved
|
||
#
|
||
def records
|
||
records_by_type = __records_by_type
|
||
|
||
records = response.response["hits"]["hits"].map do |hit|
|
||
records_by_type[ __type_for_hit(hit) ][ hit[:_id] ]
|
||
end
|
||
|
||
records.compact
|
||
end
|
||
|
||
# Returns the collection of records grouped by class based on `_type`
|
||
#
|
||
# Example:
|
||
#
|
||
# {
|
||
# Foo => {"1"=> #<Foo id: 1, title: "ABC"}, ...},
|
||
# Bar => {"1"=> #<Bar id: 1, name: "XYZ"}, ...}
|
||
# }
|
||
#
|
||
# @api private
|
||
#
|
||
def __records_by_type
|
||
result = __ids_by_type.map do |klass, ids|
|
||
records = __records_for_klass(klass, ids)
|
||
ids = records.map(&:id).map(&:to_s)
|
||
[ klass, Hash[ids.zip(records)] ]
|
||
end
|
||
|
||
Hash[result]
|
||
end
|
||
|
||
# Returns the collection of records for a specific type based on passed `klass`
|
||
#
|
||
# @api private
|
||
#
|
||
def __records_for_klass(klass, ids)
|
||
adapter = __adapter_for_klass(klass)
|
||
|
||
case
|
||
when Elasticsearch::Model::Adapter::ActiveRecord.equal?(adapter)
|
||
klass.where(klass.primary_key => ids)
|
||
when Elasticsearch::Model::Adapter::Mongoid.equal?(adapter)
|
||
klass.where(:id.in => ids)
|
||
else
|
||
klass.find(ids)
|
||
end
|
||
end
|
||
|
||
# Returns the record IDs grouped by class based on type `_type`
|
||
#
|
||
# Example:
|
||
#
|
||
# { Foo => ["1"], Bar => ["1", "5"] }
|
||
#
|
||
# @api private
|
||
#
|
||
def __ids_by_type
|
||
ids_by_type = {}
|
||
|
||
response.response["hits"]["hits"].each do |hit|
|
||
type = __type_for_hit(hit)
|
||
ids_by_type[type] ||= []
|
||
ids_by_type[type] << hit[:_id]
|
||
end
|
||
ids_by_type
|
||
end
|
||
|
||
# Returns the class of the model corresponding to a specific `hit` in Elasticsearch results
|
||
#
|
||
# @see Elasticsearch::Model::Registry
|
||
#
|
||
# @api private
|
||
#
|
||
def __type_for_hit(hit)
|
||
@@__types ||= {}
|
||
|
||
@@__types[ "#{hit[:_index]}::#{hit[:_type]}" ] ||= begin
|
||
Registry.all.detect do |model|
|
||
model.index_name == hit[:_index] && model.document_type == hit[:_type]
|
||
end
|
||
end
|
||
end
|
||
|
||
# Returns the adapter registered for a particular `klass` or `nil` if not available
|
||
#
|
||
# @api private
|
||
#
|
||
def __adapter_for_klass(klass)
|
||
Adapter.adapters.select { |name, checker| checker.call(klass) }.keys.first
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|