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