searchkick 1.0.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile +1 -0
- data/README.md +26 -1
- data/lib/searchkick/query.rb +58 -37
- data/lib/searchkick/results.rb +12 -6
- data/lib/searchkick/version.rb +1 -1
- data/test/aggs_test.rb +4 -0
- data/test/index_test.rb +6 -1
- data/test/match_test.rb +12 -0
- data/test/query_test.rb +1 -0
- data/test/sql_test.rb +2 -2
- data/test/test_helper.rb +3 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a27fb3f9f9fd1773e06eec376ec0677ef60aca65
|
4
|
+
data.tar.gz: 03c0d4c6dabe39b5e9e7beda4f0365e0a3f71472
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 722dce0a1e9366ed296a9003b435deddc6f6f4c43a90771644bd0f6558d969c348b42279e4c6d04e5ef3cfa188abc75b26a483c6d3d022bd175453135e78f8d6
|
7
|
+
data.tar.gz: e59ff3b1b239b5a9d6d96f3c8482bfece495d936caef69397958292be7069c6e130c6c1bb7aec8b4fb5c0b412914eca4ad9d3af80dca5bf4b9976f9e76496de9
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -315,6 +315,22 @@ Or turn off misspellings with:
|
|
315
315
|
Product.search "zuchini", misspellings: false # no zucchini
|
316
316
|
```
|
317
317
|
|
318
|
+
### Emoji
|
319
|
+
|
320
|
+
Make :ice_cream::cake: match `ice cream cake`!
|
321
|
+
|
322
|
+
Add this line to your application’s Gemfile:
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
gem 'gemoji-parser'
|
326
|
+
```
|
327
|
+
|
328
|
+
And use:
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
Product.search "[emoji go here]", emoji: true
|
332
|
+
```
|
333
|
+
|
318
334
|
### Indexing
|
319
335
|
|
320
336
|
Control what data is indexed with the `search_data` method. Call `Product.reindex` after changing this method.
|
@@ -644,7 +660,7 @@ Product.search "*", aggs: {price: {ranges: price_ranges}}
|
|
644
660
|
|
645
661
|
### Facets [deprecated]
|
646
662
|
|
647
|
-
Facets have been deprecated in favor of aggregations as of Searchkick 0.
|
663
|
+
Facets have been deprecated in favor of aggregations as of Searchkick 1.0. See [how to upgrade](#moving-from-facets).
|
648
664
|
|
649
665
|
```ruby
|
650
666
|
products = Product.search "chuck taylor", facets: [:product_type, :gender, :brand]
|
@@ -1004,6 +1020,8 @@ And use the `body` option to search:
|
|
1004
1020
|
products = Product.search body: {match: {name: "milk"}}
|
1005
1021
|
```
|
1006
1022
|
|
1023
|
+
**Note:** This replaces the entire body, so other options are ignored.
|
1024
|
+
|
1007
1025
|
View the response with:
|
1008
1026
|
|
1009
1027
|
```ruby
|
@@ -1135,6 +1153,13 @@ Create index without importing
|
|
1135
1153
|
Product.reindex(import: false)
|
1136
1154
|
```
|
1137
1155
|
|
1156
|
+
Lazy searching
|
1157
|
+
|
1158
|
+
```ruby
|
1159
|
+
products = Product.search("carrots", execute: false)
|
1160
|
+
products.each { ... } # search not executed until here
|
1161
|
+
```
|
1162
|
+
|
1138
1163
|
Make fields unsearchable but include in the source
|
1139
1164
|
|
1140
1165
|
```ruby
|
data/lib/searchkick/query.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
module Searchkick
|
2
2
|
class Query
|
3
|
+
extend Forwardable
|
4
|
+
|
3
5
|
attr_reader :klass, :term, :options
|
4
6
|
attr_accessor :body
|
5
7
|
|
8
|
+
def_delegators :execute, :map, :each, :any?, :empty?, :size, :length, :slice, :[], :to_ary
|
9
|
+
|
6
10
|
def initialize(klass, term, options = {})
|
7
11
|
if term.is_a?(Hash)
|
8
12
|
options = term
|
@@ -11,6 +15,10 @@ module Searchkick
|
|
11
15
|
term = term.to_s
|
12
16
|
end
|
13
17
|
|
18
|
+
if options[:emoji]
|
19
|
+
term = EmojiParser.parse_unicode(term) { |e| " #{e.name} " }.strip
|
20
|
+
end
|
21
|
+
|
14
22
|
@klass = klass
|
15
23
|
@term = term
|
16
24
|
@options = options
|
@@ -480,49 +488,62 @@ module Searchkick
|
|
480
488
|
end
|
481
489
|
|
482
490
|
def execute
|
483
|
-
begin
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
491
|
+
@execute ||= begin
|
492
|
+
begin
|
493
|
+
response = Searchkick.client.search(params)
|
494
|
+
rescue => e # TODO rescue type
|
495
|
+
status_code = e.message[1..3].to_i
|
496
|
+
if status_code == 404
|
497
|
+
raise MissingIndexError, "Index missing - run #{searchkick_klass.name}.reindex"
|
498
|
+
elsif status_code == 500 && (
|
499
|
+
e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") ||
|
500
|
+
e.message.include?("No query registered for [multi_match]") ||
|
501
|
+
e.message.include?("[match] query does not support [cutoff_frequency]]") ||
|
502
|
+
e.message.include?("No query registered for [function_score]]")
|
503
|
+
)
|
504
|
+
|
505
|
+
raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 1.0 or greater"
|
506
|
+
elsif status_code == 400
|
507
|
+
if e.message.include?("[multi_match] analyzer [searchkick_search] not found")
|
508
|
+
raise InvalidQueryError, "Bad mapping - run #{searchkick_klass.name}.reindex"
|
509
|
+
else
|
510
|
+
raise InvalidQueryError, e.message
|
511
|
+
end
|
500
512
|
else
|
501
|
-
raise
|
513
|
+
raise e
|
502
514
|
end
|
503
|
-
else
|
504
|
-
raise e
|
505
515
|
end
|
506
|
-
end
|
507
516
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
517
|
+
# apply facet limit in client due to
|
518
|
+
# https://github.com/elasticsearch/elasticsearch/issues/1305
|
519
|
+
@facet_limits.each do |field, limit|
|
520
|
+
field = field.to_s
|
521
|
+
facet = response["facets"][field]
|
522
|
+
response["facets"][field]["terms"] = facet["terms"].first(limit)
|
523
|
+
response["facets"][field]["other"] = facet["total"] - facet["terms"].sum { |term| term["count"] }
|
524
|
+
end
|
525
|
+
|
526
|
+
opts = {
|
527
|
+
page: @page,
|
528
|
+
per_page: @per_page,
|
529
|
+
padding: @padding,
|
530
|
+
load: @load,
|
531
|
+
includes: options[:include] || options[:includes],
|
532
|
+
json: !options[:json].nil?
|
533
|
+
}
|
534
|
+
Searchkick::Results.new(searchkick_klass, response, opts)
|
515
535
|
end
|
536
|
+
end
|
516
537
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
}
|
525
|
-
|
538
|
+
def to_curl
|
539
|
+
query = params
|
540
|
+
type = query[:type]
|
541
|
+
index = query[:index].is_a?(Array) ? query[:index].join(",") : query[:index]
|
542
|
+
|
543
|
+
# no easy way to tell which host the client will use
|
544
|
+
host = Searchkick.client.transport.hosts.first
|
545
|
+
credentials = (host[:user] || host[:password]) ? "#{host[:user]}:#{host[:password]}@" : nil
|
546
|
+
"curl #{host[:protocol]}://#{credentials}#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?pretty -d '#{query[:body].to_json}'"
|
526
547
|
end
|
527
548
|
|
528
549
|
private
|
data/lib/searchkick/results.rb
CHANGED
@@ -75,14 +75,20 @@ module Searchkick
|
|
75
75
|
response["facets"]
|
76
76
|
end
|
77
77
|
|
78
|
+
def aggregations
|
79
|
+
response["aggregations"]
|
80
|
+
end
|
81
|
+
|
78
82
|
def aggs
|
79
83
|
@aggs ||= begin
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
if aggregations
|
85
|
+
aggregations.dup.each do |field, filtered_agg|
|
86
|
+
buckets = filtered_agg[field]
|
87
|
+
# move the buckets one level above into the field hash
|
88
|
+
if buckets
|
89
|
+
filtered_agg.delete(field)
|
90
|
+
filtered_agg.merge!(buckets)
|
91
|
+
end
|
86
92
|
end
|
87
93
|
end
|
88
94
|
end
|
data/lib/searchkick/version.rb
CHANGED
data/test/aggs_test.rb
CHANGED
@@ -25,6 +25,10 @@ class AggsTest < Minitest::Test
|
|
25
25
|
assert_equal ({1 => 1, 2 => 2}), store_agg({aggs: {store_id_new: {field: "store_id"}}}, "store_id_new")
|
26
26
|
end
|
27
27
|
|
28
|
+
def test_no_aggs
|
29
|
+
assert_nil Product.search("*").aggs
|
30
|
+
end
|
31
|
+
|
28
32
|
def test_limit
|
29
33
|
agg = Product.search("Product", aggs: {store_id: {limit: 1}}).aggs["store_id"]
|
30
34
|
assert_equal 1, agg["buckets"].size
|
data/test/index_test.rb
CHANGED
@@ -107,6 +107,11 @@ class IndexTest < Minitest::Test
|
|
107
107
|
assert_raises(Searchkick::DangerousOperation) { Product.where(id: [1, 2, 3]).reindex }
|
108
108
|
end
|
109
109
|
|
110
|
+
def test_dangerous_index_associations
|
111
|
+
Store.create!(name: "Test")
|
112
|
+
assert_raises(Searchkick::DangerousOperation) { Store.first.products.reindex }
|
113
|
+
end
|
114
|
+
|
110
115
|
def test_dangerous_reindex_accepted
|
111
116
|
store_names ["Product A", "Product B"]
|
112
117
|
Product.where(name: "Product A").reindex(accept_danger: true)
|
@@ -124,7 +129,7 @@ class IndexTest < Minitest::Test
|
|
124
129
|
store_names ["Product A"]
|
125
130
|
raise ActiveRecord::Rollback
|
126
131
|
end
|
127
|
-
assert_search "product", []
|
132
|
+
assert_search "product", []
|
128
133
|
end
|
129
134
|
end
|
130
135
|
end
|
data/test/match_test.rb
CHANGED
@@ -197,4 +197,16 @@ class MatchTest < Minitest::Test
|
|
197
197
|
]
|
198
198
|
assert_search "almond", []
|
199
199
|
end
|
200
|
+
|
201
|
+
def test_emoji
|
202
|
+
skip unless defined?(EmojiParser)
|
203
|
+
store_names ["Banana"]
|
204
|
+
assert_search "🍌", ["Banana"], emoji: true
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_emoji_multiple
|
208
|
+
skip unless defined?(EmojiParser)
|
209
|
+
store_names ["Ice Cream Cake"]
|
210
|
+
assert_search "🍨🍰", ["Ice Cream Cake"], emoji: true
|
211
|
+
end
|
200
212
|
end
|
data/test/query_test.rb
CHANGED
@@ -7,6 +7,7 @@ class QueryTest < Minitest::Test
|
|
7
7
|
# query.body = {query: {match_all: {}}}
|
8
8
|
# query.body = {query: {match: {name: "Apple"}}}
|
9
9
|
query.body[:query] = {match_all: {}}
|
10
|
+
assert_equal ["Apple", "Milk"], query.map(&:name).sort
|
10
11
|
assert_equal ["Apple", "Milk"], query.execute.map(&:name).sort
|
11
12
|
end
|
12
13
|
end
|
data/test/sql_test.rb
CHANGED
@@ -213,12 +213,12 @@ class SqlTest < Minitest::Test
|
|
213
213
|
end
|
214
214
|
|
215
215
|
def test_order_ignore_unmapped
|
216
|
-
assert_order "product", [], order: {not_mapped: {ignore_unmapped: true}}
|
216
|
+
assert_order "product", [], order: {not_mapped: {ignore_unmapped: true}}
|
217
217
|
end
|
218
218
|
|
219
219
|
def test_order_array
|
220
220
|
store [{name: "San Francisco", latitude: 37.7833, longitude: -122.4167}]
|
221
|
-
assert_order "francisco", ["San Francisco"], order: [{_geo_distance: {location: "0,0"}}]
|
221
|
+
assert_order "francisco", ["San Francisco"], order: [{_geo_distance: {location: "0,0"}}]
|
222
222
|
end
|
223
223
|
|
224
224
|
def test_partial
|
data/test/test_helper.rb
CHANGED
@@ -76,6 +76,7 @@ if defined?(Mongoid)
|
|
76
76
|
|
77
77
|
class Store
|
78
78
|
include Mongoid::Document
|
79
|
+
has_many :products
|
79
80
|
|
80
81
|
field :name
|
81
82
|
end
|
@@ -178,6 +179,7 @@ else
|
|
178
179
|
end
|
179
180
|
|
180
181
|
class Store < ActiveRecord::Base
|
182
|
+
has_many :products
|
181
183
|
end
|
182
184
|
|
183
185
|
class Animal < ActiveRecord::Base
|
@@ -257,6 +259,7 @@ end
|
|
257
259
|
Product.searchkick_index.delete if Product.searchkick_index.exists?
|
258
260
|
Product.reindex
|
259
261
|
Product.reindex # run twice for both index paths
|
262
|
+
Product.create!(name: "Set mapping")
|
260
263
|
|
261
264
|
Store.reindex
|
262
265
|
Animal.reindex
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchkick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|