Commit 329e05353fe8a16822832a910d16cadc475ef38c

Authored by Andrew Kane
1 parent 69cf12cf

Use lazy loading for searches [skip ci]

CHANGELOG.md
1 1 ## 5.0.0 (unreleased)
2 2  
  3 +- Searches now use lazy loading (similar to Active Record)
3 4 - Anchor regular expressions by default
4 5 - Raise error when `search` called on relations
5 6 - Raise `ArgumentError` (instead of warning) for invalid regular expression modifiers
... ...
README.md
... ... @@ -2053,6 +2053,18 @@ gem "elasticsearch"
2053 2053  
2054 2054 If using the deprecated `faraday_middleware-aws-signers-v4` gem, switch to `faraday_middleware-aws-sigv4`.
2055 2055  
  2056 +Also, searches now use lazy loading:
  2057 +
  2058 +```ruby
  2059 +# search not executed
  2060 +products = Product.search("milk")
  2061 +
  2062 +# search executed
  2063 +products.each do |product|
  2064 + # ...
  2065 +end
  2066 +```
  2067 +
2056 2068 Check out the [changelog](https://github.com/ankane/searchkick/blob/master/CHANGELOG.md) for the full list of changes.
2057 2069  
2058 2070 ## History
... ...
lib/searchkick.rb
... ... @@ -15,6 +15,7 @@ require "searchkick/query"
15 15 require "searchkick/reindex_queue"
16 16 require "searchkick/record_data"
17 17 require "searchkick/record_indexer"
  18 +require "searchkick/relation"
18 19 require "searchkick/results"
19 20 require "searchkick/version"
20 21  
... ... @@ -150,16 +151,18 @@ module Searchkick
150 151 end
151 152 end
152 153  
153   - options = options.merge(block: block) if block
154   - query = Searchkick::Query.new(klass, term, **options)
  154 + # TODO remove in Searchkick 6.0
155 155 if options[:execute] == false
156   - query
157   - else
158   - query.execute
  156 + Searchkick.warn("The execute option is no longer needed")
  157 + options.delete(:execute)
159 158 end
  159 +
  160 + options = options.merge(block: block) if block
  161 + Searchkick::Relation.new(klass, term, **options)
160 162 end
161 163  
162 164 def self.multi_search(queries)
  165 + queries = queries.map { |q| q.send(:query) }
163 166 Searchkick::MultiSearch.new(queries).perform
164 167 end
165 168  
... ...
lib/searchkick/query.rb
... ... @@ -18,7 +18,7 @@ module Searchkick
18 18  
19 19 def initialize(klass, term = "*", **options)
20 20 unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost,
21   - :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :execute, :explain,
  21 + :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain,
22 22 :fields, :highlight, :includes, :index_name, :indices_boost, :limit, :load,
23 23 :match, :misspellings, :models, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile,
24 24 :request_params, :routing, :scope_results, :scroll, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where]
... ...
lib/searchkick/relation.rb 0 โ†’ 100644
... ... @@ -0,0 +1,38 @@
  1 +require "active_support/core_ext/module/delegation"
  2 +
  3 +module Searchkick
  4 + class Relation
  5 + # note: modifying body directly is not supported
  6 + # and has no impact on query after being executed
  7 + # TODO freeze body object?
  8 + delegate :body, :params, to: :@query
  9 + delegate_missing_to :private_execute
  10 +
  11 + def initialize(klass, term = "*", **options)
  12 + @query = Query.new(klass, term, **options)
  13 + end
  14 +
  15 + # same as Active Record
  16 + def inspect
  17 + entries = results.first(11).map!(&:inspect)
  18 + entries[10] = "..." if entries.size == 11
  19 + "#<#{self.class.name} [#{entries.join(', ')}]>"
  20 + end
  21 +
  22 + def execute
  23 + Searchkick.warn("The execute method is no longer needed")
  24 + private_execute
  25 + self
  26 + end
  27 +
  28 + private
  29 +
  30 + def private_execute
  31 + @execute ||= @query.execute
  32 + end
  33 +
  34 + def query
  35 + @query
  36 + end
  37 + end
  38 +end
... ...
test/search_test.rb
... ... @@ -56,14 +56,14 @@ class SearchTest &lt; Minitest::Test
56 56 def test_bad_mapping
57 57 Product.searchkick_index.delete
58 58 store_names ["Product A"]
59   - error = assert_raises(Searchkick::InvalidQueryError) { Product.search "test" }
  59 + error = assert_raises(Searchkick::InvalidQueryError) { Product.search("test").to_a }
60 60 assert_equal "Bad mapping - run Product.reindex", error.message
61 61 ensure
62 62 Product.reindex
63 63 end
64 64  
65 65 def test_missing_index
66   - assert_raises(Searchkick::MissingIndexError) { Product.search("test", index_name: "not_found") }
  66 + assert_raises(Searchkick::MissingIndexError) { Product.search("test", index_name: "not_found").to_a }
67 67 end
68 68  
69 69 def test_unsupported_version
... ... @@ -77,11 +77,11 @@ class SearchTest &lt; Minitest::Test
77 77 end
78 78 end
79 79 Searchkick.client.stub :search, raises_exception do
80   - assert_raises(Searchkick::UnsupportedVersionError) { Product.search("test") }
  80 + assert_raises(Searchkick::UnsupportedVersionError) { Product.search("test").to_a }
81 81 end
82 82 end
83 83  
84 84 def test_invalid_body
85   - assert_raises(Searchkick::InvalidQueryError) { Product.search(body: {boom: true}) }
  85 + assert_raises(Searchkick::InvalidQueryError) { Product.search(body: {boom: true}).to_a }
86 86 end
87 87 end
... ...