searchkick 5.0.0 → 5.0.3
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 +14 -1
- data/README.md +83 -73
- data/lib/searchkick/index.rb +29 -15
- data/lib/searchkick/index_options.rb +1 -1
- data/lib/searchkick/model.rb +9 -7
- data/lib/searchkick/query.rb +2 -2
- data/lib/searchkick/record_data.rb +1 -0
- data/lib/searchkick/record_indexer.rb +2 -2
- data/lib/searchkick/reindex_queue.rb +1 -1
- data/lib/searchkick/relation.rb +193 -4
- data/lib/searchkick/results.rb +5 -5
- data/lib/searchkick/version.rb +1 -1
- data/lib/searchkick/where.rb +11 -0
- data/lib/searchkick.rb +5 -4
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec7741cb306a56f1a5ae5a07450c5c102236bc6103079fc34a5f602fa2853b31
|
4
|
+
data.tar.gz: 2bd747ee31846c901ce2a58125b34e9c90af6937a2c7dc041f2dd9f69701f1e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f92f2a3c7bb27862b1768f5ecedc19ad0eab515d62515f94d72412ed7c22a20493049dd0e52cba21b3a43d8c846b0dc0c6cda40c68edc12f6af37e8487fb9403
|
7
|
+
data.tar.gz: f69a1cfc401bad0f09bda3f2788b1096cc004dcb765eb20b3d8ada06202185eeae630d953005298e5944ed1b8c239fa808eeebb32e9440cc7ae9a32e771469dc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 5.0.3 (2022-03-13)
|
2
|
+
|
3
|
+
- Fixed context for index name for inherited models
|
4
|
+
|
5
|
+
## 5.0.2 (2022-03-03)
|
6
|
+
|
7
|
+
- Fixed index name for inherited models
|
8
|
+
|
9
|
+
## 5.0.1 (2022-02-27)
|
10
|
+
|
11
|
+
- Prefer `mode: :async` over `async: true` for full reindex
|
12
|
+
- Fixed instance method overriding with concerns
|
13
|
+
|
1
14
|
## 5.0.0 (2022-02-21)
|
2
15
|
|
3
16
|
- Searches now use lazy loading (similar to Active Record)
|
@@ -15,7 +28,7 @@
|
|
15
28
|
- Raise error when `search` called on relations
|
16
29
|
- Raise `ArgumentError` (instead of warning) for invalid regular expression modifiers
|
17
30
|
- Raise `ArgumentError` instead of `RuntimeError` for unknown operators
|
18
|
-
- Removed mapping of `id` to `_id` with `order` option
|
31
|
+
- Removed mapping of `id` to `_id` with `order` option (not supported in Elasticsearch 8)
|
19
32
|
- Removed `wordnet` option (no longer worked)
|
20
33
|
- Removed dependency on `elasticsearch` gem (can use `elasticsearch` or `opensearch-ruby`)
|
21
34
|
- Dropped support for Elasticsearch 6
|
data/README.md
CHANGED
@@ -50,8 +50,8 @@ Searchkick 5.0 was recently released! See [how to upgrade](#upgrading)
|
|
50
50
|
Install [Elasticsearch](https://www.elastic.co/downloads/elasticsearch) or [OpenSearch](https://opensearch.org/downloads.html). For Homebrew, use:
|
51
51
|
|
52
52
|
```sh
|
53
|
-
brew install elasticsearch
|
54
|
-
brew services start elasticsearch
|
53
|
+
brew install elastic/tap/elasticsearch-full
|
54
|
+
brew services start elasticsearch-full
|
55
55
|
# or
|
56
56
|
brew install opensearch
|
57
57
|
brew services start opensearch
|
@@ -98,7 +98,7 @@ Searchkick supports the complete [Elasticsearch Search API](https://www.elastic.
|
|
98
98
|
Query like SQL
|
99
99
|
|
100
100
|
```ruby
|
101
|
-
Product.search
|
101
|
+
Product.search("apples", where: {in_stock: true}, limit: 10, offset: 50)
|
102
102
|
```
|
103
103
|
|
104
104
|
Search specific fields
|
@@ -226,7 +226,7 @@ You can also boost by:
|
|
226
226
|
Use a `*` for the query.
|
227
227
|
|
228
228
|
```ruby
|
229
|
-
Product.search
|
229
|
+
Product.search("*")
|
230
230
|
```
|
231
231
|
|
232
232
|
### Pagination
|
@@ -235,7 +235,7 @@ Plays nicely with kaminari and will_paginate.
|
|
235
235
|
|
236
236
|
```ruby
|
237
237
|
# controller
|
238
|
-
@products = Product.search
|
238
|
+
@products = Product.search("milk", page: params[:page], per_page: 20)
|
239
239
|
```
|
240
240
|
|
241
241
|
View with kaminari
|
@@ -255,13 +255,13 @@ View with will_paginate
|
|
255
255
|
By default, results must match all words in the query.
|
256
256
|
|
257
257
|
```ruby
|
258
|
-
Product.search
|
258
|
+
Product.search("fresh honey") # fresh AND honey
|
259
259
|
```
|
260
260
|
|
261
261
|
To change this, use:
|
262
262
|
|
263
263
|
```ruby
|
264
|
-
Product.search
|
264
|
+
Product.search("fresh honey", operator: "or") # fresh OR honey
|
265
265
|
```
|
266
266
|
|
267
267
|
By default, results must match the entire word - `back` will not match `backpack`. You can change this behavior with:
|
@@ -275,7 +275,7 @@ end
|
|
275
275
|
And to search (after you reindex):
|
276
276
|
|
277
277
|
```ruby
|
278
|
-
Product.search
|
278
|
+
Product.search("back", fields: [:name], match: :word_start)
|
279
279
|
```
|
280
280
|
|
281
281
|
Available options are:
|
@@ -297,7 +297,7 @@ The default is `:word`. The most matches will happen with `:word_middle`.
|
|
297
297
|
To match a field exactly (case-sensitive), use:
|
298
298
|
|
299
299
|
```ruby
|
300
|
-
|
300
|
+
Product.search(query, fields: [{email: :exact}, :name])
|
301
301
|
```
|
302
302
|
|
303
303
|
### Phrase Matches
|
@@ -305,7 +305,7 @@ User.search query, fields: [{email: :exact}, :name]
|
|
305
305
|
To only match the exact order, use:
|
306
306
|
|
307
307
|
```ruby
|
308
|
-
|
308
|
+
Product.search("fresh honey", match: :phrase)
|
309
309
|
```
|
310
310
|
|
311
311
|
### Stemming and Language
|
@@ -426,7 +426,7 @@ end
|
|
426
426
|
Search with:
|
427
427
|
|
428
428
|
```ruby
|
429
|
-
Product.search
|
429
|
+
Product.search(query, fields: [:name_tagged])
|
430
430
|
```
|
431
431
|
|
432
432
|
### Misspellings
|
@@ -436,13 +436,13 @@ By default, Searchkick handles misspelled queries by returning results with an [
|
|
436
436
|
You can change this with:
|
437
437
|
|
438
438
|
```ruby
|
439
|
-
Product.search
|
439
|
+
Product.search("zucini", misspellings: {edit_distance: 2}) # zucchini
|
440
440
|
```
|
441
441
|
|
442
442
|
To prevent poor precision and improve performance for correctly spelled queries (which should be a majority for most applications), Searchkick can first perform a search without misspellings, and if there are too few results, perform another with them.
|
443
443
|
|
444
444
|
```ruby
|
445
|
-
Product.search
|
445
|
+
Product.search("zuchini", misspellings: {below: 5})
|
446
446
|
```
|
447
447
|
|
448
448
|
If there are fewer than 5 results, a 2nd search is performed with misspellings enabled. The result of this query is returned.
|
@@ -450,13 +450,13 @@ If there are fewer than 5 results, a 2nd search is performed with misspellings e
|
|
450
450
|
Turn off misspellings with:
|
451
451
|
|
452
452
|
```ruby
|
453
|
-
Product.search
|
453
|
+
Product.search("zuchini", misspellings: false) # no zucchini
|
454
454
|
```
|
455
455
|
|
456
456
|
Specify which fields can include misspellings with:
|
457
457
|
|
458
458
|
```ruby
|
459
|
-
Product.search
|
459
|
+
Product.search("zucini", fields: [:name, :color], misspellings: {fields: [:name]})
|
460
460
|
```
|
461
461
|
|
462
462
|
> When doing this, you must also specify fields to search
|
@@ -466,7 +466,7 @@ Product.search "zucini", fields: [:name, :color], misspellings: {fields: [:name]
|
|
466
466
|
If a user searches `butter`, they may also get results for `peanut butter`. To prevent this, use:
|
467
467
|
|
468
468
|
```ruby
|
469
|
-
Product.search
|
469
|
+
Product.search("butter", exclude: ["peanut butter"])
|
470
470
|
```
|
471
471
|
|
472
472
|
You can map queries and terms to exclude with:
|
@@ -477,7 +477,7 @@ exclude_queries = {
|
|
477
477
|
"cream" => ["ice cream", "whipped cream"]
|
478
478
|
}
|
479
479
|
|
480
|
-
Product.search
|
480
|
+
Product.search(query, exclude: exclude_queries[query])
|
481
481
|
```
|
482
482
|
|
483
483
|
You can demote results by boosting by a factor less than one:
|
@@ -499,7 +499,7 @@ gem "gemoji-parser"
|
|
499
499
|
And use:
|
500
500
|
|
501
501
|
```ruby
|
502
|
-
Product.search
|
502
|
+
Product.search("🍨🍰", emoji: true)
|
503
503
|
```
|
504
504
|
|
505
505
|
## Indexing
|
@@ -596,7 +596,7 @@ You can also do bulk updates.
|
|
596
596
|
|
597
597
|
```ruby
|
598
598
|
Searchkick.callbacks(:bulk) do
|
599
|
-
|
599
|
+
Product.find_each(&:update_fields)
|
600
600
|
end
|
601
601
|
```
|
602
602
|
|
@@ -604,7 +604,7 @@ Or temporarily skip updates.
|
|
604
604
|
|
605
605
|
```ruby
|
606
606
|
Searchkick.callbacks(false) do
|
607
|
-
|
607
|
+
Product.find_each(&:update_fields)
|
608
608
|
end
|
609
609
|
```
|
610
610
|
|
@@ -651,7 +651,7 @@ end
|
|
651
651
|
The best starting point to improve your search **by far** is to track searches and conversions. [Searchjoy](https://github.com/ankane/searchjoy) makes it easy.
|
652
652
|
|
653
653
|
```ruby
|
654
|
-
Product.search
|
654
|
+
Product.search("apple", track: {user_id: current_user.id})
|
655
655
|
```
|
656
656
|
|
657
657
|
[See the docs](https://github.com/ankane/searchjoy) for how to install and use.
|
@@ -683,7 +683,7 @@ end
|
|
683
683
|
|
684
684
|
Reindex and set up a cron job to add new conversions daily.
|
685
685
|
|
686
|
-
```
|
686
|
+
```sh
|
687
687
|
rake searchkick:reindex CLASS=Product
|
688
688
|
```
|
689
689
|
|
@@ -709,7 +709,7 @@ end
|
|
709
709
|
Reindex and search with:
|
710
710
|
|
711
711
|
```ruby
|
712
|
-
Product.search
|
712
|
+
Product.search("milk", boost_where: {orderer_ids: current_user.id})
|
713
713
|
```
|
714
714
|
|
715
715
|
## Instant Search / Autocomplete
|
@@ -733,7 +733,7 @@ end
|
|
733
733
|
Reindex and search with:
|
734
734
|
|
735
735
|
```ruby
|
736
|
-
Movie.search
|
736
|
+
Movie.search("jurassic pa", fields: [:title], match: :word_start)
|
737
737
|
```
|
738
738
|
|
739
739
|
Typically, you want to use a JavaScript library like [typeahead.js](https://twitter.github.io/typeahead.js/) or [jQuery UI](https://jqueryui.com/autocomplete/).
|
@@ -793,7 +793,7 @@ end
|
|
793
793
|
Reindex and search with:
|
794
794
|
|
795
795
|
```ruby
|
796
|
-
products = Product.search
|
796
|
+
products = Product.search("peantu butta", suggest: true)
|
797
797
|
products.suggestions # ["peanut butter"]
|
798
798
|
```
|
799
799
|
|
@@ -804,40 +804,40 @@ products.suggestions # ["peanut butter"]
|
|
804
804
|

|
805
805
|
|
806
806
|
```ruby
|
807
|
-
products = Product.search
|
807
|
+
products = Product.search("chuck taylor", aggs: [:product_type, :gender, :brand])
|
808
808
|
products.aggs
|
809
809
|
```
|
810
810
|
|
811
811
|
By default, `where` conditions apply to aggregations.
|
812
812
|
|
813
813
|
```ruby
|
814
|
-
Product.search
|
814
|
+
Product.search("wingtips", where: {color: "brandy"}, aggs: [:size])
|
815
815
|
# aggregations for brandy wingtips are returned
|
816
816
|
```
|
817
817
|
|
818
818
|
Change this with:
|
819
819
|
|
820
820
|
```ruby
|
821
|
-
Product.search
|
821
|
+
Product.search("wingtips", where: {color: "brandy"}, aggs: [:size], smart_aggs: false)
|
822
822
|
# aggregations for all wingtips are returned
|
823
823
|
```
|
824
824
|
|
825
825
|
Set `where` conditions for each aggregation separately with:
|
826
826
|
|
827
827
|
```ruby
|
828
|
-
Product.search
|
828
|
+
Product.search("wingtips", aggs: {size: {where: {color: "brandy"}}})
|
829
829
|
```
|
830
830
|
|
831
831
|
Limit
|
832
832
|
|
833
833
|
```ruby
|
834
|
-
Product.search
|
834
|
+
Product.search("apples", aggs: {store_id: {limit: 10}})
|
835
835
|
```
|
836
836
|
|
837
837
|
Order
|
838
838
|
|
839
839
|
```ruby
|
840
|
-
Product.search
|
840
|
+
Product.search("wingtips", aggs: {color: {order: {"_key" => "asc"}}}) # alphabetically
|
841
841
|
```
|
842
842
|
|
843
843
|
[All of these options are supported](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-order)
|
@@ -846,31 +846,31 @@ Ranges
|
|
846
846
|
|
847
847
|
```ruby
|
848
848
|
price_ranges = [{to: 20}, {from: 20, to: 50}, {from: 50}]
|
849
|
-
Product.search
|
849
|
+
Product.search("*", aggs: {price: {ranges: price_ranges}})
|
850
850
|
```
|
851
851
|
|
852
852
|
Minimum document count
|
853
853
|
|
854
854
|
```ruby
|
855
|
-
Product.search
|
855
|
+
Product.search("apples", aggs: {store_id: {min_doc_count: 2}})
|
856
856
|
```
|
857
857
|
|
858
858
|
Script support
|
859
859
|
|
860
860
|
```ruby
|
861
|
-
Product.search
|
861
|
+
Product.search("*", aggs: {color: {script: {source: "'Color: ' + _value"}}})
|
862
862
|
```
|
863
863
|
|
864
864
|
Date histogram
|
865
865
|
|
866
866
|
```ruby
|
867
|
-
Product.search
|
867
|
+
Product.search("pear", aggs: {products_per_year: {date_histogram: {field: :created_at, interval: :year}}})
|
868
868
|
```
|
869
869
|
|
870
870
|
For other aggregation types, including sub-aggregations, use `body_options`:
|
871
871
|
|
872
872
|
```ruby
|
873
|
-
Product.search
|
873
|
+
Product.search("orange", body_options: {aggs: {price: {histogram: {field: :price, interval: 10}}}})
|
874
874
|
```
|
875
875
|
|
876
876
|
## Highlight
|
@@ -878,7 +878,7 @@ Product.search "orange", body_options: {aggs: {price: {histogram: {field: :price
|
|
878
878
|
Specify which fields to index with highlighting.
|
879
879
|
|
880
880
|
```ruby
|
881
|
-
class
|
881
|
+
class Band < ApplicationRecord
|
882
882
|
searchkick highlight: [:name]
|
883
883
|
end
|
884
884
|
```
|
@@ -886,7 +886,7 @@ end
|
|
886
886
|
Highlight the search query in the results.
|
887
887
|
|
888
888
|
```ruby
|
889
|
-
bands = Band.search
|
889
|
+
bands = Band.search("cinema", highlight: true)
|
890
890
|
```
|
891
891
|
|
892
892
|
View the highlighted fields with:
|
@@ -900,19 +900,19 @@ end
|
|
900
900
|
To change the tag, use:
|
901
901
|
|
902
902
|
```ruby
|
903
|
-
Band.search
|
903
|
+
Band.search("cinema", highlight: {tag: "<strong>"})
|
904
904
|
```
|
905
905
|
|
906
906
|
To highlight and search different fields, use:
|
907
907
|
|
908
908
|
```ruby
|
909
|
-
Band.search
|
909
|
+
Band.search("cinema", fields: [:name], highlight: {fields: [:description]})
|
910
910
|
```
|
911
911
|
|
912
912
|
By default, the entire field is highlighted. To get small snippets instead, use:
|
913
913
|
|
914
914
|
```ruby
|
915
|
-
bands = Band.search
|
915
|
+
bands = Band.search("cinema", highlight: {fragment_size: 20})
|
916
916
|
bands.with_highlights(multiple: true).each do |band, highlights|
|
917
917
|
highlights[:name].join(" and ")
|
918
918
|
end
|
@@ -921,7 +921,7 @@ end
|
|
921
921
|
Additional options can be specified for each field:
|
922
922
|
|
923
923
|
```ruby
|
924
|
-
Band.search
|
924
|
+
Band.search("cinema", fields: [:name], highlight: {fields: {name: {fragment_size: 200}}})
|
925
925
|
```
|
926
926
|
|
927
927
|
You can find available highlight options in the [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html).
|
@@ -950,13 +950,13 @@ end
|
|
950
950
|
Reindex and search with:
|
951
951
|
|
952
952
|
```ruby
|
953
|
-
Restaurant.search
|
953
|
+
Restaurant.search("pizza", where: {location: {near: {lat: 37, lon: -114}, within: "100mi"}}) # or 160km
|
954
954
|
```
|
955
955
|
|
956
956
|
Bounded by a box
|
957
957
|
|
958
958
|
```ruby
|
959
|
-
Restaurant.search
|
959
|
+
Restaurant.search("sushi", where: {location: {top_left: {lat: 38, lon: -123}, bottom_right: {lat: 37, lon: -122}}})
|
960
960
|
```
|
961
961
|
|
962
962
|
**Note:** `top_right` and `bottom_left` also work
|
@@ -964,7 +964,7 @@ Restaurant.search "sushi", where: {location: {top_left: {lat: 38, lon: -123}, bo
|
|
964
964
|
Bounded by a polygon
|
965
965
|
|
966
966
|
```ruby
|
967
|
-
Restaurant.search
|
967
|
+
Restaurant.search("dessert", where: {location: {geo_polygon: {points: [{lat: 38, lon: -123}, {lat: 39, lon: -123}, {lat: 37, lon: 122}]}}})
|
968
968
|
```
|
969
969
|
|
970
970
|
### Boost By Distance
|
@@ -972,13 +972,13 @@ Restaurant.search "dessert", where: {location: {geo_polygon: {points: [{lat: 38,
|
|
972
972
|
Boost results by distance - closer results are boosted more
|
973
973
|
|
974
974
|
```ruby
|
975
|
-
Restaurant.search
|
975
|
+
Restaurant.search("noodles", boost_by_distance: {location: {origin: {lat: 37, lon: -122}}})
|
976
976
|
```
|
977
977
|
|
978
978
|
Also supports [additional options](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html#function-decay)
|
979
979
|
|
980
980
|
```ruby
|
981
|
-
Restaurant.search
|
981
|
+
Restaurant.search("wings", boost_by_distance: {location: {origin: {lat: 37, lon: -122}, function: "linear", scale: "30mi", decay: 0.5}})
|
982
982
|
```
|
983
983
|
|
984
984
|
### Geo Shapes
|
@@ -1005,19 +1005,19 @@ See the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsea
|
|
1005
1005
|
Find shapes intersecting with the query shape
|
1006
1006
|
|
1007
1007
|
```ruby
|
1008
|
-
Restaurant.search
|
1008
|
+
Restaurant.search("soup", where: {bounds: {geo_shape: {type: "polygon", coordinates: [[{lat: 38, lon: -123}, ...]]}}})
|
1009
1009
|
```
|
1010
1010
|
|
1011
1011
|
Falling entirely within the query shape
|
1012
1012
|
|
1013
1013
|
```ruby
|
1014
|
-
Restaurant.search
|
1014
|
+
Restaurant.search("salad", where: {bounds: {geo_shape: {type: "circle", relation: "within", coordinates: [{lat: 38, lon: -123}], radius: "1km"}}})
|
1015
1015
|
```
|
1016
1016
|
|
1017
1017
|
Not touching the query shape
|
1018
1018
|
|
1019
1019
|
```ruby
|
1020
|
-
Restaurant.search
|
1020
|
+
Restaurant.search("burger", where: {bounds: {geo_shape: {type: "envelope", relation: "disjoint", coordinates: [{lat: 38, lon: -123}, {lat: 37, lon: -122}]}}})
|
1021
1021
|
```
|
1022
1022
|
|
1023
1023
|
## Inheritance
|
@@ -1047,9 +1047,9 @@ Dog.reindex # equivalent, all animals reindexed
|
|
1047
1047
|
And to search, use:
|
1048
1048
|
|
1049
1049
|
```ruby
|
1050
|
-
Animal.search
|
1051
|
-
Dog.search
|
1052
|
-
Animal.search
|
1050
|
+
Animal.search("*") # all animals
|
1051
|
+
Dog.search("*") # just dogs
|
1052
|
+
Animal.search("*", type: [Dog, Cat]) # just cats and dogs
|
1053
1053
|
```
|
1054
1054
|
|
1055
1055
|
**Notes:**
|
@@ -1057,7 +1057,7 @@ Animal.search "*", type: [Dog, Cat] # just cats and dogs
|
|
1057
1057
|
1. The `suggest` option retrieves suggestions from the parent at the moment.
|
1058
1058
|
|
1059
1059
|
```ruby
|
1060
|
-
Dog.search
|
1060
|
+
Dog.search("airbudd", suggest: true) # suggestions for all animals
|
1061
1061
|
```
|
1062
1062
|
2. This relies on a `type` field that is automatically added to the indexed document. Be wary of defining your own `type` field in `search_data`, as it will take precedence.
|
1063
1063
|
|
@@ -1100,7 +1100,7 @@ Product.search_index.tokens("dieg", analyzer: "searchkick_word_search")
|
|
1100
1100
|
# ["dieg"] - match!!
|
1101
1101
|
```
|
1102
1102
|
|
1103
|
-
See the [complete list of analyzers](
|
1103
|
+
See the [complete list of analyzers](lib/searchkick/index_options.rb#L36).
|
1104
1104
|
|
1105
1105
|
## Testing
|
1106
1106
|
|
@@ -1376,13 +1376,15 @@ Bonsai, Elastic Cloud, and Amazon OpenSearch Service all support encryption at r
|
|
1376
1376
|
|
1377
1377
|
### Automatic Failover
|
1378
1378
|
|
1379
|
-
Create an initializer
|
1379
|
+
Create an initializer with multiple hosts:
|
1380
1380
|
|
1381
1381
|
```ruby
|
1382
1382
|
ENV["ELASTICSEARCH_URL"] = "https://user:password@host1,https://user:password@host2"
|
1383
|
+
# or
|
1384
|
+
ENV["OPENSEARCH_URL"] = "https://user:password@host1,https://user:password@host2"
|
1383
1385
|
```
|
1384
1386
|
|
1385
|
-
See [
|
1387
|
+
See [elastic-transport](https://github.com/elastic/elastic-transport-ruby) or [opensearch-transport](https://github.com/opensearch-project/opensearch-ruby/tree/main/opensearch-transport) for a complete list of options.
|
1386
1388
|
|
1387
1389
|
### Lograge
|
1388
1390
|
|
@@ -1453,7 +1455,7 @@ end
|
|
1453
1455
|
For large data sets, you can use background jobs to parallelize reindexing.
|
1454
1456
|
|
1455
1457
|
```ruby
|
1456
|
-
Product.reindex(
|
1458
|
+
Product.reindex(mode: :async)
|
1457
1459
|
# {index_name: "products_production_20170111210018065"}
|
1458
1460
|
```
|
1459
1461
|
|
@@ -1478,7 +1480,7 @@ Searchkick.reindex_status(index_name)
|
|
1478
1480
|
You can also have Searchkick wait for reindexing to complete
|
1479
1481
|
|
1480
1482
|
```ruby
|
1481
|
-
Product.reindex(
|
1483
|
+
Product.reindex(mode: :async, wait: true)
|
1482
1484
|
```
|
1483
1485
|
|
1484
1486
|
You can use [ActiveJob::TrafficControl](https://github.com/nickelser/activejob-traffic_control) to control concurrency. Install the gem:
|
@@ -1504,7 +1506,7 @@ This will allow only 3 jobs to run at once.
|
|
1504
1506
|
You can specify a longer refresh interval while reindexing to increase performance.
|
1505
1507
|
|
1506
1508
|
```ruby
|
1507
|
-
Product.reindex(
|
1509
|
+
Product.reindex(mode: :async, refresh_interval: "30s")
|
1508
1510
|
```
|
1509
1511
|
|
1510
1512
|
**Note:** This only makes a noticable difference with parallel reindexing.
|
@@ -1562,7 +1564,7 @@ end
|
|
1562
1564
|
Reindex and search with:
|
1563
1565
|
|
1564
1566
|
```ruby
|
1565
|
-
Business.search
|
1567
|
+
Business.search("ice cream", routing: params[:city_id])
|
1566
1568
|
```
|
1567
1569
|
|
1568
1570
|
### Partial Reindexing
|
@@ -1685,7 +1687,7 @@ end
|
|
1685
1687
|
And use the `body` option to search:
|
1686
1688
|
|
1687
1689
|
```ruby
|
1688
|
-
products = Product.search
|
1690
|
+
products = Product.search(body: {query: {match: {name: "milk"}}})
|
1689
1691
|
```
|
1690
1692
|
|
1691
1693
|
View the response with:
|
@@ -1697,21 +1699,21 @@ products.response
|
|
1697
1699
|
To modify the query generated by Searchkick, use:
|
1698
1700
|
|
1699
1701
|
```ruby
|
1700
|
-
products = Product.search
|
1702
|
+
products = Product.search("milk", body_options: {min_score: 1})
|
1701
1703
|
```
|
1702
1704
|
|
1703
1705
|
or
|
1704
1706
|
|
1705
1707
|
```ruby
|
1706
1708
|
products =
|
1707
|
-
Product.search
|
1709
|
+
Product.search("apples") do |body|
|
1708
1710
|
body[:min_score] = 1
|
1709
1711
|
end
|
1710
1712
|
```
|
1711
1713
|
|
1712
1714
|
### Client
|
1713
1715
|
|
1714
|
-
|
1716
|
+
To access the `Elasticsearch::Client` or `OpenSearch::Client` directly, use:
|
1715
1717
|
|
1716
1718
|
```ruby
|
1717
1719
|
Searchkick.client
|
@@ -1736,7 +1738,7 @@ Then use `products` and `coupons` as typical results.
|
|
1736
1738
|
Search across multiple models with:
|
1737
1739
|
|
1738
1740
|
```ruby
|
1739
|
-
Searchkick.search
|
1741
|
+
Searchkick.search("milk", models: [Product, Category])
|
1740
1742
|
```
|
1741
1743
|
|
1742
1744
|
Boost specific models with:
|
@@ -1762,7 +1764,7 @@ end
|
|
1762
1764
|
You can also scroll batches manually.
|
1763
1765
|
|
1764
1766
|
```ruby
|
1765
|
-
products = Product.search
|
1767
|
+
products = Product.search("*", scroll: "1m")
|
1766
1768
|
while products.any?
|
1767
1769
|
# process batch ...
|
1768
1770
|
|
@@ -1793,7 +1795,7 @@ Product.search("pears", body_options: {track_total_hits: true})
|
|
1793
1795
|
To query nested data, use dot notation.
|
1794
1796
|
|
1795
1797
|
```ruby
|
1796
|
-
|
1798
|
+
Product.search("san", fields: ["store.city"], where: {"store.zip_code" => 12345})
|
1797
1799
|
```
|
1798
1800
|
|
1799
1801
|
## Reference
|
@@ -1923,7 +1925,7 @@ Searchkick.queue_name = :search_reindex
|
|
1923
1925
|
Eager load associations
|
1924
1926
|
|
1925
1927
|
```ruby
|
1926
|
-
Product.search
|
1928
|
+
Product.search("milk", includes: [:brand, :stores])
|
1927
1929
|
```
|
1928
1930
|
|
1929
1931
|
Eager load different associations by model
|
@@ -1935,7 +1937,7 @@ Searchkick.search("*", models: [Product, Store], model_includes: {Product => [:
|
|
1935
1937
|
Run additional scopes on results
|
1936
1938
|
|
1937
1939
|
```ruby
|
1938
|
-
Product.search
|
1940
|
+
Product.search("milk", scope_results: ->(r) { r.with_attached_images })
|
1939
1941
|
```
|
1940
1942
|
|
1941
1943
|
Specify default fields to search
|
@@ -2031,13 +2033,13 @@ rake searchkick:reindex:all
|
|
2031
2033
|
Turn on misspellings after a certain number of characters
|
2032
2034
|
|
2033
2035
|
```ruby
|
2034
|
-
Product.search
|
2036
|
+
Product.search("api", misspellings: {prefix_length: 2}) # api, apt, no ahi
|
2035
2037
|
```
|
2036
2038
|
|
2037
2039
|
**Note:** With this option, if the query length is the same as `prefix_length`, misspellings are turned off with Elasticsearch 7 and OpenSearch
|
2038
2040
|
|
2039
2041
|
```ruby
|
2040
|
-
Product.search
|
2042
|
+
Product.search("ah", misspellings: {prefix_length: 2}) # ah, no aha
|
2041
2043
|
```
|
2042
2044
|
|
2043
2045
|
## Gotchas
|
@@ -2087,9 +2089,17 @@ Product.search("milk")
|
|
2087
2089
|
Product.search("milk").to_a
|
2088
2090
|
```
|
2089
2091
|
|
2092
|
+
You can reindex relations in the background:
|
2093
|
+
|
2094
|
+
```ruby
|
2095
|
+
store.products.reindex(mode: :async)
|
2096
|
+
# or
|
2097
|
+
store.products.reindex(mode: :queue)
|
2098
|
+
```
|
2099
|
+
|
2090
2100
|
And there’s a [new option](#default-scopes) for models with default scopes.
|
2091
2101
|
|
2092
|
-
Check out the [changelog](https://github.com/ankane/searchkick/blob/master/CHANGELOG.md) for the full list of changes.
|
2102
|
+
Check out the [changelog](https://github.com/ankane/searchkick/blob/master/CHANGELOG.md#500-2022-02-21) for the full list of changes.
|
2093
2103
|
|
2094
2104
|
## History
|
2095
2105
|
|
data/lib/searchkick/index.rb
CHANGED
@@ -71,12 +71,12 @@ module Searchkick
|
|
71
71
|
}
|
72
72
|
)
|
73
73
|
|
74
|
-
|
74
|
+
Results.new(nil, response).total_count
|
75
75
|
end
|
76
76
|
|
77
77
|
def promote(new_name, update_refresh_interval: false)
|
78
78
|
if update_refresh_interval
|
79
|
-
new_index =
|
79
|
+
new_index = Index.new(new_name, @options)
|
80
80
|
settings = options[:settings] || {}
|
81
81
|
refresh_interval = (settings[:index] && settings[:index][:refresh_interval]) || "1s"
|
82
82
|
new_index.update_settings(index: {refresh_interval: refresh_interval})
|
@@ -123,7 +123,7 @@ module Searchkick
|
|
123
123
|
def clean_indices
|
124
124
|
indices = all_indices(unaliased: true)
|
125
125
|
indices.each do |index|
|
126
|
-
|
126
|
+
Index.new(index).delete
|
127
127
|
end
|
128
128
|
indices
|
129
129
|
end
|
@@ -204,7 +204,7 @@ module Searchkick
|
|
204
204
|
# queue
|
205
205
|
|
206
206
|
def reindex_queue
|
207
|
-
|
207
|
+
ReindexQueue.new(name)
|
208
208
|
end
|
209
209
|
|
210
210
|
# reindex
|
@@ -237,13 +237,26 @@ module Searchkick
|
|
237
237
|
self.refresh if refresh
|
238
238
|
true
|
239
239
|
else
|
240
|
+
async = options.delete(:async)
|
241
|
+
if async
|
242
|
+
if async.is_a?(Hash) && async[:wait]
|
243
|
+
# TODO warn in 5.1
|
244
|
+
# Searchkick.warn "async option is deprecated - use mode: :async, wait: true instead"
|
245
|
+
options[:wait] = true unless options.key?(:wait)
|
246
|
+
else
|
247
|
+
# TODO warn in 5.1
|
248
|
+
# Searchkick.warn "async option is deprecated - use mode: :async instead"
|
249
|
+
end
|
250
|
+
options[:mode] ||= :async
|
251
|
+
end
|
252
|
+
|
240
253
|
full_reindex(relation, **options)
|
241
254
|
end
|
242
255
|
end
|
243
256
|
|
244
257
|
def create_index(index_options: nil)
|
245
258
|
index_options ||= self.index_options
|
246
|
-
index =
|
259
|
+
index = Index.new("#{name}_#{Time.now.strftime('%Y%m%d%H%M%S%L')}", @options)
|
247
260
|
index.create(index_options)
|
248
261
|
index
|
249
262
|
end
|
@@ -326,7 +339,7 @@ module Searchkick
|
|
326
339
|
end
|
327
340
|
|
328
341
|
def reindex_records(object, mode: nil, refresh: false, **options)
|
329
|
-
mode ||= Searchkick.callbacks_value || @options[:callbacks] ||
|
342
|
+
mode ||= Searchkick.callbacks_value || @options[:callbacks] || :inline
|
330
343
|
mode = :inline if mode == :bulk
|
331
344
|
|
332
345
|
result = RecordIndexer.new(self).reindex(object, mode: mode, full: false, **options)
|
@@ -336,12 +349,13 @@ module Searchkick
|
|
336
349
|
|
337
350
|
# https://gist.github.com/jarosan/3124884
|
338
351
|
# http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
|
339
|
-
|
340
|
-
|
352
|
+
def full_reindex(relation, import: true, resume: false, retain: false, mode: nil, refresh_interval: nil, scope: nil, wait: nil)
|
353
|
+
raise ArgumentError, "wait only available in :async mode" if !wait.nil? && mode != :async
|
354
|
+
|
341
355
|
if resume
|
342
356
|
index_name = all_indices.sort.last
|
343
|
-
raise
|
344
|
-
index =
|
357
|
+
raise Error, "No index to resume" unless index_name
|
358
|
+
index = Index.new(index_name, @options)
|
345
359
|
else
|
346
360
|
clean_indices unless retain
|
347
361
|
|
@@ -351,7 +365,7 @@ module Searchkick
|
|
351
365
|
end
|
352
366
|
|
353
367
|
import_options = {
|
354
|
-
mode: (
|
368
|
+
mode: (mode || :inline),
|
355
369
|
full: true,
|
356
370
|
resume: resume,
|
357
371
|
scope: scope
|
@@ -365,7 +379,7 @@ module Searchkick
|
|
365
379
|
import_before_promotion(index, relation, **import_options) if import
|
366
380
|
|
367
381
|
# get existing indices to remove
|
368
|
-
unless async
|
382
|
+
unless mode == :async
|
369
383
|
check_uuid(uuid, index.uuid)
|
370
384
|
promote(index.name, update_refresh_interval: !refresh_interval.nil?)
|
371
385
|
clean_indices unless retain
|
@@ -378,8 +392,8 @@ module Searchkick
|
|
378
392
|
index.import_scope(relation, **import_options) if import
|
379
393
|
end
|
380
394
|
|
381
|
-
if async
|
382
|
-
if
|
395
|
+
if mode == :async
|
396
|
+
if wait
|
383
397
|
puts "Created index: #{index.name}"
|
384
398
|
puts "Jobs queued. Waiting..."
|
385
399
|
loop do
|
@@ -417,7 +431,7 @@ module Searchkick
|
|
417
431
|
# https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#index-creation
|
418
432
|
def check_uuid(old_uuid, new_uuid)
|
419
433
|
if old_uuid != new_uuid
|
420
|
-
raise
|
434
|
+
raise Error, "Safety check failed - only run one Model.reindex per model at a time"
|
421
435
|
end
|
422
436
|
end
|
423
437
|
|
@@ -513,7 +513,7 @@ module Searchkick
|
|
513
513
|
else
|
514
514
|
[:searchkick_search2, :searchkick_word_search].each do |analyzer|
|
515
515
|
unless settings[:analysis][:analyzer][analyzer].key?(:filter)
|
516
|
-
raise
|
516
|
+
raise Error, "Search synonyms are not supported yet for language"
|
517
517
|
end
|
518
518
|
|
519
519
|
settings[:analysis][:analyzer][analyzer][:filter].insert(2, "searchkick_synonym_graph")
|
data/lib/searchkick/model.rb
CHANGED
@@ -22,16 +22,18 @@ module Searchkick
|
|
22
22
|
raise ArgumentError, "Invalid value for callbacks"
|
23
23
|
end
|
24
24
|
|
25
|
+
base = self
|
26
|
+
|
25
27
|
mod = Module.new
|
26
28
|
include(mod)
|
27
29
|
mod.module_eval do
|
28
30
|
def reindex(method_name = nil, mode: nil, refresh: false)
|
29
31
|
self.class.searchkick_index.reindex([self], method_name: method_name, mode: mode, refresh: refresh, single: true)
|
30
|
-
end
|
32
|
+
end unless base.method_defined?(:reindex)
|
31
33
|
|
32
34
|
def similar(**options)
|
33
35
|
self.class.searchkick_index.similar_record(self, **options)
|
34
|
-
end
|
36
|
+
end unless base.method_defined?(:similar)
|
35
37
|
|
36
38
|
def search_data
|
37
39
|
data = respond_to?(:to_hash) ? to_hash : serializable_hash
|
@@ -39,11 +41,11 @@ module Searchkick
|
|
39
41
|
data.delete("_id")
|
40
42
|
data.delete("_type")
|
41
43
|
data
|
42
|
-
end
|
44
|
+
end unless base.method_defined?(:search_data)
|
43
45
|
|
44
46
|
def should_index?
|
45
47
|
true
|
46
|
-
end
|
48
|
+
end unless base.method_defined?(:should_index?)
|
47
49
|
end
|
48
50
|
|
49
51
|
class_eval do
|
@@ -64,10 +66,10 @@ module Searchkick
|
|
64
66
|
alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
|
65
67
|
|
66
68
|
def searchkick_index(name: nil)
|
67
|
-
|
68
|
-
|
69
|
+
index_name = name || searchkick_klass.searchkick_index_name
|
70
|
+
index_name = index_name.call if index_name.respond_to?(:call)
|
69
71
|
index_cache = class_variable_get(:@@searchkick_index_cache)
|
70
|
-
index_cache.fetch(
|
72
|
+
index_cache.fetch(index_name) { Searchkick::Index.new(index_name, searchkick_options) }
|
71
73
|
end
|
72
74
|
alias_method :search_index, :searchkick_index unless method_defined?(:search_index)
|
73
75
|
|
data/lib/searchkick/query.rb
CHANGED
@@ -187,11 +187,11 @@ module Searchkick
|
|
187
187
|
end
|
188
188
|
|
189
189
|
# set execute for multi search
|
190
|
-
@execute =
|
190
|
+
@execute = Results.new(searchkick_klass, response, opts)
|
191
191
|
end
|
192
192
|
|
193
193
|
def retry_misspellings?(response)
|
194
|
-
@misspellings_below &&
|
194
|
+
@misspellings_below && Results.new(searchkick_klass, response).total_count < @misspellings_below
|
195
195
|
end
|
196
196
|
|
197
197
|
private
|
@@ -14,7 +14,7 @@ module Searchkick
|
|
14
14
|
case mode
|
15
15
|
when :async
|
16
16
|
unless defined?(ActiveJob)
|
17
|
-
raise
|
17
|
+
raise Error, "Active Job not found"
|
18
18
|
end
|
19
19
|
|
20
20
|
# we could likely combine ReindexV2Job, BulkReindexJob, and ProcessBatchJob
|
@@ -45,7 +45,7 @@ module Searchkick
|
|
45
45
|
end
|
46
46
|
when :queue
|
47
47
|
if method_name
|
48
|
-
raise
|
48
|
+
raise Error, "Partial reindex not supported with queue option"
|
49
49
|
end
|
50
50
|
|
51
51
|
index.reindex_queue.push_records(records)
|
data/lib/searchkick/relation.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
module Searchkick
|
2
2
|
class Relation
|
3
|
+
NO_DEFAULT_VALUE = Object.new
|
4
|
+
|
3
5
|
# note: modifying body directly is not supported
|
4
6
|
# and has no impact on query after being executed
|
5
7
|
# TODO freeze body object?
|
6
|
-
delegate :body, :params, to:
|
8
|
+
delegate :body, :params, to: :query
|
7
9
|
delegate_missing_to :private_execute
|
8
10
|
|
9
11
|
def initialize(model, term = "*", **options)
|
10
|
-
@
|
12
|
+
@model = model
|
13
|
+
@term = term
|
14
|
+
@options = options
|
15
|
+
|
16
|
+
# generate query to validate options
|
17
|
+
query
|
11
18
|
end
|
12
19
|
|
13
20
|
# same as Active Record
|
@@ -23,14 +30,196 @@ module Searchkick
|
|
23
30
|
self
|
24
31
|
end
|
25
32
|
|
33
|
+
# experimental
|
34
|
+
def limit(value)
|
35
|
+
clone.limit!(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
# experimental
|
39
|
+
def limit!(value)
|
40
|
+
check_loaded
|
41
|
+
@options[:limit] = value
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# experimental
|
46
|
+
def offset(value = NO_DEFAULT_VALUE)
|
47
|
+
# TODO remove in Searchkick 6
|
48
|
+
if value == NO_DEFAULT_VALUE
|
49
|
+
private_execute.offset
|
50
|
+
else
|
51
|
+
clone.offset!(value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# experimental
|
56
|
+
def offset!(value)
|
57
|
+
check_loaded
|
58
|
+
@options[:offset] = value
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# experimental
|
63
|
+
def page(value)
|
64
|
+
clone.page!(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
# experimental
|
68
|
+
def page!(value)
|
69
|
+
check_loaded
|
70
|
+
@options[:page] = value
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# experimental
|
75
|
+
def per_page(value = NO_DEFAULT_VALUE)
|
76
|
+
# TODO remove in Searchkick 6
|
77
|
+
if value == NO_DEFAULT_VALUE
|
78
|
+
private_execute.per_page
|
79
|
+
else
|
80
|
+
clone.per_page!(value)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# experimental
|
85
|
+
def per_page!(value)
|
86
|
+
check_loaded
|
87
|
+
@options[:per_page] = value
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
# experimental
|
92
|
+
def where(value = NO_DEFAULT_VALUE)
|
93
|
+
if value == NO_DEFAULT_VALUE
|
94
|
+
Where.new(self)
|
95
|
+
else
|
96
|
+
clone.where!(value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# experimental
|
101
|
+
def where!(value)
|
102
|
+
check_loaded
|
103
|
+
if @options[:where]
|
104
|
+
@options[:where] = {_and: [@options[:where], ensure_permitted(value)]}
|
105
|
+
else
|
106
|
+
@options[:where] = ensure_permitted(value)
|
107
|
+
end
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
# experimental
|
112
|
+
def rewhere(value)
|
113
|
+
clone.rewhere!(value)
|
114
|
+
end
|
115
|
+
|
116
|
+
# experimental
|
117
|
+
def rewhere!(value)
|
118
|
+
check_loaded
|
119
|
+
@options[:where] = ensure_permitted(value)
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
# experimental
|
124
|
+
def order(*values)
|
125
|
+
clone.order!(*values)
|
126
|
+
end
|
127
|
+
|
128
|
+
# experimental
|
129
|
+
def order!(*values)
|
130
|
+
values = values.first if values.size == 1 && values.first.is_a?(Array)
|
131
|
+
check_loaded
|
132
|
+
(@options[:order] ||= []).concat(values)
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
# experimental
|
137
|
+
def reorder(*values)
|
138
|
+
clone.reorder!(*values)
|
139
|
+
end
|
140
|
+
|
141
|
+
# experimental
|
142
|
+
def reorder!(*values)
|
143
|
+
check_loaded
|
144
|
+
@options[:order] = values
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
# experimental
|
149
|
+
def select(*values, &block)
|
150
|
+
if block_given?
|
151
|
+
private_execute.select(*values, &block)
|
152
|
+
else
|
153
|
+
clone.select!(*values)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# experimental
|
158
|
+
def select!(*values)
|
159
|
+
check_loaded
|
160
|
+
(@options[:select] ||= []).concat(values)
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
# experimental
|
165
|
+
def reselect(*values)
|
166
|
+
clone.reselect!(*values)
|
167
|
+
end
|
168
|
+
|
169
|
+
# experimental
|
170
|
+
def reselect!(*values)
|
171
|
+
check_loaded
|
172
|
+
@options[:select] = values
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
# experimental
|
177
|
+
def includes(*values)
|
178
|
+
clone.includes!(*values)
|
179
|
+
end
|
180
|
+
|
181
|
+
# experimental
|
182
|
+
def includes!(*values)
|
183
|
+
check_loaded
|
184
|
+
(@options[:includes] ||= []).concat(values)
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# experimental
|
189
|
+
def only(*keys)
|
190
|
+
Relation.new(@model, @term, **@options.slice(*keys))
|
191
|
+
end
|
192
|
+
|
193
|
+
# experimental
|
194
|
+
def except(*keys)
|
195
|
+
Relation.new(@model, @term, **@options.except(*keys))
|
196
|
+
end
|
197
|
+
|
198
|
+
def loaded?
|
199
|
+
!@execute.nil?
|
200
|
+
end
|
201
|
+
|
26
202
|
private
|
27
203
|
|
28
204
|
def private_execute
|
29
|
-
@execute ||=
|
205
|
+
@execute ||= query.execute
|
30
206
|
end
|
31
207
|
|
32
208
|
def query
|
33
|
-
@query
|
209
|
+
@query ||= Query.new(@model, @term, **@options)
|
210
|
+
end
|
211
|
+
|
212
|
+
def check_loaded
|
213
|
+
raise Error, "Relation loaded" if loaded?
|
214
|
+
|
215
|
+
# reset query since options will change
|
216
|
+
@query = nil
|
217
|
+
end
|
218
|
+
|
219
|
+
# provides *very* basic protection from unfiltered parameters
|
220
|
+
# this is not meant to be comprehensive and may be expanded in the future
|
221
|
+
def ensure_permitted(obj)
|
222
|
+
obj.to_h
|
34
223
|
end
|
35
224
|
end
|
36
225
|
end
|
data/lib/searchkick/results.rb
CHANGED
@@ -141,7 +141,7 @@ module Searchkick
|
|
141
141
|
|
142
142
|
def hits
|
143
143
|
if error
|
144
|
-
raise
|
144
|
+
raise Error, "Query error - use the error method to view it"
|
145
145
|
else
|
146
146
|
@response["hits"]["hits"]
|
147
147
|
end
|
@@ -178,7 +178,7 @@ module Searchkick
|
|
178
178
|
end
|
179
179
|
|
180
180
|
def scroll
|
181
|
-
raise
|
181
|
+
raise Error, "Pass `scroll` option to the search method for scrolling" unless scroll_id
|
182
182
|
|
183
183
|
if block_given?
|
184
184
|
records = self
|
@@ -191,10 +191,10 @@ module Searchkick
|
|
191
191
|
else
|
192
192
|
begin
|
193
193
|
# TODO Active Support notifications for this scroll call
|
194
|
-
|
194
|
+
Results.new(@klass, Searchkick.client.scroll(scroll: options[:scroll], body: {scroll_id: scroll_id}), @options)
|
195
195
|
rescue => e
|
196
196
|
if Searchkick.not_found_error?(e) && e.message =~ /search_context_missing_exception/i
|
197
|
-
raise
|
197
|
+
raise Error, "Scroll id has expired"
|
198
198
|
else
|
199
199
|
raise e
|
200
200
|
end
|
@@ -232,7 +232,7 @@ module Searchkick
|
|
232
232
|
index_alias = index.split("_")[0..-2].join("_")
|
233
233
|
Array((options[:index_mapping] || {})[index_alias])
|
234
234
|
end
|
235
|
-
raise
|
235
|
+
raise Error, "Unknown model for index: #{index}. Pass the `models` option to the search method." unless models.any?
|
236
236
|
index_models[index] = models
|
237
237
|
end
|
238
238
|
|
data/lib/searchkick/version.rb
CHANGED
data/lib/searchkick.rb
CHANGED
@@ -27,6 +27,7 @@ require "searchkick/relation"
|
|
27
27
|
require "searchkick/relation_indexer"
|
28
28
|
require "searchkick/results"
|
29
29
|
require "searchkick/version"
|
30
|
+
require "searchkick/where"
|
30
31
|
|
31
32
|
# integrations
|
32
33
|
require "searchkick/railtie" if defined?(Rails)
|
@@ -171,7 +172,7 @@ module Searchkick
|
|
171
172
|
end
|
172
173
|
|
173
174
|
options = options.merge(block: block) if block
|
174
|
-
|
175
|
+
Relation.new(klass, term, **options)
|
175
176
|
end
|
176
177
|
|
177
178
|
def self.multi_search(queries)
|
@@ -183,7 +184,7 @@ module Searchkick
|
|
183
184
|
body: queries.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join,
|
184
185
|
}
|
185
186
|
ActiveSupport::Notifications.instrument("multi_search.searchkick", event) do
|
186
|
-
|
187
|
+
MultiSearch.new(queries).perform
|
187
188
|
end
|
188
189
|
end
|
189
190
|
|
@@ -241,9 +242,9 @@ module Searchkick
|
|
241
242
|
end
|
242
243
|
|
243
244
|
def self.reindex_status(index_name)
|
244
|
-
raise
|
245
|
+
raise Error, "Redis not configured" unless redis
|
245
246
|
|
246
|
-
batches_left =
|
247
|
+
batches_left = Index.new(index_name).batches_left
|
247
248
|
{
|
248
249
|
completed: batches_left == 0,
|
249
250
|
batches_left: batches_left
|
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: 5.0.
|
4
|
+
version: 5.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- lib/searchkick/relation_indexer.rb
|
72
72
|
- lib/searchkick/results.rb
|
73
73
|
- lib/searchkick/version.rb
|
74
|
+
- lib/searchkick/where.rb
|
74
75
|
- lib/tasks/searchkick.rake
|
75
76
|
homepage: https://github.com/ankane/searchkick
|
76
77
|
licenses:
|
@@ -91,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
|
-
rubygems_version: 3.3.
|
95
|
+
rubygems_version: 3.3.7
|
95
96
|
signing_key:
|
96
97
|
specification_version: 4
|
97
98
|
summary: Intelligent search made easy with Rails and Elasticsearch or OpenSearch
|