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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc223f746f73c44e5c6f56b5e351cb31ae5a655069d92f5bfd2a0b2e531afae8
4
- data.tar.gz: 1969964f34a5adaf3ed746230fbef57673454f7304d890466e2814d27eb2cb47
3
+ metadata.gz: ec7741cb306a56f1a5ae5a07450c5c102236bc6103079fc34a5f602fa2853b31
4
+ data.tar.gz: 2bd747ee31846c901ce2a58125b34e9c90af6937a2c7dc041f2dd9f69701f1e9
5
5
  SHA512:
6
- metadata.gz: 17259c3e071d69c3852584aba480b523211b167f1249951f32d1bd2f761fc07322144283f0ccabac4878c0a2ff2f4c9dd8def2db2beac004c449839052e503ea
7
- data.tar.gz: 1109a53d99eb30f7e76cab8df00505c82af5e898c4e3f1821773932b8f6917e68f6bfedfa952b8e4acb25387607edf10856cc994021943671086af4d751cc728
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 "apples", where: {in_stock: true}, limit: 10, offset: 50
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 "milk", page: params[:page], per_page: 20
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 "fresh honey" # fresh AND honey
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 "fresh honey", operator: "or" # fresh OR honey
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 "back", fields: [:name], match: :word_start
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
- User.search query, fields: [{email: :exact}, :name]
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
- User.search "fresh honey", match: :phrase
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 query, fields: [:name_tagged]
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 "zucini", misspellings: {edit_distance: 2} # zucchini
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 "zuchini", misspellings: {below: 5}
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 "zuchini", misspellings: false # no zucchini
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 "zucini", fields: [:name, :color], misspellings: {fields: [:name]}
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 "butter", exclude: ["peanut butter"]
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 query, exclude: exclude_queries[query]
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 "🍨🍰", emoji: true
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
- User.find_each(&:update_fields)
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
- User.find_each(&:update_fields)
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 "apple", track: {user_id: current_user.id}
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
- ```ruby
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 "milk", boost_where: {orderer_ids: current_user.id}
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 "jurassic pa", fields: [:title], match: :word_start
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 "peantu butta", suggest: true
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
  ![Aggregations](https://gist.github.com/ankane/b6988db2802aca68a589b31e41b44195/raw/40febe948427e5bc53ec4e5dc248822855fef76f/facets.png)
805
805
 
806
806
  ```ruby
807
- products = Product.search "chuck taylor", aggs: [:product_type, :gender, :brand]
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 "wingtips", where: {color: "brandy"}, aggs: [:size]
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 "wingtips", where: {color: "brandy"}, aggs: [:size], smart_aggs: false
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 "wingtips", aggs: {size: {where: {color: "brandy"}}}
828
+ Product.search("wingtips", aggs: {size: {where: {color: "brandy"}}})
829
829
  ```
830
830
 
831
831
  Limit
832
832
 
833
833
  ```ruby
834
- Product.search "apples", aggs: {store_id: {limit: 10}}
834
+ Product.search("apples", aggs: {store_id: {limit: 10}})
835
835
  ```
836
836
 
837
837
  Order
838
838
 
839
839
  ```ruby
840
- Product.search "wingtips", aggs: {color: {order: {"_key" => "asc"}}} # alphabetically
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 "*", aggs: {price: {ranges: price_ranges}}
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 "apples", aggs: {store_id: {min_doc_count: 2}}
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 "*", aggs: {color: {script: {source: "'Color: ' + _value"}}}
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 "pear", aggs: {products_per_year: {date_histogram: {field: :created_at, interval: :year}}}
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 "orange", body_options: {aggs: {price: {histogram: {field: :price, interval: 10}}}}
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 Product < ApplicationRecord
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 "cinema", highlight: true
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 "cinema", highlight: {tag: "<strong>"}
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 "cinema", fields: [:name], highlight: {fields: [:description]}
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 "cinema", highlight: {fragment_size: 20}
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 "cinema", fields: [:name], highlight: {fields: {name: {fragment_size: 200}}}
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 "pizza", where: {location: {near: {lat: 37, lon: -114}, within: "100mi"}} # or 160km
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 "sushi", where: {location: {top_left: {lat: 38, lon: -123}, bottom_right: {lat: 37, lon: -122}}}
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 "dessert", where: {location: {geo_polygon: {points: [{lat: 38, lon: -123}, {lat: 39, lon: -123}, {lat: 37, lon: 122}]}}}
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 "noodles", boost_by_distance: {location: {origin: {lat: 37, lon: -122}}}
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 "wings", boost_by_distance: {location: {origin: {lat: 37, lon: -122}, function: "linear", scale: "30mi", decay: 0.5}}
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 "soup", where: {bounds: {geo_shape: {type: "polygon", coordinates: [[{lat: 38, lon: -123}, ...]]}}}
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 "salad", where: {bounds: {geo_shape: {type: "circle", relation: "within", coordinates: [{lat: 38, lon: -123}], radius: "1km"}}}
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 "burger", where: {bounds: {geo_shape: {type: "envelope", relation: "disjoint", coordinates: [{lat: 38, lon: -123}, {lat: 37, lon: -122}]}}}
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 "*" # all animals
1051
- Dog.search "*" # just dogs
1052
- Animal.search "*", type: [Dog, Cat] # just cats and dogs
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 "airbudd", suggest: true # suggestions for all animals
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](https://github.com/ankane/searchkick/blob/31780ddac7a89eab1e0552a32b403f2040a37931/lib/searchkick/index_options.rb#L32).
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 `config/initializers/elasticsearch.rb` with multiple hosts:
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 [elasticsearch-transport](https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-transport) for a complete list of options.
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(async: true)
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(async: {wait: true})
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(async: true, refresh_interval: "30s")
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 "ice cream", routing: params[:city_id]
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 body: {query: {match: {name: "milk"}}}
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 "milk", body_options: {min_score: 1}
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 "apples" do |body|
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
- Searchkick is built on top of the [elasticsearch](https://github.com/elastic/elasticsearch-ruby) gem. To access the client directly, use:
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 "milk", models: [Product, Category]
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 "*", scroll: "1m"
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
- User.search "san", fields: ["address.city"], where: {"address.zip_code" => 12345}
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 "milk", includes: [:brand, :stores]
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 "milk", scope_results: ->(r) { r.with_attached_images }
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 "api", misspellings: {prefix_length: 2} # api, apt, no ahi
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 "ah", misspellings: {prefix_length: 2} # ah, no aha
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
 
@@ -71,12 +71,12 @@ module Searchkick
71
71
  }
72
72
  )
73
73
 
74
- Searchkick::Results.new(nil, response).total_count
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 = Searchkick::Index.new(new_name, @options)
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
- Searchkick::Index.new(index).delete
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
- Searchkick::ReindexQueue.new(name)
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 = Searchkick::Index.new("#{name}_#{Time.now.strftime('%Y%m%d%H%M%S%L')}", @options)
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] || true
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
- # TODO deprecate async in favor of mode: :async, wait: true/false
340
- def full_reindex(relation, import: true, resume: false, retain: false, async: false, refresh_interval: nil, scope: nil)
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 Searchkick::Error, "No index to resume" unless index_name
344
- index = Searchkick::Index.new(index_name, @options)
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: (async ? :async : :inline),
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 async.is_a?(Hash) && async[:wait]
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 Searchkick::Error, "Safety check failed - only run one Model.reindex per model at a time"
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 Searchkick::Error, "Search synonyms are not supported yet for language"
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")
@@ -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
- index = name || searchkick_index_name
68
- index = index.call if index.respond_to?(:call)
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(index) { Searchkick::Index.new(index, searchkick_options) }
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
 
@@ -187,11 +187,11 @@ module Searchkick
187
187
  end
188
188
 
189
189
  # set execute for multi search
190
- @execute = Searchkick::Results.new(searchkick_klass, response, opts)
190
+ @execute = Results.new(searchkick_klass, response, opts)
191
191
  end
192
192
 
193
193
  def retry_misspellings?(response)
194
- @misspellings_below && Searchkick::Results.new(searchkick_klass, response).total_count < @misspellings_below
194
+ @misspellings_below && Results.new(searchkick_klass, response).total_count < @misspellings_below
195
195
  end
196
196
 
197
197
  private
@@ -25,6 +25,7 @@ module Searchkick
25
25
  {delete: record_data}
26
26
  end
27
27
 
28
+ # custom id can be useful for load: false
28
29
  def search_id
29
30
  id = record.respond_to?(:search_document_id) ? record.search_document_id : record.id
30
31
  id.is_a?(Numeric) ? id : id.to_s
@@ -14,7 +14,7 @@ module Searchkick
14
14
  case mode
15
15
  when :async
16
16
  unless defined?(ActiveJob)
17
- raise Searchkick::Error, "Active Job not found"
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 Searchkick::Error, "Partial reindex not supported with queue option"
48
+ raise Error, "Partial reindex not supported with queue option"
49
49
  end
50
50
 
51
51
  index.reindex_queue.push_records(records)
@@ -5,7 +5,7 @@ module Searchkick
5
5
  def initialize(name)
6
6
  @name = name
7
7
 
8
- raise Searchkick::Error, "Searchkick.redis not set" unless Searchkick.redis
8
+ raise Error, "Searchkick.redis not set" unless Searchkick.redis
9
9
  end
10
10
 
11
11
  # supports single and multiple ids
@@ -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: :@query
8
+ delegate :body, :params, to: :query
7
9
  delegate_missing_to :private_execute
8
10
 
9
11
  def initialize(model, term = "*", **options)
10
- @query = Query.new(model, term, **options)
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 ||= @query.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
@@ -141,7 +141,7 @@ module Searchkick
141
141
 
142
142
  def hits
143
143
  if error
144
- raise Searchkick::Error, "Query error - use the error method to view it"
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 Searchkick::Error, "Pass `scroll` option to the search method for scrolling" unless scroll_id
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
- Searchkick::Results.new(@klass, Searchkick.client.scroll(scroll: options[:scroll], body: {scroll_id: scroll_id}), @options)
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 Searchkick::Error, "Scroll id has expired"
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 Searchkick::Error, "Unknown model for index: #{index}. Pass the `models` option to the search method." unless models.any?
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
 
@@ -1,3 +1,3 @@
1
1
  module Searchkick
2
- VERSION = "5.0.0"
2
+ VERSION = "5.0.3"
3
3
  end
@@ -0,0 +1,11 @@
1
+ module Searchkick
2
+ class Where
3
+ def initialize(relation)
4
+ @relation = relation
5
+ end
6
+
7
+ def not(value)
8
+ @relation.where(_not: value)
9
+ end
10
+ end
11
+ end
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
- Searchkick::Relation.new(klass, term, **options)
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
- Searchkick::MultiSearch.new(queries).perform
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 Searchkick::Error, "Redis not configured" unless redis
245
+ raise Error, "Redis not configured" unless redis
245
246
 
246
- batches_left = Searchkick::Index.new(index_name).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.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-02-22 00:00:00.000000000 Z
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.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