searchkick 2.0.4 → 2.1.0
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 +5 -0
- data/README.md +143 -54
- data/lib/searchkick/bulk_reindex_job.rb +6 -1
- data/lib/searchkick/index.rb +77 -45
- data/lib/searchkick/logging.rb +38 -2
- data/lib/searchkick/process_batch_job.rb +2 -2
- data/lib/searchkick/process_queue_job.rb +1 -1
- data/lib/searchkick/reindex_queue.rb +2 -0
- data/lib/searchkick/version.rb +1 -1
- data/test/callbacks_test.rb +1 -1
- data/test/gemfiles/mongoid5.gemfile +1 -1
- data/test/index_test.rb +1 -1
- data/test/reindex_test.rb +5 -6
- data/test/routing_test.rb +10 -0
- data/test/test_helper.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95d822316595ca2806bc9f2e3e44644ee2ab5fd8
|
4
|
+
data.tar.gz: 27f86dc98a28378ee489b5adf7bfd11be054baf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb18990e391306eb6d3f9a6193b8c14acf8cb22691f0a470d7c47b40c6146a58bc7babc6250378dfadf86b85f5f6846bd79f5302969b54de632df77aa7a55809
|
7
|
+
data.tar.gz: 0983c46e8eda2d402cc632a9849e97fca61f3036cfa96378ff81921dc6901416671b6192a184a23dc04615534422f79a7f00c2694c3b9cb46d7b38f7a2410345
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,18 @@ Plus:
|
|
29
29
|
|
30
30
|
**Searchkick 2.0 was just released!** See [notable changes](#200).
|
31
31
|
|
32
|
-
##
|
32
|
+
## Contents
|
33
|
+
|
34
|
+
- [Getting Started](#getting-started)
|
35
|
+
- [Querying](#querying)
|
36
|
+
- [Indexing](#indexing)
|
37
|
+
- [Aggregations](#aggregations)
|
38
|
+
- [Deployment](#deployment)
|
39
|
+
- [Performance](#performance)
|
40
|
+
- [Elasticsearch DSL](#advanced)
|
41
|
+
- [Reference](#reference)
|
42
|
+
|
43
|
+
## Getting Started
|
33
44
|
|
34
45
|
[Install Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html). For Homebrew, use:
|
35
46
|
|
@@ -73,7 +84,7 @@ end
|
|
73
84
|
|
74
85
|
Searchkick supports the complete [Elasticsearch Search API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html). As your search becomes more advanced, we recommend you use the [Elasticsearch DSL](#advanced) for maximum flexibility.
|
75
86
|
|
76
|
-
|
87
|
+
## Querying
|
77
88
|
|
78
89
|
Query like SQL
|
79
90
|
|
@@ -381,7 +392,7 @@ And use:
|
|
381
392
|
Product.search "🍨🍰", emoji: true
|
382
393
|
```
|
383
394
|
|
384
|
-
|
395
|
+
## Indexing
|
385
396
|
|
386
397
|
Control what data is indexed with the `search_data` method. Call `Product.reindex` after changing this method.
|
387
398
|
|
@@ -425,6 +436,8 @@ If a reindex is interrupted, you can resume it with:
|
|
425
436
|
Product.reindex(resume: true)
|
426
437
|
```
|
427
438
|
|
439
|
+
For large data sets, try [parallel reindexing](#parallel-reindexing).
|
440
|
+
|
428
441
|
### To Reindex, or Not to Reindex
|
429
442
|
|
430
443
|
#### Reindex
|
@@ -439,7 +452,7 @@ Product.reindex(resume: true)
|
|
439
452
|
|
440
453
|
### Stay Synced
|
441
454
|
|
442
|
-
There are
|
455
|
+
There are four strategies for keeping the index synced with your database.
|
443
456
|
|
444
457
|
1. Immediate (default)
|
445
458
|
|
@@ -457,7 +470,11 @@ There are three strategies for keeping the index synced with your database.
|
|
457
470
|
|
458
471
|
And [install Active Job](https://github.com/ankane/activejob_backport) for Rails 4.1 and below. Jobs are added to a queue named `searchkick`.
|
459
472
|
|
460
|
-
3.
|
473
|
+
3. Queuing
|
474
|
+
|
475
|
+
Push ids of records that need updated to a queue and reindex in the background in batches. This is more performant than the asynchronous method, which updates records individually. See [how to set up](#queuing).
|
476
|
+
|
477
|
+
4. Manual
|
461
478
|
|
462
479
|
Turn off automatic syncing
|
463
480
|
|
@@ -543,6 +560,8 @@ Reindex and set up a cron job to add new conversions daily.
|
|
543
560
|
rake searchkick:reindex CLASS=Product
|
544
561
|
```
|
545
562
|
|
563
|
+
**Note:** For a more performant (but more advanced) approach, check out [performant conversions](#performant-conversions).
|
564
|
+
|
546
565
|
### Personalized Results
|
547
566
|
|
548
567
|
Order results differently for each user. For example, show a user’s previously purchased products before other results.
|
@@ -962,23 +981,23 @@ Product.search("soap", explain: true).response
|
|
962
981
|
See how Elasticsearch tokenizes your queries with:
|
963
982
|
|
964
983
|
```ruby
|
965
|
-
Product.
|
984
|
+
Product.search_index.tokens("Dish Washer Soap", analyzer: "searchkick_index")
|
966
985
|
# ["dish", "dishwash", "washer", "washersoap", "soap"]
|
967
986
|
|
968
|
-
Product.
|
987
|
+
Product.search_index.tokens("dishwasher soap", analyzer: "searchkick_search")
|
969
988
|
# ["dishwashersoap"] - no match
|
970
989
|
|
971
|
-
Product.
|
990
|
+
Product.search_index.tokens("dishwasher soap", analyzer: "searchkick_search2")
|
972
991
|
# ["dishwash", "soap"] - match!!
|
973
992
|
```
|
974
993
|
|
975
994
|
Partial matches
|
976
995
|
|
977
996
|
```ruby
|
978
|
-
Product.
|
997
|
+
Product.search_index.tokens("San Diego", analyzer: "searchkick_word_start_index")
|
979
998
|
# ["s", "sa", "san", "d", "di", "die", "dieg", "diego"]
|
980
999
|
|
981
|
-
Product.
|
1000
|
+
Product.search_index.tokens("dieg", analyzer: "searchkick_word_search")
|
982
1001
|
# ["dieg"] - match!!
|
983
1002
|
```
|
984
1003
|
|
@@ -1136,29 +1155,7 @@ class Product < ActiveRecord::Base
|
|
1136
1155
|
end
|
1137
1156
|
```
|
1138
1157
|
|
1139
|
-
###
|
1140
|
-
|
1141
|
-
Searchkick supports [Elasticsearch’s routing feature](https://www.elastic.co/blog/customizing-your-document-routing), which can significantly speed up searches.
|
1142
|
-
|
1143
|
-
```ruby
|
1144
|
-
class Business < ActiveRecord::Base
|
1145
|
-
searchkick routing: true
|
1146
|
-
|
1147
|
-
def search_routing
|
1148
|
-
city_id
|
1149
|
-
end
|
1150
|
-
end
|
1151
|
-
```
|
1152
|
-
|
1153
|
-
Reindex and search with:
|
1154
|
-
|
1155
|
-
```ruby
|
1156
|
-
Business.search "ice cream", routing: params[:city_id]
|
1157
|
-
```
|
1158
|
-
|
1159
|
-
## Large Data Sets
|
1160
|
-
|
1161
|
-
### Background Reindexing [experimental, ActiveRecord only]
|
1158
|
+
### Parallel Reindexing
|
1162
1159
|
|
1163
1160
|
For large data sets, you can use background jobs to parallelize reindexing.
|
1164
1161
|
|
@@ -1170,7 +1167,7 @@ Product.reindex(async: true)
|
|
1170
1167
|
Once the jobs complete, promote the new index with:
|
1171
1168
|
|
1172
1169
|
```ruby
|
1173
|
-
Product.
|
1170
|
+
Product.search_index.promote(index_name)
|
1174
1171
|
```
|
1175
1172
|
|
1176
1173
|
You can optionally track the status with Redis:
|
@@ -1185,9 +1182,9 @@ And use:
|
|
1185
1182
|
Searchkick.reindex_status(index_name)
|
1186
1183
|
```
|
1187
1184
|
|
1188
|
-
###
|
1185
|
+
### Queuing
|
1189
1186
|
|
1190
|
-
|
1187
|
+
Push ids of records needing reindexed to a queue and reindex in bulk for better performance. First, set up Redis in an initializer.
|
1191
1188
|
|
1192
1189
|
```ruby
|
1193
1190
|
Searchkick.redis = Redis.new
|
@@ -1210,14 +1207,121 @@ Searchkick::ProcessQueueJob.perform_later(class_name: "Product")
|
|
1210
1207
|
You can check the queue length with:
|
1211
1208
|
|
1212
1209
|
```ruby
|
1213
|
-
Product.
|
1210
|
+
Product.search_index.reindex_queue.length
|
1214
1211
|
```
|
1215
1212
|
|
1216
1213
|
For more tips, check out [Keeping Elasticsearch in Sync](https://www.elastic.co/blog/found-keeping-elasticsearch-in-sync).
|
1217
1214
|
|
1215
|
+
### Routing
|
1216
|
+
|
1217
|
+
Searchkick supports [Elasticsearch’s routing feature](https://www.elastic.co/blog/customizing-your-document-routing), which can significantly speed up searches.
|
1218
|
+
|
1219
|
+
```ruby
|
1220
|
+
class Business < ActiveRecord::Base
|
1221
|
+
searchkick routing: true
|
1222
|
+
|
1223
|
+
def search_routing
|
1224
|
+
city_id
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
```
|
1228
|
+
|
1229
|
+
Reindex and search with:
|
1230
|
+
|
1231
|
+
```ruby
|
1232
|
+
Business.search "ice cream", routing: params[:city_id]
|
1233
|
+
```
|
1234
|
+
|
1235
|
+
### Partial Reindexing
|
1236
|
+
|
1237
|
+
Reindex a subset of attributes to reduce time spent generating search data and cut down on network traffic.
|
1238
|
+
|
1239
|
+
```ruby
|
1240
|
+
class Product < ActiveRecord::Base
|
1241
|
+
def search_data
|
1242
|
+
{
|
1243
|
+
name: name
|
1244
|
+
}.merge(search_prices)
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
def search_prices
|
1248
|
+
{
|
1249
|
+
price: price,
|
1250
|
+
sale_price: sale_price
|
1251
|
+
}
|
1252
|
+
end
|
1253
|
+
end
|
1254
|
+
```
|
1255
|
+
|
1256
|
+
And use:
|
1257
|
+
|
1258
|
+
```ruby
|
1259
|
+
Product.reindex(:search_prices)
|
1260
|
+
```
|
1261
|
+
|
1262
|
+
### Performant Conversions
|
1263
|
+
|
1264
|
+
Split out conversions into a separate method so you can use partial reindexing, and cache conversions to prevent N+1 queries. Be sure to use a centralized cache store like Memcached or Redis.
|
1265
|
+
|
1266
|
+
```ruby
|
1267
|
+
class Product < ActiveRecord::Base
|
1268
|
+
def search_data
|
1269
|
+
{
|
1270
|
+
name: name
|
1271
|
+
}.merge(search_conversions)
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
def search_conversions
|
1275
|
+
{
|
1276
|
+
conversions: Rails.cache.read("search_conversions:#{self.class.name}:#{id}") || {}
|
1277
|
+
}
|
1278
|
+
end
|
1279
|
+
end
|
1280
|
+
```
|
1281
|
+
|
1282
|
+
Create a job to update the cache and reindex records with new conversions.
|
1283
|
+
|
1284
|
+
```ruby
|
1285
|
+
class ReindexConversionsJob < ActiveJob::Base
|
1286
|
+
def perform(class_name)
|
1287
|
+
# get records that have a recent conversion
|
1288
|
+
recently_converted_ids =
|
1289
|
+
Searchjoy::Search.where("convertable_type = ? AND converted_at > ?", class_name, 1.day.ago)
|
1290
|
+
.order(:convertable_id).uniq.pluck(:convertable_id)
|
1291
|
+
|
1292
|
+
# split into groups
|
1293
|
+
recently_converted_ids.in_groups_of(1000, false) do |ids|
|
1294
|
+
# fetch conversions and group by record
|
1295
|
+
conversions_by_record = {}
|
1296
|
+
conversions =
|
1297
|
+
Searchjoy::Search.where(convertable_id: ids, convertable_type: class_name)
|
1298
|
+
.group(:convertable_id, :query).uniq.count(:user_id)
|
1299
|
+
|
1300
|
+
conversions.each do |(id, query), count|
|
1301
|
+
(conversions_by_record[id] ||= {})[query] = count
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
# write to cache
|
1305
|
+
conversions_by_record.each do |id, conversions|
|
1306
|
+
Rails.cache.write("search_conversions:#{class_name}:#{id}", conversions)
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
# partial reindex
|
1310
|
+
class_name.constantize.where(id: ids).reindex(:search_conversions)
|
1311
|
+
end
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
```
|
1315
|
+
|
1316
|
+
Run the job with:
|
1317
|
+
|
1318
|
+
```ruby
|
1319
|
+
ReindexConversionsJob.perform_later("Product")
|
1320
|
+
```
|
1321
|
+
|
1218
1322
|
## Advanced
|
1219
1323
|
|
1220
|
-
|
1324
|
+
Searchkick makes it easy to use the Elasticsearch DSL on its own.
|
1221
1325
|
|
1222
1326
|
### Advanced Mapping
|
1223
1327
|
|
@@ -1334,25 +1438,10 @@ Reindex associations
|
|
1334
1438
|
store.products.reindex
|
1335
1439
|
```
|
1336
1440
|
|
1337
|
-
Reindex a subset of attributes (partial reindex)
|
1338
|
-
|
1339
|
-
```ruby
|
1340
|
-
class Product < ActiveRecord::Base
|
1341
|
-
def search_prices
|
1342
|
-
{
|
1343
|
-
price: price,
|
1344
|
-
sale_price: sale_price
|
1345
|
-
}
|
1346
|
-
end
|
1347
|
-
end
|
1348
|
-
|
1349
|
-
Product.reindex(:search_prices)
|
1350
|
-
```
|
1351
|
-
|
1352
1441
|
Remove old indices
|
1353
1442
|
|
1354
1443
|
```ruby
|
1355
|
-
Product.
|
1444
|
+
Product.search_index.clean_indices
|
1356
1445
|
```
|
1357
1446
|
|
1358
1447
|
Use custom settings
|
@@ -6,7 +6,12 @@ module Searchkick
|
|
6
6
|
klass = class_name.constantize
|
7
7
|
index = index_name ? Searchkick::Index.new(index_name) : klass.searchkick_index
|
8
8
|
record_ids ||= min_id..max_id
|
9
|
-
index.import_scope(
|
9
|
+
index.import_scope(
|
10
|
+
Searchkick.load_records(klass, record_ids),
|
11
|
+
method_name: method_name,
|
12
|
+
batch: true,
|
13
|
+
batch_id: batch_id
|
14
|
+
)
|
10
15
|
end
|
11
16
|
end
|
12
17
|
end
|
data/lib/searchkick/index.rb
CHANGED
@@ -248,37 +248,14 @@ module Searchkick
|
|
248
248
|
end
|
249
249
|
|
250
250
|
def import_scope(scope, resume: false, method_name: nil, async: false, batch: false, batch_id: nil, full: false)
|
251
|
-
batch_size = @options[:batch_size] || 1000
|
252
|
-
|
253
251
|
# use scope for import
|
254
252
|
scope = scope.search_import if scope.respond_to?(:search_import)
|
255
253
|
|
256
254
|
if batch
|
257
255
|
import_or_update scope.to_a, method_name, async
|
258
|
-
|
256
|
+
redis.srem(batches_key, batch_id) if batch_id && redis
|
259
257
|
elsif full && async
|
260
|
-
|
261
|
-
# TODO expire Redis key
|
262
|
-
primary_key = scope.primary_key
|
263
|
-
starting_id = scope.minimum(primary_key) || 0
|
264
|
-
max_id = scope.maximum(primary_key) || 0
|
265
|
-
batches_count = ((max_id - starting_id + 1) / batch_size.to_f).ceil
|
266
|
-
|
267
|
-
batches_count.times do |i|
|
268
|
-
batch_id = i + 1
|
269
|
-
min_id = starting_id + (i * batch_size)
|
270
|
-
Searchkick::BulkReindexJob.perform_later(
|
271
|
-
class_name: scope.model_name.name,
|
272
|
-
min_id: min_id,
|
273
|
-
max_id: min_id + batch_size - 1,
|
274
|
-
index_name: name,
|
275
|
-
batch_id: batch_id
|
276
|
-
)
|
277
|
-
Searchkick.redis.sadd(batches_key, batch_id) if Searchkick.redis
|
278
|
-
end
|
279
|
-
else
|
280
|
-
raise Searchkick::Error, "async option only supported for ActiveRecord"
|
281
|
-
end
|
258
|
+
full_reindex_async(scope)
|
282
259
|
elsif scope.respond_to?(:find_in_batches)
|
283
260
|
if resume
|
284
261
|
# use total docs instead of max id since there's not a great way
|
@@ -294,23 +271,14 @@ module Searchkick
|
|
294
271
|
import_or_update batch, method_name, async
|
295
272
|
end
|
296
273
|
else
|
297
|
-
|
298
|
-
|
299
|
-
items = []
|
300
|
-
# TODO add resume
|
301
|
-
scope.all.each do |item|
|
302
|
-
items << item
|
303
|
-
if items.length == batch_size
|
304
|
-
import_or_update items, method_name, async
|
305
|
-
items = []
|
306
|
-
end
|
274
|
+
each_batch(scope) do |items|
|
275
|
+
import_or_update items, method_name, async
|
307
276
|
end
|
308
|
-
import_or_update items, method_name, async
|
309
277
|
end
|
310
278
|
end
|
311
279
|
|
312
280
|
def batches_left
|
313
|
-
|
281
|
+
redis.scard(batches_key) if redis
|
314
282
|
end
|
315
283
|
|
316
284
|
# other
|
@@ -432,21 +400,85 @@ module Searchkick
|
|
432
400
|
method_name: method_name ? method_name.to_s : nil
|
433
401
|
)
|
434
402
|
else
|
435
|
-
retries = 0
|
436
403
|
records = records.select(&:should_index?)
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
if retries < 1
|
441
|
-
retries += 1
|
442
|
-
retry
|
404
|
+
if records.any?
|
405
|
+
with_retries do
|
406
|
+
method_name ? bulk_update(records, method_name) : import(records)
|
443
407
|
end
|
444
|
-
raise e
|
445
408
|
end
|
446
409
|
end
|
447
410
|
end
|
448
411
|
end
|
449
412
|
|
413
|
+
def full_reindex_async(scope)
|
414
|
+
if scope.respond_to?(:primary_key)
|
415
|
+
# TODO expire Redis key
|
416
|
+
primary_key = scope.primary_key
|
417
|
+
starting_id = scope.minimum(primary_key) || 0
|
418
|
+
max_id = scope.maximum(primary_key) || 0
|
419
|
+
batches_count = ((max_id - starting_id + 1) / batch_size.to_f).ceil
|
420
|
+
|
421
|
+
batches_count.times do |i|
|
422
|
+
batch_id = i + 1
|
423
|
+
min_id = starting_id + (i * batch_size)
|
424
|
+
bulk_reindex_job scope, batch_id, min_id: min_id, max_id: min_id + batch_size - 1
|
425
|
+
end
|
426
|
+
else
|
427
|
+
batch_id = 1
|
428
|
+
# TODO remove any eager loading
|
429
|
+
scope = scope.only(:_id) if scope.respond_to?(:only)
|
430
|
+
each_batch(scope) do |items|
|
431
|
+
bulk_reindex_job scope, batch_id, record_ids: items.map { |i| i.id.to_s }
|
432
|
+
batch_id += 1
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def each_batch(scope)
|
438
|
+
# https://github.com/karmi/tire/blob/master/lib/tire/model/import.rb
|
439
|
+
# use cursor for Mongoid
|
440
|
+
items = []
|
441
|
+
scope.all.each do |item|
|
442
|
+
items << item
|
443
|
+
if items.length == batch_size
|
444
|
+
yield items
|
445
|
+
items = []
|
446
|
+
end
|
447
|
+
end
|
448
|
+
yield items if items.any?
|
449
|
+
end
|
450
|
+
|
451
|
+
def bulk_reindex_job(scope, batch_id, options)
|
452
|
+
Searchkick::BulkReindexJob.perform_later({
|
453
|
+
class_name: scope.model_name.name,
|
454
|
+
index_name: name,
|
455
|
+
batch_id: batch_id
|
456
|
+
}.merge(options))
|
457
|
+
redis.sadd(batches_key, batch_id) if redis
|
458
|
+
end
|
459
|
+
|
460
|
+
def batch_size
|
461
|
+
@batch_size ||= @options[:batch_size] || 1000
|
462
|
+
end
|
463
|
+
|
464
|
+
def with_retries
|
465
|
+
retries = 0
|
466
|
+
|
467
|
+
begin
|
468
|
+
yield
|
469
|
+
rescue Faraday::ClientError => e
|
470
|
+
if retries < 1
|
471
|
+
retries += 1
|
472
|
+
retry
|
473
|
+
end
|
474
|
+
raise e
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def redis
|
479
|
+
Searchkick.redis
|
480
|
+
end
|
481
|
+
|
450
482
|
def batches_key
|
451
483
|
"searchkick:reindex:#{name}:batches"
|
452
484
|
end
|
data/lib/searchkick/logging.rb
CHANGED
@@ -51,8 +51,44 @@ module Searchkick
|
|
51
51
|
name: "#{records.first.searchkick_klass.name} Import",
|
52
52
|
count: records.size
|
53
53
|
}
|
54
|
-
|
55
|
-
super
|
54
|
+
if Searchkick.callbacks_value == :bulk
|
55
|
+
super
|
56
|
+
else
|
57
|
+
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def bulk_update(records, *args)
|
65
|
+
if records.any?
|
66
|
+
event = {
|
67
|
+
name: "#{records.first.searchkick_klass.name} Update",
|
68
|
+
count: records.size
|
69
|
+
}
|
70
|
+
if Searchkick.callbacks_value == :bulk
|
71
|
+
super
|
72
|
+
else
|
73
|
+
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
74
|
+
super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def bulk_delete(records)
|
81
|
+
if records.any?
|
82
|
+
event = {
|
83
|
+
name: "#{records.first.searchkick_klass.name} Delete",
|
84
|
+
count: records.size
|
85
|
+
}
|
86
|
+
if Searchkick.callbacks_value == :bulk
|
87
|
+
super
|
88
|
+
else
|
89
|
+
ActiveSupport::Notifications.instrument("request.searchkick", event) do
|
90
|
+
super
|
91
|
+
end
|
56
92
|
end
|
57
93
|
end
|
58
94
|
end
|
@@ -15,8 +15,8 @@ module Searchkick
|
|
15
15
|
# bulk reindex
|
16
16
|
index = klass.searchkick_index
|
17
17
|
Searchkick.callbacks(:bulk) do
|
18
|
-
index.bulk_index(records)
|
19
|
-
index.bulk_delete(delete_records)
|
18
|
+
index.bulk_index(records) if records.any?
|
19
|
+
index.bulk_delete(delete_records) if delete_records.any?
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -5,7 +5,7 @@ module Searchkick
|
|
5
5
|
def perform(class_name:)
|
6
6
|
model = class_name.constantize
|
7
7
|
|
8
|
-
limit = 1000
|
8
|
+
limit = model.searchkick_index.options[:batch_size] || 1000
|
9
9
|
record_ids = Searchkick::ReindexQueue.new(model.searchkick_index.name).reserve(limit: limit)
|
10
10
|
if record_ids.any?
|
11
11
|
Searchkick::ProcessBatchJob.perform_later(
|
data/lib/searchkick/version.rb
CHANGED
data/test/callbacks_test.rb
CHANGED
@@ -35,7 +35,7 @@ class CallbacksTest < Minitest::Test
|
|
35
35
|
store_names ["Product A", "Product B"]
|
36
36
|
end
|
37
37
|
Product.searchkick_index.refresh
|
38
|
-
assert_search "product", [], load: false
|
38
|
+
assert_search "product", [], load: false, conversions: false
|
39
39
|
assert_equal 2, reindex_queue.length
|
40
40
|
|
41
41
|
Searchkick::ProcessQueueJob.perform_later(class_name: "Product")
|
data/test/index_test.rb
CHANGED
@@ -87,7 +87,7 @@ class IndexTest < Minitest::Test
|
|
87
87
|
|
88
88
|
def test_remove_blank_id
|
89
89
|
store_names ["Product A"]
|
90
|
-
Product.searchkick_index.remove(
|
90
|
+
Product.searchkick_index.remove(Product.new)
|
91
91
|
assert_search "product", ["Product A"]
|
92
92
|
ensure
|
93
93
|
Product.reindex
|
data/test/reindex_test.rb
CHANGED
@@ -1,12 +1,9 @@
|
|
1
1
|
require_relative "test_helper"
|
2
2
|
|
3
3
|
class ReindexTest < Minitest::Test
|
4
|
-
def
|
4
|
+
def test_scoped
|
5
5
|
skip if nobrainer?
|
6
|
-
super
|
7
|
-
end
|
8
6
|
|
9
|
-
def test_scoped
|
10
7
|
store_names ["Product A"]
|
11
8
|
Searchkick.callbacks(false) do
|
12
9
|
store_names ["Product B", "Product C"]
|
@@ -16,6 +13,8 @@ class ReindexTest < Minitest::Test
|
|
16
13
|
end
|
17
14
|
|
18
15
|
def test_associations
|
16
|
+
skip if nobrainer?
|
17
|
+
|
19
18
|
store_names ["Product A"]
|
20
19
|
store = Store.create!(name: "Test")
|
21
20
|
Product.create!(name: "Product B", store_id: store.id)
|
@@ -24,13 +23,13 @@ class ReindexTest < Minitest::Test
|
|
24
23
|
end
|
25
24
|
|
26
25
|
def test_async
|
27
|
-
skip
|
26
|
+
skip if !defined?(ActiveJob)
|
28
27
|
|
29
28
|
Searchkick.callbacks(false) do
|
30
29
|
store_names ["Product A"]
|
31
30
|
end
|
32
31
|
reindex = Product.reindex(async: true)
|
33
|
-
assert_search "product", []
|
32
|
+
assert_search "product", [], conversions: false
|
34
33
|
|
35
34
|
index = Searchkick::Index.new(reindex[:index_name])
|
36
35
|
index.refresh
|
data/test/routing_test.rb
CHANGED
@@ -10,4 +10,14 @@ class RoutingTest < Minitest::Test
|
|
10
10
|
index_options = Store.searchkick_index.index_options
|
11
11
|
assert_equal index_options[:mappings][:_default_][:_routing], required: true
|
12
12
|
end
|
13
|
+
|
14
|
+
def test_routing_correct_node
|
15
|
+
store_names ["Dollar Tree"], Store
|
16
|
+
assert_search "dollar", ["Dollar Tree"], {routing: "Dollar Tree"}, Store
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_routing_incorrect_node
|
20
|
+
store_names ["Dollar Tree"], Store
|
21
|
+
assert_search "dollar", ["Dollar Tree"], {routing: "Boom"}, Store
|
22
|
+
end
|
13
23
|
end
|
data/test/test_helper.rb
CHANGED
@@ -25,7 +25,7 @@ if defined?(ActiveJob)
|
|
25
25
|
ActiveJob::Base.queue_adapter = :inline
|
26
26
|
end
|
27
27
|
|
28
|
-
ActiveSupport::LogSubscriber.logger = Logger.new(STDOUT) if ENV["NOTIFICATIONS"]
|
28
|
+
ActiveSupport::LogSubscriber.logger = ActiveSupport::Logger.new(STDOUT) if ENV["NOTIFICATIONS"]
|
29
29
|
|
30
30
|
def elasticsearch_below50?
|
31
31
|
Searchkick.server_below?("5.0.0-alpha1")
|
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: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -189,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
189
|
version: '0'
|
190
190
|
requirements: []
|
191
191
|
rubyforge_project:
|
192
|
-
rubygems_version: 2.
|
192
|
+
rubygems_version: 2.5.1
|
193
193
|
signing_key:
|
194
194
|
specification_version: 4
|
195
195
|
summary: Searchkick learns what your users are looking for. As more people search,
|