-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
210 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
module MeiliSearch | ||
module Rails | ||
class MultiSearchResult | ||
attr_reader :metadata | ||
|
||
def initialize(searches, raw_results) | ||
@results = {} | ||
@metadata = {} | ||
|
||
searches.zip(raw_results).each do |(index_target, search_options), result| | ||
index_target = search_options[:class_name].constantize if search_options[:class_name] | ||
|
||
@results[index_target] = case index_target | ||
when String, Symbol | ||
result['hits'] | ||
else | ||
load_results(index_target, result) | ||
end | ||
|
||
@metadata[index_target] = result.except('hits') | ||
end | ||
end | ||
|
||
include Enumerable | ||
|
||
def each_hit | ||
@results.each do |_index_target, results| | ||
results.each { |res| yield res } | ||
end | ||
end | ||
alias_method :each, :each_hit | ||
|
||
def each_result | ||
@results.each | ||
end | ||
|
||
def to_a | ||
@results.values.flatten(1) | ||
end | ||
alias_method :to_ary, :to_a | ||
|
||
def to_h | ||
@results | ||
end | ||
alias_method :to_hash, :to_h | ||
|
||
private | ||
|
||
def load_results(klass, result) | ||
pk_method = klass.ms_primary_key_method | ||
pk_method = pk_method.in if Utilities.is_mongo_model?(klass) | ||
|
||
ms_pk = klass.meilisearch_options[:primary_key] || IndexSettings::DEFAULT_PRIMARY_KEY | ||
|
||
condition_key = pk_is_virtual?(klass, pk_method) ? klass.primary_key : pk_method | ||
|
||
hits_by_id = | ||
result['hits'].index_by { |hit| hit[condition_key.to_s] } | ||
|
||
records = klass.where(condition_key => hits_by_id.keys) | ||
|
||
if records.respond_to? :in_order_of | ||
records.in_order_of(condition_key, hits_by_id.keys).each do |record| | ||
record.formatted = hits_by_id[record.send(condition_key).to_s]['_formatted'] | ||
end | ||
else | ||
results_by_id = records.index_by do |hit| | ||
hit.send(condition_key).to_s | ||
end | ||
|
||
result['hits'].filter_map do |hit| | ||
record = results_by_id[hit[condition_key.to_s].to_s] | ||
record&.formatted = hit['_formatted'] | ||
record | ||
end | ||
end | ||
end | ||
|
||
def pk_is_virtual?(model_class, pk_method) | ||
model_class.columns | ||
.map(&(Utilities.is_sequel_model?(model_class) ? :to_s : :name)) | ||
.exclude?(pk_method.to_s) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,116 @@ | ||
require 'spec_helper' | ||
|
||
describe 'MeiliSearch::Rails::MultiSearchResult' do # rubocop:todo RSpec/EmptyExampleGroup | ||
# TODO: Write specs | ||
describe MeiliSearch::Rails::MultiSearchResult do | ||
it 'is enumerable' do | ||
expect(described_class).to include(Enumerable) | ||
end | ||
|
||
let(:raw_results) do | ||
[ | ||
{ 'indexUid' => 'books_index', | ||
'hits' => [{ 'name' => 'Steve Jobs', 'id' => '3', 'author' => 'Walter Isaacson', 'premium' => nil, 'released' => nil, 'genre' => nil }], | ||
'query' => 'Steve', 'processingTimeMs' => 0, 'limit' => 20, 'offset' => 0, 'estimatedTotalHits' => 1 | ||
}, | ||
{ 'indexUid' => 'products_index', | ||
'hits' => [{ 'id' => '4', 'href' => 'ebay', 'name' => 'palm pixi plus' }], | ||
'query' => 'palm', 'processingTimeMs' => 0, 'limit' => 1, 'offset' => 0, 'estimatedTotalHits' => 2 | ||
}, | ||
{ 'indexUid' => 'color_index', | ||
'hits' => [ | ||
{ 'name' => 'black', 'id' => '5', 'short_name' => 'bla', 'hex' => 0 }, | ||
{ 'name' => 'blue', 'id' => '4', 'short_name' => 'blu', 'hex' => 255 } | ||
], | ||
'query' => 'bl', 'processingTimeMs' => 0, 'limit' => 20, 'offset' => 0, 'estimatedTotalHits' => 2 | ||
} | ||
] | ||
end | ||
|
||
context 'with index name keys' do | ||
subject(:result) { described_class.new(searches, raw_results) } | ||
|
||
let(:searches) do | ||
{ | ||
'books_index' => { q: 'Steve' }, | ||
'products_index' => { q: 'palm', limit: 1 }, | ||
'color_index' => { q: 'bl' } | ||
} | ||
end | ||
|
||
it 'enumerates through the hits' do | ||
expect(result).to contain_exactly( | ||
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs'), | ||
a_hash_including('name' => 'palm pixi plus'), | ||
a_hash_including('name' => 'blue', 'short_name' => 'blu'), | ||
a_hash_including('name' => 'black', 'short_name' => 'bla') | ||
) | ||
end | ||
|
||
it 'enumerates through the hits of each result with #each_result' do | ||
expect(result.each_result).to be_an(Enumerator) | ||
expect(result.each_result).to contain_exactly( | ||
[ 'books_index', contain_exactly( | ||
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs')) ], | ||
[ 'products_index', contain_exactly( | ||
a_hash_including('name' => 'palm pixi plus')) ], | ||
[ 'color_index', contain_exactly( | ||
a_hash_including('name' => 'blue', 'short_name' => 'blu'), | ||
a_hash_including('name' => 'black', 'short_name' => 'bla')) ] | ||
) | ||
end | ||
|
||
describe '#to_a' do | ||
it 'returns the hits' do | ||
expect(result.to_a).to contain_exactly( | ||
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs'), | ||
a_hash_including('name' => 'palm pixi plus'), | ||
a_hash_including('name' => 'blue', 'short_name' => 'blu'), | ||
a_hash_including('name' => 'black', 'short_name' => 'bla') | ||
) | ||
end | ||
|
||
it 'aliases as #to_ary' do | ||
expect(subject.method(:to_ary).original_name).to eq :to_a | ||
end | ||
end | ||
|
||
describe '#to_h' do | ||
it 'returns a hash of indexes and hits' do | ||
expect(result.to_h).to match( | ||
'books_index' => contain_exactly( | ||
a_hash_including('author' => 'Walter Isaacson', 'name' => 'Steve Jobs') | ||
), | ||
'products_index' => contain_exactly( | ||
a_hash_including('name' => 'palm pixi plus') | ||
), | ||
'color_index' => contain_exactly( | ||
a_hash_including('name' => 'blue', 'short_name' => 'blu'), | ||
a_hash_including('name' => 'black', 'short_name' => 'bla') | ||
) | ||
) | ||
end | ||
|
||
it 'is aliased as #to_hash' do | ||
expect(result.method(:to_hash).original_name).to eq :to_h | ||
end | ||
end | ||
|
||
describe '#metadata' do | ||
it 'returns search metadata for each result' do | ||
expect(result.metadata).to match( | ||
'books_index' => { | ||
'indexUid' => 'books_index', | ||
'query' => 'Steve', 'processingTimeMs' => 0, 'limit' => 20, 'offset' => 0, 'estimatedTotalHits' => 1 | ||
}, | ||
'products_index' => { | ||
'indexUid' => 'products_index', | ||
'query' => 'palm', 'processingTimeMs' => 0, 'limit' => 1, 'offset' => 0, 'estimatedTotalHits' => 2 | ||
}, | ||
'color_index' => { | ||
'indexUid' => 'color_index', | ||
'query' => 'bl', 'processingTimeMs' => 0, 'limit' => 20, 'offset' => 0, 'estimatedTotalHits' => 2 | ||
} | ||
) | ||
end | ||
end | ||
end | ||
end |