Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,14 @@ Use `#each_result` to loop through pairs of your provided keys and the results:
</ul>
```

Records are loaded when the keys are models, or when `:class_name` option is passed:
Records are loaded when the keys are models, or when `:scope` option is passed:

```ruby
multi_search_results = Meilisearch::Rails.multi_search(
'books' => { q: 'Harry', class_name: 'Book' },
'mangas' => { q: 'Attack', class_name: 'Manga' }
# scope may be a relation
'books' => { q: 'Harry', scope: Book.all },
# or a model
'mangas' => { q: 'Attack', scope: Manga }
)
```

Expand All @@ -280,8 +282,8 @@ The index to search is inferred from the model if the key is a model, if the key

```ruby
multi_search_results = Meilisearch::Rails.multi_search(
'western' => { q: 'Harry', class_name: 'Book', index_uid: 'books_production' },
'japanese' => { q: 'Attack', class_name: 'Manga', index_uid: 'mangas_production' }
'western' => { q: 'Harry', scope: Book, index_uid: 'books_production' },
'japanese' => { q: 'Attack', scope: Manga, index_uid: 'mangas_production' }
)
```

Expand All @@ -291,10 +293,11 @@ You can search the same index multiple times by specifying `:index_uid`:

```ruby
query = 'hero'

multi_search_results = Meilisearch::Rails.multi_search(
'Isekai Manga' => { q: query, class_name: 'Manga', filters: 'genre:isekai', index_uid: 'mangas_production' }
'Shounen Manga' => { q: query, class_name: 'Manga', filters: 'genre:shounen', index_uid: 'mangas_production' }
'Steampunk Manga' => { q: query, class_name: 'Manga', filters: 'genre:steampunk', index_uid: 'mangas_production' }
'Isekai Manga' => { q: query, scope: Manga, filters: 'genre:isekai', index_uid: 'mangas_production' }
'Shounen Manga' => { q: query, scope: Manga, filters: 'genre:shounen', index_uid: 'mangas_production' }
'Steampunk Manga' => { q: query, scope: Manga, filters: 'genre:steampunk', index_uid: 'mangas_production' }
)
```

Expand Down
5 changes: 3 additions & 2 deletions lib/meilisearch/rails/multi_search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module Rails
class << self
def multi_search(searches)
search_parameters = searches.map do |(index_target, options)|
index_target = options.delete(:index_uid) || index_target
model_class = options[:scope].respond_to?(:model) ? options[:scope].model : options[:scope]
index_target = options.delete(:index_uid) || model_class || index_target

paginate(options) if pagination_enabled?
normalize(options, index_target)
Expand All @@ -18,7 +19,7 @@ def multi_search(searches)

def normalize(options, index_target)
options
.except(:class_name)
.except(:class_name, :scope)
.merge!(index_uid: index_uid_from_target(index_target))
end

Expand Down
14 changes: 11 additions & 3 deletions lib/meilisearch/rails/multi_search/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ def initialize(searches, raw_results)

searches.zip(raw_results['results']).each do |(target, search_options), result|
results_class = if search_options[:class_name]
Meilisearch::Rails.logger.warn(
'[meilisearch-rails] The :class_name option in multi search is deprecated, please use :scope instead.'
)

search_options[:class_name].constantize
elsif target.instance_of?(Class)
target
elsif search_options[:scope]
search_options[:scope]
end

@results[target] = results_class ? load_results(results_class, result) : result['hits']
@results[target] = results_class ? load_results(results_class, result, scope: search_options[:scope]) : result['hits']

@metadata[target] = result.except('hits')
end
Expand Down Expand Up @@ -69,7 +75,9 @@ def to_h

private

def load_results(klass, result)
def load_results(klass, result, scope:)
scope ||= klass

pk_method = klass.ms_primary_key_method
pk_method = pk_method.in if Utilities.mongo_model?(klass)

Expand All @@ -78,7 +86,7 @@ def load_results(klass, result)
hits_by_id =
result['hits'].index_by { |hit| hit[condition_key.to_s] }

records = klass.where(condition_key => hits_by_id.keys)
records = scope.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|
Expand Down
45 changes: 44 additions & 1 deletion spec/multi_search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ def reset_indexes

context 'when :class_name is also present' do
it 'loads results from the correct models' do
allow(Meilisearch::Rails.logger).to receive(:warn).and_return(nil)

results = Meilisearch::Rails.multi_search(
'books' => { q: 'Steve', index_uid: Book.index.uid, class_name: 'Book' },
'products' => { q: 'palm', limit: 1, index_uid: Product.index.uid, class_name: 'Product' },
Expand Down Expand Up @@ -122,6 +124,18 @@ def reset_indexes
end

context 'when class_name is specified' do
let(:logger) { instance_double('Logger', warn: nil) }

before do
allow(Meilisearch::Rails).to receive(:logger).and_return(logger)
end

it 'warns about deprecation' do
results = Meilisearch::Rails.multi_search(Book.index.uid => { q: 'Steve', class_name: 'Book' })
expect(results.to_h[Book.index.uid]).to contain_exactly(steve_jobs)
expect(logger).to have_received(:warn).with(a_string_matching(':class_name'))
end

it 'returns ORM records' do
results = Meilisearch::Rails.multi_search(
Book.index.uid => { q: 'Steve', class_name: 'Book' },
Expand Down Expand Up @@ -150,7 +164,7 @@ def reset_indexes
it 'returns a mixture of ORM records and hashes' do
results = Meilisearch::Rails.multi_search(
Book => { q: 'Steve' },
Product.index.uid => { q: 'palm', limit: 1, class_name: 'Product' },
Product.index.uid => { q: 'palm', limit: 1, scope: Product },
Color.index.uid => { q: 'bl' }
)

Expand Down Expand Up @@ -180,4 +194,33 @@ def reset_indexes
Meilisearch::Rails.configuration[:pagination_backend] = nil
end
end

context 'with scopes' do
it 'fetches items from the given scope' do
results = Meilisearch::Rails.multi_search(
Product => { q: 'palm', scope: Product.where('tags LIKE "%terrible%"') },
Color => { q: 'bl', scope: Color.where(short_name: 'bla') }
)

expect(results).to contain_exactly(
black, palm_pixi_plus
)
end

it 'infers the model' do
results = Meilisearch::Rails.multi_search(
'colors' => { q: 'bl', scope: Color.all, index_uid: Color.index.uid }
)

expect(results.to_h['colors']).to contain_exactly(blue, black)
end

it 'infers the index as well as the model' do
results = Meilisearch::Rails.multi_search(
'colors' => { q: 'bl', scope: Color }
)

expect(results.to_h['colors']).to contain_exactly(blue, black)
end
end
end