searchkick 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|