searchkick 2.3.2 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +377 -84
- data/LICENSE.txt +1 -1
- data/README.md +859 -602
- data/lib/searchkick/bulk_reindex_job.rb +13 -9
- data/lib/searchkick/controller_runtime.rb +40 -0
- data/lib/searchkick/hash_wrapper.rb +12 -0
- data/lib/searchkick/index.rb +281 -356
- data/lib/searchkick/index_cache.rb +30 -0
- data/lib/searchkick/index_options.rb +487 -281
- data/lib/searchkick/indexer.rb +15 -8
- data/lib/searchkick/log_subscriber.rb +57 -0
- data/lib/searchkick/middleware.rb +9 -2
- data/lib/searchkick/model.rb +72 -118
- data/lib/searchkick/multi_search.rb +9 -10
- data/lib/searchkick/process_batch_job.rb +12 -15
- data/lib/searchkick/process_queue_job.rb +22 -13
- data/lib/searchkick/query.rb +458 -217
- data/lib/searchkick/railtie.rb +7 -0
- data/lib/searchkick/record_data.rb +128 -0
- data/lib/searchkick/record_indexer.rb +164 -0
- data/lib/searchkick/reindex_queue.rb +51 -9
- data/lib/searchkick/reindex_v2_job.rb +10 -32
- data/lib/searchkick/relation.rb +247 -0
- data/lib/searchkick/relation_indexer.rb +155 -0
- data/lib/searchkick/results.rb +201 -82
- data/lib/searchkick/version.rb +1 -1
- data/lib/searchkick/where.rb +11 -0
- data/lib/searchkick.rb +269 -97
- data/lib/tasks/searchkick.rake +37 -0
- metadata +24 -178
- data/.gitignore +0 -22
- data/.travis.yml +0 -39
- data/Gemfile +0 -16
- data/Rakefile +0 -20
- data/benchmark/Gemfile +0 -23
- data/benchmark/benchmark.rb +0 -97
- data/lib/searchkick/logging.rb +0 -242
- data/lib/searchkick/tasks.rb +0 -33
- data/searchkick.gemspec +0 -28
- data/test/aggs_test.rb +0 -197
- data/test/autocomplete_test.rb +0 -75
- data/test/boost_test.rb +0 -202
- data/test/callbacks_test.rb +0 -59
- data/test/ci/before_install.sh +0 -17
- data/test/errors_test.rb +0 -19
- data/test/gemfiles/activerecord31.gemfile +0 -7
- data/test/gemfiles/activerecord32.gemfile +0 -7
- data/test/gemfiles/activerecord40.gemfile +0 -8
- data/test/gemfiles/activerecord41.gemfile +0 -8
- data/test/gemfiles/activerecord42.gemfile +0 -7
- data/test/gemfiles/activerecord50.gemfile +0 -7
- data/test/gemfiles/apartment.gemfile +0 -8
- data/test/gemfiles/cequel.gemfile +0 -8
- data/test/gemfiles/mongoid2.gemfile +0 -7
- data/test/gemfiles/mongoid3.gemfile +0 -6
- data/test/gemfiles/mongoid4.gemfile +0 -7
- data/test/gemfiles/mongoid5.gemfile +0 -7
- data/test/gemfiles/mongoid6.gemfile +0 -12
- data/test/gemfiles/nobrainer.gemfile +0 -8
- data/test/gemfiles/parallel_tests.gemfile +0 -8
- data/test/geo_shape_test.rb +0 -175
- data/test/highlight_test.rb +0 -78
- data/test/index_test.rb +0 -166
- data/test/inheritance_test.rb +0 -83
- data/test/marshal_test.rb +0 -8
- data/test/match_test.rb +0 -276
- data/test/misspellings_test.rb +0 -56
- data/test/model_test.rb +0 -42
- data/test/multi_search_test.rb +0 -36
- data/test/multi_tenancy_test.rb +0 -22
- data/test/order_test.rb +0 -46
- data/test/pagination_test.rb +0 -70
- data/test/partial_reindex_test.rb +0 -58
- data/test/query_test.rb +0 -35
- data/test/records_test.rb +0 -10
- data/test/reindex_test.rb +0 -64
- data/test/reindex_v2_job_test.rb +0 -32
- data/test/routing_test.rb +0 -23
- data/test/should_index_test.rb +0 -32
- data/test/similar_test.rb +0 -28
- data/test/sql_test.rb +0 -214
- data/test/suggest_test.rb +0 -95
- data/test/support/kaminari.yml +0 -21
- data/test/synonyms_test.rb +0 -67
- data/test/test_helper.rb +0 -567
- data/test/where_test.rb +0 -223
data/lib/searchkick/logging.rb
DELETED
@@ -1,242 +0,0 @@
|
|
1
|
-
# based on https://gist.github.com/mnutt/566725
|
2
|
-
require "active_support/core_ext/module/attr_internal"
|
3
|
-
|
4
|
-
module Searchkick
|
5
|
-
module QueryWithInstrumentation
|
6
|
-
def execute_search
|
7
|
-
name = searchkick_klass ? "#{searchkick_klass.name} Search" : "Search"
|
8
|
-
event = {
|
9
|
-
name: name,
|
10
|
-
query: params
|
11
|
-
}
|
12
|
-
ActiveSupport::Notifications.instrument("search.searchkick", event) do
|
13
|
-
super
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module IndexWithInstrumentation
|
19
|
-
def store(record)
|
20
|
-
event = {
|
21
|
-
name: "#{record.searchkick_klass.name} Store",
|
22
|
-
id: search_id(record)
|
23
|
-
}
|
24
|
-
if Searchkick.callbacks_value == :bulk
|
25
|
-
super
|
26
|
-
else
|
27
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
28
|
-
super
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def remove(record)
|
34
|
-
name = record && record.searchkick_klass ? "#{record.searchkick_klass.name} Remove" : "Remove"
|
35
|
-
event = {
|
36
|
-
name: name,
|
37
|
-
id: search_id(record)
|
38
|
-
}
|
39
|
-
if Searchkick.callbacks_value == :bulk
|
40
|
-
super
|
41
|
-
else
|
42
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
43
|
-
super
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def update_record(record, method_name)
|
49
|
-
event = {
|
50
|
-
name: "#{record.searchkick_klass.name} Update",
|
51
|
-
id: search_id(record)
|
52
|
-
}
|
53
|
-
if Searchkick.callbacks_value == :bulk
|
54
|
-
super
|
55
|
-
else
|
56
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
57
|
-
super
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def bulk_index(records)
|
63
|
-
if records.any?
|
64
|
-
event = {
|
65
|
-
name: "#{records.first.searchkick_klass.name} Import",
|
66
|
-
count: records.size
|
67
|
-
}
|
68
|
-
event[:id] = search_id(records.first) if records.size == 1
|
69
|
-
if Searchkick.callbacks_value == :bulk
|
70
|
-
super
|
71
|
-
else
|
72
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
73
|
-
super
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
alias_method :import, :bulk_index
|
79
|
-
|
80
|
-
def bulk_update(records, *args)
|
81
|
-
if records.any?
|
82
|
-
event = {
|
83
|
-
name: "#{records.first.searchkick_klass.name} Update",
|
84
|
-
count: records.size
|
85
|
-
}
|
86
|
-
event[:id] = search_id(records.first) if records.size == 1
|
87
|
-
if Searchkick.callbacks_value == :bulk
|
88
|
-
super
|
89
|
-
else
|
90
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
91
|
-
super
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def bulk_delete(records)
|
98
|
-
if records.any?
|
99
|
-
event = {
|
100
|
-
name: "#{records.first.searchkick_klass.name} Delete",
|
101
|
-
count: records.size
|
102
|
-
}
|
103
|
-
event[:id] = search_id(records.first) if records.size == 1
|
104
|
-
if Searchkick.callbacks_value == :bulk
|
105
|
-
super
|
106
|
-
else
|
107
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
108
|
-
super
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
module IndexerWithInstrumentation
|
116
|
-
def perform
|
117
|
-
if Searchkick.callbacks_value == :bulk
|
118
|
-
event = {
|
119
|
-
name: "Bulk",
|
120
|
-
count: queued_items.size
|
121
|
-
}
|
122
|
-
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
123
|
-
super
|
124
|
-
end
|
125
|
-
else
|
126
|
-
super
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
module SearchkickWithInstrumentation
|
132
|
-
def multi_search(searches, **options)
|
133
|
-
event = {
|
134
|
-
name: "Multi Search",
|
135
|
-
body: searches.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join
|
136
|
-
}
|
137
|
-
ActiveSupport::Notifications.instrument("multi_search.searchkick", event) do
|
138
|
-
super
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/log_subscriber.rb
|
144
|
-
class LogSubscriber < ActiveSupport::LogSubscriber
|
145
|
-
def self.runtime=(value)
|
146
|
-
Thread.current[:searchkick_runtime] = value
|
147
|
-
end
|
148
|
-
|
149
|
-
def self.runtime
|
150
|
-
Thread.current[:searchkick_runtime] ||= 0
|
151
|
-
end
|
152
|
-
|
153
|
-
def self.reset_runtime
|
154
|
-
rt = runtime
|
155
|
-
self.runtime = 0
|
156
|
-
rt
|
157
|
-
end
|
158
|
-
|
159
|
-
def search(event)
|
160
|
-
self.class.runtime += event.duration
|
161
|
-
return unless logger.debug?
|
162
|
-
|
163
|
-
payload = event.payload
|
164
|
-
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
165
|
-
type = payload[:query][:type]
|
166
|
-
index = payload[:query][:index].is_a?(Array) ? payload[:query][:index].join(",") : payload[:query][:index]
|
167
|
-
|
168
|
-
# no easy way to tell which host the client will use
|
169
|
-
host = Searchkick.client.transport.hosts.first
|
170
|
-
debug " #{color(name, YELLOW, true)} curl #{host[:protocol]}://#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?pretty -d '#{payload[:query][:body].to_json}'"
|
171
|
-
end
|
172
|
-
|
173
|
-
def request(event)
|
174
|
-
self.class.runtime += event.duration
|
175
|
-
return unless logger.debug?
|
176
|
-
|
177
|
-
payload = event.payload
|
178
|
-
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
179
|
-
|
180
|
-
debug " #{color(name, YELLOW, true)} #{payload.except(:name).to_json}"
|
181
|
-
end
|
182
|
-
|
183
|
-
def multi_search(event)
|
184
|
-
self.class.runtime += event.duration
|
185
|
-
return unless logger.debug?
|
186
|
-
|
187
|
-
payload = event.payload
|
188
|
-
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
189
|
-
|
190
|
-
# no easy way to tell which host the client will use
|
191
|
-
host = Searchkick.client.transport.hosts.first
|
192
|
-
debug " #{color(name, YELLOW, true)} curl #{host[:protocol]}://#{host[:host]}:#{host[:port]}/_msearch?pretty -d '#{payload[:body]}'"
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/railties/controller_runtime.rb
|
197
|
-
module ControllerRuntime
|
198
|
-
extend ActiveSupport::Concern
|
199
|
-
|
200
|
-
protected
|
201
|
-
|
202
|
-
attr_internal :searchkick_runtime
|
203
|
-
|
204
|
-
def process_action(action, *args)
|
205
|
-
# We also need to reset the runtime before each action
|
206
|
-
# because of queries in middleware or in cases we are streaming
|
207
|
-
# and it won't be cleaned up by the method below.
|
208
|
-
Searchkick::LogSubscriber.reset_runtime
|
209
|
-
super
|
210
|
-
end
|
211
|
-
|
212
|
-
def cleanup_view_runtime
|
213
|
-
searchkick_rt_before_render = Searchkick::LogSubscriber.reset_runtime
|
214
|
-
runtime = super
|
215
|
-
searchkick_rt_after_render = Searchkick::LogSubscriber.reset_runtime
|
216
|
-
self.searchkick_runtime = searchkick_rt_before_render + searchkick_rt_after_render
|
217
|
-
runtime - searchkick_rt_after_render
|
218
|
-
end
|
219
|
-
|
220
|
-
def append_info_to_payload(payload)
|
221
|
-
super
|
222
|
-
payload[:searchkick_runtime] = (searchkick_runtime || 0) + Searchkick::LogSubscriber.reset_runtime
|
223
|
-
end
|
224
|
-
|
225
|
-
module ClassMethods
|
226
|
-
def log_process_action(payload)
|
227
|
-
messages = super
|
228
|
-
runtime = payload[:searchkick_runtime]
|
229
|
-
messages << ("Searchkick: %.1fms" % runtime.to_f) if runtime.to_f > 0
|
230
|
-
messages
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
Searchkick::Query.send(:prepend, Searchkick::QueryWithInstrumentation)
|
236
|
-
Searchkick::Index.send(:prepend, Searchkick::IndexWithInstrumentation)
|
237
|
-
Searchkick::Indexer.send(:prepend, Searchkick::IndexerWithInstrumentation)
|
238
|
-
Searchkick.singleton_class.send(:prepend, Searchkick::SearchkickWithInstrumentation)
|
239
|
-
Searchkick::LogSubscriber.attach_to :searchkick
|
240
|
-
ActiveSupport.on_load(:action_controller) do
|
241
|
-
include Searchkick::ControllerRuntime
|
242
|
-
end
|
data/lib/searchkick/tasks.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require "rake"
|
2
|
-
|
3
|
-
namespace :searchkick do
|
4
|
-
desc "reindex model"
|
5
|
-
task reindex: :environment do
|
6
|
-
if ENV["CLASS"]
|
7
|
-
klass = ENV["CLASS"].constantize rescue nil
|
8
|
-
if klass
|
9
|
-
klass.reindex
|
10
|
-
else
|
11
|
-
abort "Could not find class: #{ENV['CLASS']}"
|
12
|
-
end
|
13
|
-
else
|
14
|
-
abort "USAGE: rake searchkick:reindex CLASS=Product"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
if defined?(Rails)
|
19
|
-
|
20
|
-
namespace :reindex do
|
21
|
-
desc "reindex all models"
|
22
|
-
task all: :environment do
|
23
|
-
Rails.application.eager_load!
|
24
|
-
Searchkick.models.each do |model|
|
25
|
-
puts "Reindexing #{model.name}..."
|
26
|
-
model.reindex
|
27
|
-
end
|
28
|
-
puts "Reindex complete"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
data/searchkick.gemspec
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "searchkick/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "searchkick"
|
8
|
-
spec.version = Searchkick::VERSION
|
9
|
-
spec.authors = ["Andrew Kane"]
|
10
|
-
spec.email = ["andrew@chartkick.com"]
|
11
|
-
spec.description = "Intelligent search made easy"
|
12
|
-
spec.summary = "Searchkick learns what your users are looking for. As more people search, it gets smarter and the results get better. It’s friendly for developers - and magical for your users."
|
13
|
-
spec.homepage = "https://github.com/ankane/searchkick"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files`.split($/)
|
17
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features|benchmark)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_dependency "activemodel", ">= 4.1"
|
22
|
-
spec.add_dependency "elasticsearch", ">= 1"
|
23
|
-
spec.add_dependency "hashie"
|
24
|
-
|
25
|
-
spec.add_development_dependency "bundler"
|
26
|
-
spec.add_development_dependency "rake"
|
27
|
-
spec.add_development_dependency "minitest"
|
28
|
-
end
|
data/test/aggs_test.rb
DELETED
@@ -1,197 +0,0 @@
|
|
1
|
-
require_relative "test_helper"
|
2
|
-
|
3
|
-
class AggsTest < Minitest::Test
|
4
|
-
def setup
|
5
|
-
super
|
6
|
-
store [
|
7
|
-
{name: "Product Show", latitude: 37.7833, longitude: 12.4167, store_id: 1, in_stock: true, color: "blue", price: 21, created_at: 2.days.ago},
|
8
|
-
{name: "Product Hide", latitude: 29.4167, longitude: -98.5000, store_id: 2, in_stock: false, color: "green", price: 25, created_at: 2.days.from_now},
|
9
|
-
{name: "Product B", latitude: 43.9333, longitude: -122.4667, store_id: 2, in_stock: false, color: "red", price: 5, created_at: Time.now},
|
10
|
-
{name: "Foo", latitude: 43.9333, longitude: 12.4667, store_id: 3, in_stock: false, color: "yellow", price: 15, created_at: Time.now}
|
11
|
-
]
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_basic
|
15
|
-
assert_equal ({1 => 1, 2 => 2}), store_agg(aggs: [:store_id])
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_where
|
19
|
-
assert_equal ({1 => 1}), store_agg(aggs: {store_id: {where: {in_stock: true}}})
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_order
|
23
|
-
agg = Product.search("Product", aggs: {color: {order: {"_term" => "desc"}}}).aggs["color"]
|
24
|
-
assert_equal %w(red green blue), agg["buckets"].map { |b| b["key"] }
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_field
|
28
|
-
assert_equal ({1 => 1, 2 => 2}), store_agg(aggs: {store_id: {}})
|
29
|
-
assert_equal ({1 => 1, 2 => 2}), store_agg(aggs: {store_id: {field: "store_id"}})
|
30
|
-
assert_equal ({1 => 1, 2 => 2}), store_agg({aggs: {store_id_new: {field: "store_id"}}}, "store_id_new")
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_min_doc_count
|
34
|
-
assert_equal ({2 => 2}), store_agg(aggs: {store_id: {min_doc_count: 2}})
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_no_aggs
|
38
|
-
assert_nil Product.search("*").aggs
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_limit
|
42
|
-
agg = Product.search("Product", aggs: {store_id: {limit: 1}}).aggs["store_id"]
|
43
|
-
assert_equal 1, agg["buckets"].size
|
44
|
-
# assert_equal 3, agg["doc_count"]
|
45
|
-
assert_equal(1, agg["sum_other_doc_count"])
|
46
|
-
end
|
47
|
-
|
48
|
-
def test_ranges
|
49
|
-
price_ranges = [{to: 10}, {from: 10, to: 20}, {from: 20}]
|
50
|
-
agg = Product.search("Product", aggs: {price: {ranges: price_ranges}}).aggs["price"]
|
51
|
-
|
52
|
-
assert_equal 3, agg["buckets"].size
|
53
|
-
assert_equal 10.0, agg["buckets"][0]["to"]
|
54
|
-
assert_equal 20.0, agg["buckets"][2]["from"]
|
55
|
-
assert_equal 1, agg["buckets"][0]["doc_count"]
|
56
|
-
assert_equal 0, agg["buckets"][1]["doc_count"]
|
57
|
-
assert_equal 2, agg["buckets"][2]["doc_count"]
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_date_ranges
|
61
|
-
ranges = [{to: 1.day.ago}, {from: 1.day.ago, to: 1.day.from_now}, {from: 1.day.from_now}]
|
62
|
-
agg = Product.search("Product", aggs: {created_at: {date_ranges: ranges}}).aggs["created_at"]
|
63
|
-
|
64
|
-
assert_equal 1, agg["buckets"][0]["doc_count"]
|
65
|
-
assert_equal 1, agg["buckets"][1]["doc_count"]
|
66
|
-
assert_equal 1, agg["buckets"][2]["doc_count"]
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_query_where
|
70
|
-
assert_equal ({1 => 1}), store_agg(where: {in_stock: true}, aggs: [:store_id])
|
71
|
-
end
|
72
|
-
|
73
|
-
def test_two_wheres
|
74
|
-
assert_equal ({2 => 1}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false}}})
|
75
|
-
end
|
76
|
-
|
77
|
-
def test_where_override
|
78
|
-
assert_equal ({}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false, color: "blue"}}})
|
79
|
-
assert_equal ({2 => 1}), store_agg(where: {color: "blue"}, aggs: {store_id: {where: {in_stock: false, color: "red"}}})
|
80
|
-
end
|
81
|
-
|
82
|
-
def test_skip
|
83
|
-
assert_equal ({1 => 1, 2 => 2}), store_agg(where: {store_id: 2}, aggs: [:store_id])
|
84
|
-
end
|
85
|
-
|
86
|
-
def test_skip_complex
|
87
|
-
assert_equal ({1 => 1, 2 => 1}), store_agg(where: {store_id: 2, price: {gt: 5}}, aggs: [:store_id])
|
88
|
-
end
|
89
|
-
|
90
|
-
def test_multiple
|
91
|
-
assert_equal ({"store_id" => {1 => 1, 2 => 2}, "color" => {"blue" => 1, "green" => 1, "red" => 1}}), store_multiple_aggs(aggs: [:store_id, :color])
|
92
|
-
end
|
93
|
-
|
94
|
-
def test_smart_aggs_false
|
95
|
-
assert_equal ({2 => 2}), store_agg(where: {color: "red"}, aggs: {store_id: {where: {in_stock: false}}}, smart_aggs: false)
|
96
|
-
assert_equal ({2 => 2}), store_agg(where: {color: "blue"}, aggs: {store_id: {where: {in_stock: false}}}, smart_aggs: false)
|
97
|
-
end
|
98
|
-
|
99
|
-
def test_aggs_group_by_date
|
100
|
-
store [{name: "Old Product", created_at: 3.years.ago}]
|
101
|
-
products =
|
102
|
-
Product.search("Product", {
|
103
|
-
where: {
|
104
|
-
created_at: {lt: Time.now}
|
105
|
-
},
|
106
|
-
aggs: {
|
107
|
-
products_per_year: {
|
108
|
-
date_histogram: {
|
109
|
-
field: :created_at,
|
110
|
-
interval: :year
|
111
|
-
}
|
112
|
-
}
|
113
|
-
}
|
114
|
-
})
|
115
|
-
|
116
|
-
assert_equal 4, products.aggs["products_per_year"]["buckets"].size
|
117
|
-
end
|
118
|
-
|
119
|
-
def test_aggs_avg
|
120
|
-
products =
|
121
|
-
Product.search("*", {
|
122
|
-
aggs: {
|
123
|
-
avg_price: {
|
124
|
-
avg: {
|
125
|
-
field: :price
|
126
|
-
}
|
127
|
-
}
|
128
|
-
}
|
129
|
-
})
|
130
|
-
assert_equal 16.5, products.aggs["avg_price"]["value"]
|
131
|
-
end
|
132
|
-
|
133
|
-
def test_aggs_cardinality
|
134
|
-
products =
|
135
|
-
Product.search("*", {
|
136
|
-
aggs: {
|
137
|
-
total_stores: {
|
138
|
-
cardinality: {
|
139
|
-
field: :store_id
|
140
|
-
}
|
141
|
-
}
|
142
|
-
}
|
143
|
-
})
|
144
|
-
assert_equal 3, products.aggs["total_stores"]["value"]
|
145
|
-
end
|
146
|
-
|
147
|
-
def test_aggs_min_max
|
148
|
-
products =
|
149
|
-
Product.search("*", {
|
150
|
-
aggs: {
|
151
|
-
min_price: {
|
152
|
-
min: {
|
153
|
-
field: :price
|
154
|
-
}
|
155
|
-
},
|
156
|
-
max_price: {
|
157
|
-
max: {
|
158
|
-
field: :price
|
159
|
-
}
|
160
|
-
}
|
161
|
-
}
|
162
|
-
})
|
163
|
-
assert_equal 5, products.aggs["min_price"]["value"]
|
164
|
-
assert_equal 25, products.aggs["max_price"]["value"]
|
165
|
-
end
|
166
|
-
|
167
|
-
def test_aggs_sum
|
168
|
-
products =
|
169
|
-
Product.search("*", {
|
170
|
-
aggs: {
|
171
|
-
sum_price: {
|
172
|
-
sum: {
|
173
|
-
field: :price
|
174
|
-
}
|
175
|
-
}
|
176
|
-
}
|
177
|
-
})
|
178
|
-
assert_equal 66, products.aggs["sum_price"]["value"]
|
179
|
-
end
|
180
|
-
|
181
|
-
protected
|
182
|
-
|
183
|
-
def buckets_as_hash(agg)
|
184
|
-
Hash[agg["buckets"].map { |v| [v["key"], v["doc_count"]] }]
|
185
|
-
end
|
186
|
-
|
187
|
-
def store_agg(options, agg_key = "store_id")
|
188
|
-
buckets = Product.search("Product", options).aggs[agg_key]
|
189
|
-
buckets_as_hash(buckets)
|
190
|
-
end
|
191
|
-
|
192
|
-
def store_multiple_aggs(options)
|
193
|
-
Hash[Product.search("Product", options).aggs.map do |field, filtered_agg|
|
194
|
-
[field, buckets_as_hash(filtered_agg)]
|
195
|
-
end]
|
196
|
-
end
|
197
|
-
end
|
data/test/autocomplete_test.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
require_relative "test_helper"
|
2
|
-
|
3
|
-
class AutocompleteTest < Minitest::Test
|
4
|
-
def test_autocomplete
|
5
|
-
store_names ["Hummus"]
|
6
|
-
assert_search "hum", ["Hummus"], match: :text_start
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_autocomplete_two_words
|
10
|
-
store_names ["Organic Hummus"]
|
11
|
-
assert_search "hum", [], match: :text_start
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_autocomplete_fields
|
15
|
-
store_names ["Hummus"]
|
16
|
-
assert_search "hum", ["Hummus"], match: :text_start, fields: [:name]
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_text_start
|
20
|
-
store_names ["Where in the World is Carmen San Diego"]
|
21
|
-
assert_search "where in the world is", ["Where in the World is Carmen San Diego"], fields: [{name: :text_start}]
|
22
|
-
assert_search "in the world", [], fields: [{name: :text_start}]
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_text_middle
|
26
|
-
store_names ["Where in the World is Carmen San Diego"]
|
27
|
-
assert_search "where in the world is", ["Where in the World is Carmen San Diego"], fields: [{name: :text_middle}]
|
28
|
-
assert_search "n the wor", ["Where in the World is Carmen San Diego"], fields: [{name: :text_middle}]
|
29
|
-
assert_search "men san diego", ["Where in the World is Carmen San Diego"], fields: [{name: :text_middle}]
|
30
|
-
assert_search "world carmen", [], fields: [{name: :text_middle}]
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_text_end
|
34
|
-
store_names ["Where in the World is Carmen San Diego"]
|
35
|
-
assert_search "men san diego", ["Where in the World is Carmen San Diego"], fields: [{name: :text_end}]
|
36
|
-
assert_search "carmen san", [], fields: [{name: :text_end}]
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_word_start
|
40
|
-
store_names ["Where in the World is Carmen San Diego"]
|
41
|
-
assert_search "car san wor", ["Where in the World is Carmen San Diego"], fields: [{name: :word_start}]
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_word_middle
|
45
|
-
store_names ["Where in the World is Carmen San Diego"]
|
46
|
-
assert_search "orl", ["Where in the World is Carmen San Diego"], fields: [{name: :word_middle}]
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_word_end
|
50
|
-
store_names ["Where in the World is Carmen San Diego"]
|
51
|
-
assert_search "rld men ego", ["Where in the World is Carmen San Diego"], fields: [{name: :word_end}]
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_word_start_multiple_words
|
55
|
-
store_names ["Dark Grey", "Dark Blue"]
|
56
|
-
assert_search "dark grey", ["Dark Grey"], fields: [{name: :word_start}]
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_word_start_exact
|
60
|
-
store_names ["Back Scratcher", "Backpack"]
|
61
|
-
assert_order "back", ["Back Scratcher", "Backpack"], fields: [{name: :word_start}]
|
62
|
-
end
|
63
|
-
|
64
|
-
def test_word_start_exact_martin
|
65
|
-
store_names ["Martina", "Martin"]
|
66
|
-
assert_order "martin", ["Martin", "Martina"], fields: [{name: :word_start}]
|
67
|
-
end
|
68
|
-
|
69
|
-
# TODO find a better place
|
70
|
-
|
71
|
-
def test_exact
|
72
|
-
store_names ["hi@example.org"]
|
73
|
-
assert_search "hi@example.org", ["hi@example.org"], fields: [{name: :exact}]
|
74
|
-
end
|
75
|
-
end
|