searchkick 5.0.2 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/LICENSE.txt +1 -1
- data/README.md +144 -89
- data/lib/searchkick/index.rb +3 -2
- data/lib/searchkick/index_options.rb +2 -2
- data/lib/searchkick/log_subscriber.rb +3 -3
- data/lib/searchkick/middleware.rb +8 -1
- data/lib/searchkick/model.rb +4 -4
- data/lib/searchkick/query.rb +18 -4
- data/lib/searchkick/reindex_queue.rb +12 -7
- data/lib/searchkick/relation.rb +137 -2
- data/lib/searchkick/relation_indexer.rb +12 -7
- data/lib/searchkick/results.rb +3 -1
- data/lib/searchkick/version.rb +1 -1
- data/lib/searchkick/where.rb +11 -0
- data/lib/searchkick.rb +23 -21
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18ce029db59c59e668c7c5f4d1e05702d9631f307736251856658eb9b6a20618
|
4
|
+
data.tar.gz: c4f72347ed409d994bb44d16a48da881165ce825e0dc00192a7acba2b687318f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b17985dfd054b0fe5579ea1057ab9f052a7dbf54c091628ade76fb86616b7c938ea616f3d0367d486e63d66d037b86866543ae902350c24f9b63f25e079e3859
|
7
|
+
data.tar.gz: b576abbc0681e466638164b9b92c8edc5e91c771fd3c538d5b4f8c07597c939be882d4dc9b3cb89e8e43ae913804960c93fdfa2ab8eab1b1cee9566f86adcbd5
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,61 @@
|
|
1
|
+
## 5.3.0 (2023-07-02)
|
2
|
+
|
3
|
+
- Fixed error with `cutoff_frequency`
|
4
|
+
- Dropped support for Ruby < 3 and Active Record < 6.1
|
5
|
+
- Dropped support for Mongoid < 7
|
6
|
+
|
7
|
+
## 5.2.4 (2023-05-11)
|
8
|
+
|
9
|
+
- Fixed error with non-string routing and `:async` mode
|
10
|
+
|
11
|
+
## 5.2.3 (2023-04-12)
|
12
|
+
|
13
|
+
- Fixed error with missing records and multiple models
|
14
|
+
|
15
|
+
## 5.2.2 (2023-04-01)
|
16
|
+
|
17
|
+
- Fixed `total_docs` method
|
18
|
+
- Fixed deprecation warning with Active Support 7.1
|
19
|
+
|
20
|
+
## 5.2.1 (2023-02-21)
|
21
|
+
|
22
|
+
- Added support for `redis-client` gem
|
23
|
+
|
24
|
+
## 5.2.0 (2023-02-08)
|
25
|
+
|
26
|
+
- Added model name to warning about missing records
|
27
|
+
- Fixed unnecessary data loading when reindexing relations with `:async` and `:queue` modes
|
28
|
+
|
29
|
+
## 5.1.2 (2023-01-29)
|
30
|
+
|
31
|
+
- Fixed error with missing point in time
|
32
|
+
|
33
|
+
## 5.1.1 (2022-12-05)
|
34
|
+
|
35
|
+
- Added support for strings for `offset` and `per_page`
|
36
|
+
|
37
|
+
## 5.1.0 (2022-10-12)
|
38
|
+
|
39
|
+
- Added support for fractional search timeout
|
40
|
+
- Fixed search timeout with `elasticsearch` 8+ and `opensearch-ruby` gems
|
41
|
+
- Fixed search timeout not applying to `multi_search`
|
42
|
+
|
43
|
+
## 5.0.5 (2022-10-09)
|
44
|
+
|
45
|
+
- Added `model` method to `Searchkick::Relation`
|
46
|
+
- Fixed deprecation warning with `redis` gem
|
47
|
+
- Fixed `respond_to?` method on relation loading relation
|
48
|
+
- Fixed `Relation loaded` error for non-mutating methods on relation
|
49
|
+
|
50
|
+
## 5.0.4 (2022-06-16)
|
51
|
+
|
52
|
+
- Added `max_result_window` option
|
53
|
+
- Improved error message for unsupported versions of Elasticsearch
|
54
|
+
|
55
|
+
## 5.0.3 (2022-03-13)
|
56
|
+
|
57
|
+
- Fixed context for index name for inherited models
|
58
|
+
|
1
59
|
## 5.0.2 (2022-03-03)
|
2
60
|
|
3
61
|
- Fixed index name for inherited models
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -43,15 +43,13 @@ Check out [Searchjoy](https://github.com/ankane/searchjoy) for analytics and [Au
|
|
43
43
|
- [Reference](#reference)
|
44
44
|
- [Contributing](#contributing)
|
45
45
|
|
46
|
-
Searchkick 5.0 was recently released! See [how to upgrade](#upgrading)
|
47
|
-
|
48
46
|
## Getting Started
|
49
47
|
|
50
48
|
Install [Elasticsearch](https://www.elastic.co/downloads/elasticsearch) or [OpenSearch](https://opensearch.org/downloads.html). For Homebrew, use:
|
51
49
|
|
52
50
|
```sh
|
53
|
-
brew install elasticsearch
|
54
|
-
brew services start elasticsearch
|
51
|
+
brew install elastic/tap/elasticsearch-full
|
52
|
+
brew services start elasticsearch-full
|
55
53
|
# or
|
56
54
|
brew install opensearch
|
57
55
|
brew services start opensearch
|
@@ -66,7 +64,7 @@ gem "elasticsearch" # select one
|
|
66
64
|
gem "opensearch-ruby" # select one
|
67
65
|
```
|
68
66
|
|
69
|
-
The latest version works with Elasticsearch 7 and 8 and OpenSearch 1. For Elasticsearch 6, use version 4.6.3 and [this readme](https://github.com/ankane/searchkick/blob/v4.6.3/README.md).
|
67
|
+
The latest version works with Elasticsearch 7 and 8 and OpenSearch 1 and 2. For Elasticsearch 6, use version 4.6.3 and [this readme](https://github.com/ankane/searchkick/blob/v4.6.3/README.md).
|
70
68
|
|
71
69
|
Add searchkick to models you want to search.
|
72
70
|
|
@@ -292,12 +290,18 @@ Option | Matches | Example
|
|
292
290
|
|
293
291
|
The default is `:word`. The most matches will happen with `:word_middle`.
|
294
292
|
|
293
|
+
To specify different matching for different fields, use:
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
Product.search(query, fields: [{name: :word_start}, {brand: :word_middle}])
|
297
|
+
```
|
298
|
+
|
295
299
|
### Exact Matches
|
296
300
|
|
297
301
|
To match a field exactly (case-sensitive), use:
|
298
302
|
|
299
303
|
```ruby
|
300
|
-
Product.search(query, fields: [{
|
304
|
+
Product.search(query, fields: [{name: :exact}])
|
301
305
|
```
|
302
306
|
|
303
307
|
### Phrase Matches
|
@@ -323,11 +327,11 @@ end
|
|
323
327
|
See the [list of languages](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-stemmer-tokenfilter.html#analysis-stemmer-tokenfilter-configure-parms). A few languages require plugins:
|
324
328
|
|
325
329
|
- `chinese` - [analysis-ik plugin](https://github.com/medcl/elasticsearch-analysis-ik)
|
326
|
-
- `chinese2` - [analysis-smartcn plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/
|
327
|
-
- `japanese` - [analysis-kuromoji plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/
|
330
|
+
- `chinese2` - [analysis-smartcn plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-smartcn.html)
|
331
|
+
- `japanese` - [analysis-kuromoji plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-kuromoji.html)
|
328
332
|
- `korean` - [analysis-openkoreantext plugin](https://github.com/open-korean-text/elasticsearch-analysis-openkoreantext)
|
329
|
-
- `korean2` - [analysis-nori plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/
|
330
|
-
- `polish` - [analysis-stempel plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/
|
333
|
+
- `korean2` - [analysis-nori plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-nori.html)
|
334
|
+
- `polish` - [analysis-stempel plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-stempel.html)
|
331
335
|
- `ukrainian` - [analysis-ukrainian plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/7.4/analysis-ukrainian.html)
|
332
336
|
- `vietnamese` - [analysis-vietnamese plugin](https://github.com/duydo/elasticsearch-analysis-vietnamese)
|
333
337
|
|
@@ -592,6 +596,14 @@ There are four strategies for keeping the index synced with your database.
|
|
592
596
|
end
|
593
597
|
```
|
594
598
|
|
599
|
+
And reindex a record or relation manually.
|
600
|
+
|
601
|
+
```ruby
|
602
|
+
product.reindex
|
603
|
+
# or
|
604
|
+
store.products.reindex(mode: :async)
|
605
|
+
```
|
606
|
+
|
595
607
|
You can also do bulk updates.
|
596
608
|
|
597
609
|
```ruby
|
@@ -608,6 +620,12 @@ Searchkick.callbacks(false) do
|
|
608
620
|
end
|
609
621
|
```
|
610
622
|
|
623
|
+
Or override the model’s strategy.
|
624
|
+
|
625
|
+
```ruby
|
626
|
+
product.reindex(mode: :async) # :inline or :queue
|
627
|
+
```
|
628
|
+
|
611
629
|
### Associations
|
612
630
|
|
613
631
|
Data is **not** automatically synced when an association is updated. If this is desired, add a callback to reindex:
|
@@ -654,20 +672,16 @@ The best starting point to improve your search **by far** is to track searches a
|
|
654
672
|
Product.search("apple", track: {user_id: current_user.id})
|
655
673
|
```
|
656
674
|
|
657
|
-
[See the docs](https://github.com/ankane/searchjoy) for how to install and use.
|
658
|
-
|
659
|
-
Focus on:
|
675
|
+
[See the docs](https://github.com/ankane/searchjoy) for how to install and use. Focus on top searches with a low conversion rate.
|
660
676
|
|
661
|
-
|
662
|
-
- top searches with no results
|
663
|
-
|
664
|
-
Searchkick can then use the conversion data to learn what users are looking for. If a user searches for “ice cream” and adds Ben & Jerry’s Chunky Monkey to the cart (our conversion metric at Instacart), that item gets a little more weight for similar searches.
|
677
|
+
Searchkick can then use the conversion data to learn what users are looking for. If a user searches for “ice cream” and adds Ben & Jerry’s Chunky Monkey to the cart (our conversion metric at Instacart), that item gets a little more weight for similar searches. This can make a huge difference on the quality of your search.
|
665
678
|
|
666
679
|
Add conversion data with:
|
667
680
|
|
668
681
|
```ruby
|
669
682
|
class Product < ApplicationRecord
|
670
|
-
has_many :
|
683
|
+
has_many :conversions, class_name: "Searchjoy::Conversion", as: :convertable
|
684
|
+
has_many :searches, class_name: "Searchjoy::Search", through: :conversions
|
671
685
|
|
672
686
|
searchkick conversions: [:conversions] # name of field
|
673
687
|
|
@@ -681,15 +695,100 @@ class Product < ApplicationRecord
|
|
681
695
|
end
|
682
696
|
```
|
683
697
|
|
684
|
-
Reindex and set up a cron job to add new conversions daily.
|
698
|
+
Reindex and set up a cron job to add new conversions daily. For zero downtime deployment, temporarily set `conversions: false` in your search calls until the data is reindexed.
|
699
|
+
|
700
|
+
### Performant Conversions
|
701
|
+
|
702
|
+
A performant way to do conversions is to cache them to prevent N+1 queries. For Postgres, create a migration with:
|
685
703
|
|
686
704
|
```ruby
|
687
|
-
|
705
|
+
add_column :products, :search_conversions, :jsonb
|
688
706
|
```
|
689
707
|
|
690
|
-
|
708
|
+
For MySQL, use `:json`, and for others, use `:text` with a [JSON serializer](https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html).
|
709
|
+
|
710
|
+
Next, update your model. Create a separate method for conversion data so you can use [partial reindexing](#partial-reindexing).
|
711
|
+
|
712
|
+
```ruby
|
713
|
+
class Product < ApplicationRecord
|
714
|
+
searchkick conversions: [:conversions]
|
691
715
|
|
692
|
-
|
716
|
+
def search_data
|
717
|
+
{
|
718
|
+
name: name,
|
719
|
+
category: category
|
720
|
+
}.merge(conversions_data)
|
721
|
+
end
|
722
|
+
|
723
|
+
def conversions_data
|
724
|
+
{
|
725
|
+
conversions: search_conversions || {}
|
726
|
+
}
|
727
|
+
end
|
728
|
+
end
|
729
|
+
```
|
730
|
+
|
731
|
+
Deploy and reindex your data. For zero downtime deployment, temporarily set `conversions: false` in your search calls until the data is reindexed.
|
732
|
+
|
733
|
+
```ruby
|
734
|
+
Product.reindex
|
735
|
+
```
|
736
|
+
|
737
|
+
Then, create a job to update the conversions column and reindex records with new conversions. Here’s one you can use for Searchjoy:
|
738
|
+
|
739
|
+
```ruby
|
740
|
+
class UpdateConversionsJob < ApplicationJob
|
741
|
+
def perform(class_name, since: nil, update: true, reindex: true)
|
742
|
+
model = Searchkick.load_model(class_name)
|
743
|
+
|
744
|
+
# get records that have a recent conversion
|
745
|
+
recently_converted_ids =
|
746
|
+
Searchjoy::Conversion.where(convertable_type: class_name).where(created_at: since..)
|
747
|
+
.order(:convertable_id).distinct.pluck(:convertable_id)
|
748
|
+
|
749
|
+
# split into batches
|
750
|
+
recently_converted_ids.in_groups_of(1000, false) do |ids|
|
751
|
+
if update
|
752
|
+
# fetch conversions
|
753
|
+
conversions =
|
754
|
+
Searchjoy::Conversion.where(convertable_id: ids, convertable_type: class_name)
|
755
|
+
.joins(:search).where.not(searchjoy_searches: {user_id: nil})
|
756
|
+
.group(:convertable_id, :query).distinct.count(:user_id)
|
757
|
+
|
758
|
+
# group by record
|
759
|
+
conversions_by_record = {}
|
760
|
+
conversions.each do |(id, query), count|
|
761
|
+
(conversions_by_record[id] ||= {})[query] = count
|
762
|
+
end
|
763
|
+
|
764
|
+
# update conversions column
|
765
|
+
model.transaction do
|
766
|
+
conversions_by_record.each do |id, conversions|
|
767
|
+
model.where(id: id).update_all(search_conversions: conversions)
|
768
|
+
end
|
769
|
+
end
|
770
|
+
end
|
771
|
+
|
772
|
+
if reindex
|
773
|
+
# reindex conversions data
|
774
|
+
model.where(id: ids).reindex(:conversions_data)
|
775
|
+
end
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
```
|
780
|
+
|
781
|
+
Run the job:
|
782
|
+
|
783
|
+
```ruby
|
784
|
+
UpdateConversionsJob.perform_now("Product")
|
785
|
+
```
|
786
|
+
|
787
|
+
And set it up to run daily.
|
788
|
+
|
789
|
+
```ruby
|
790
|
+
UpdateConversionsJob.perform_later("Product", since: 1.day.ago)
|
791
|
+
```
|
693
792
|
|
694
793
|
## Personalized Results
|
695
794
|
|
@@ -1011,7 +1110,7 @@ Restaurant.search("soup", where: {bounds: {geo_shape: {type: "polygon", coordina
|
|
1011
1110
|
Falling entirely within the query shape
|
1012
1111
|
|
1013
1112
|
```ruby
|
1014
|
-
Restaurant.search("salad", where: {bounds: {geo_shape: {type: "circle", relation: "within", coordinates:
|
1113
|
+
Restaurant.search("salad", where: {bounds: {geo_shape: {type: "circle", relation: "within", coordinates: {lat: 38, lon: -123}, radius: "1km"}}})
|
1015
1114
|
```
|
1016
1115
|
|
1017
1116
|
Not touching the query shape
|
@@ -1575,11 +1674,12 @@ Reindex a subset of attributes to reduce time spent generating search data and c
|
|
1575
1674
|
class Product < ApplicationRecord
|
1576
1675
|
def search_data
|
1577
1676
|
{
|
1578
|
-
name: name
|
1579
|
-
|
1677
|
+
name: name,
|
1678
|
+
category: category
|
1679
|
+
}.merge(prices_data)
|
1580
1680
|
end
|
1581
1681
|
|
1582
|
-
def
|
1682
|
+
def prices_data
|
1583
1683
|
{
|
1584
1684
|
price: price,
|
1585
1685
|
sale_price: sale_price
|
@@ -1591,68 +1691,7 @@ end
|
|
1591
1691
|
And use:
|
1592
1692
|
|
1593
1693
|
```ruby
|
1594
|
-
Product.reindex(:
|
1595
|
-
```
|
1596
|
-
|
1597
|
-
### Performant Conversions
|
1598
|
-
|
1599
|
-
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.
|
1600
|
-
|
1601
|
-
```ruby
|
1602
|
-
class Product < ApplicationRecord
|
1603
|
-
def search_data
|
1604
|
-
{
|
1605
|
-
name: name
|
1606
|
-
}.merge(search_conversions)
|
1607
|
-
end
|
1608
|
-
|
1609
|
-
def search_conversions
|
1610
|
-
{
|
1611
|
-
conversions: Rails.cache.read("search_conversions:#{self.class.name}:#{id}") || {}
|
1612
|
-
}
|
1613
|
-
end
|
1614
|
-
end
|
1615
|
-
```
|
1616
|
-
|
1617
|
-
Create a job to update the cache and reindex records with new conversions.
|
1618
|
-
|
1619
|
-
```ruby
|
1620
|
-
class ReindexConversionsJob < ApplicationJob
|
1621
|
-
def perform(class_name)
|
1622
|
-
# get records that have a recent conversion
|
1623
|
-
recently_converted_ids =
|
1624
|
-
Searchjoy::Search.where("convertable_type = ? AND converted_at > ?", class_name, 1.day.ago)
|
1625
|
-
.order(:convertable_id).distinct.pluck(:convertable_id)
|
1626
|
-
|
1627
|
-
# split into groups
|
1628
|
-
recently_converted_ids.in_groups_of(1000, false) do |ids|
|
1629
|
-
# fetch conversions
|
1630
|
-
conversions =
|
1631
|
-
Searchjoy::Search.where(convertable_id: ids, convertable_type: class_name)
|
1632
|
-
.group(:convertable_id, :query).distinct.count(:user_id)
|
1633
|
-
|
1634
|
-
# group conversions by record
|
1635
|
-
conversions_by_record = {}
|
1636
|
-
conversions.each do |(id, query), count|
|
1637
|
-
(conversions_by_record[id] ||= {})[query] = count
|
1638
|
-
end
|
1639
|
-
|
1640
|
-
# write to cache
|
1641
|
-
conversions_by_record.each do |id, conversions|
|
1642
|
-
Rails.cache.write("search_conversions:#{class_name}:#{id}", conversions)
|
1643
|
-
end
|
1644
|
-
|
1645
|
-
# partial reindex
|
1646
|
-
class_name.constantize.where(id: ids).reindex(:search_conversions)
|
1647
|
-
end
|
1648
|
-
end
|
1649
|
-
end
|
1650
|
-
```
|
1651
|
-
|
1652
|
-
Run the job with:
|
1653
|
-
|
1654
|
-
```ruby
|
1655
|
-
ReindexConversionsJob.perform_later("Product")
|
1694
|
+
Product.reindex(:prices_data)
|
1656
1695
|
```
|
1657
1696
|
|
1658
1697
|
## Advanced
|
@@ -1798,6 +1837,10 @@ To query nested data, use dot notation.
|
|
1798
1837
|
Product.search("san", fields: ["store.city"], where: {"store.zip_code" => 12345})
|
1799
1838
|
```
|
1800
1839
|
|
1840
|
+
## Nearest Neighbors
|
1841
|
+
|
1842
|
+
You can use custom mapping and searching to index vectors and perform k-nearest neighbor search. See the examples for [Elasticsearch](examples/elasticsearch_knn.rb) and [OpenSearch](examples/opensearch_knn.rb).
|
1843
|
+
|
1801
1844
|
## Reference
|
1802
1845
|
|
1803
1846
|
Reindex one record
|
@@ -2036,12 +2079,24 @@ Turn on misspellings after a certain number of characters
|
|
2036
2079
|
Product.search("api", misspellings: {prefix_length: 2}) # api, apt, no ahi
|
2037
2080
|
```
|
2038
2081
|
|
2039
|
-
**Note:** With this option, if the query length is the same as `prefix_length`, misspellings are turned off with Elasticsearch 7 and OpenSearch
|
2082
|
+
**Note:** With this option, if the query length is the same as `prefix_length`, misspellings are turned off with Elasticsearch 7 and OpenSearch 1
|
2040
2083
|
|
2041
2084
|
```ruby
|
2042
2085
|
Product.search("ah", misspellings: {prefix_length: 2}) # ah, no aha
|
2043
2086
|
```
|
2044
2087
|
|
2088
|
+
BigDecimal values are indexed as floats by default so they can be used for boosting. Convert them to strings to keep full precision.
|
2089
|
+
|
2090
|
+
```ruby
|
2091
|
+
class Product < ApplicationRecord
|
2092
|
+
def search_data
|
2093
|
+
{
|
2094
|
+
units: units.to_s("F")
|
2095
|
+
}
|
2096
|
+
end
|
2097
|
+
end
|
2098
|
+
```
|
2099
|
+
|
2045
2100
|
## Gotchas
|
2046
2101
|
|
2047
2102
|
### Consistency
|
data/lib/searchkick/index.rb
CHANGED
@@ -67,7 +67,8 @@ module Searchkick
|
|
67
67
|
index: name,
|
68
68
|
body: {
|
69
69
|
query: {match_all: {}},
|
70
|
-
size: 0
|
70
|
+
size: 0,
|
71
|
+
track_total_hits: true
|
71
72
|
}
|
72
73
|
)
|
73
74
|
|
@@ -418,7 +419,7 @@ module Searchkick
|
|
418
419
|
true
|
419
420
|
end
|
420
421
|
rescue => e
|
421
|
-
if Searchkick.transport_error?(e) && e.message.include?("No handler for type [text]")
|
422
|
+
if Searchkick.transport_error?(e) && (e.message.include?("No handler for type [text]") || e.message.include?("class java.util.ArrayList cannot be cast to class java.util.Map"))
|
422
423
|
raise UnsupportedVersionError
|
423
424
|
end
|
424
425
|
|
@@ -19,7 +19,7 @@ module Searchkick
|
|
19
19
|
mappings = generate_mappings.deep_symbolize_keys.deep_merge(custom_mappings)
|
20
20
|
end
|
21
21
|
|
22
|
-
set_deep_paging(settings) if options[:deep_paging]
|
22
|
+
set_deep_paging(settings) if options[:deep_paging] || options[:max_result_window]
|
23
23
|
|
24
24
|
{
|
25
25
|
settings: settings,
|
@@ -525,7 +525,7 @@ module Searchkick
|
|
525
525
|
def set_deep_paging(settings)
|
526
526
|
if !settings.dig(:index, :max_result_window) && !settings[:"index.max_result_window"]
|
527
527
|
settings[:index] ||= {}
|
528
|
-
settings[:index][:max_result_window] = 1_000_000_000
|
528
|
+
settings[:index][:max_result_window] = options[:max_result_window] || 1_000_000_000
|
529
529
|
end
|
530
530
|
end
|
531
531
|
|
@@ -31,7 +31,7 @@ module Searchkick
|
|
31
31
|
params << "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
|
32
32
|
end
|
33
33
|
|
34
|
-
debug " #{color(name, YELLOW, true)} #{index}#{type ? "/#{type.join(',')}" : ''}/_search#{params.any? ? '?' + params.join('&') : nil} #{payload[:query][:body].to_json}"
|
34
|
+
debug " #{color(name, YELLOW, bold: true)} #{index}#{type ? "/#{type.join(',')}" : ''}/_search#{params.any? ? '?' + params.join('&') : nil} #{payload[:query][:body].to_json}"
|
35
35
|
end
|
36
36
|
|
37
37
|
def request(event)
|
@@ -41,7 +41,7 @@ module Searchkick
|
|
41
41
|
payload = event.payload
|
42
42
|
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
43
43
|
|
44
|
-
debug " #{color(name, YELLOW, true)} #{payload.except(:name).to_json}"
|
44
|
+
debug " #{color(name, YELLOW, bold: true)} #{payload.except(:name).to_json}"
|
45
45
|
end
|
46
46
|
|
47
47
|
def multi_search(event)
|
@@ -51,7 +51,7 @@ module Searchkick
|
|
51
51
|
payload = event.payload
|
52
52
|
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
53
53
|
|
54
|
-
debug " #{color(name, YELLOW, true)} _msearch #{payload[:body]}"
|
54
|
+
debug " #{color(name, YELLOW, bold: true)} _msearch #{payload[:body]}"
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -3,8 +3,15 @@ require "faraday"
|
|
3
3
|
module Searchkick
|
4
4
|
class Middleware < Faraday::Middleware
|
5
5
|
def call(env)
|
6
|
-
|
6
|
+
path = env[:url].path.to_s
|
7
|
+
if path.end_with?("/_search")
|
7
8
|
env[:request][:timeout] = Searchkick.search_timeout
|
9
|
+
elsif path.end_with?("/_msearch")
|
10
|
+
# assume no concurrent searches for timeout for now
|
11
|
+
searches = env[:request_body].count("\n") / 2
|
12
|
+
# do not allow timeout to exceed Searchkick.timeout
|
13
|
+
timeout = [Searchkick.search_timeout * searches, Searchkick.timeout].min
|
14
|
+
env[:request][:timeout] = timeout
|
8
15
|
end
|
9
16
|
@app.call(env)
|
10
17
|
end
|
data/lib/searchkick/model.rb
CHANGED
@@ -5,7 +5,7 @@ module Searchkick
|
|
5
5
|
|
6
6
|
unknown_keywords = options.keys - [:_all, :_type, :batch_size, :callbacks, :case_sensitive, :conversions, :deep_paging, :default_fields,
|
7
7
|
:filterable, :geo_shape, :highlight, :ignore_above, :index_name, :index_prefix, :inheritance, :language,
|
8
|
-
:locations, :mappings, :match, :merge_mappings, :routing, :searchable, :search_synonyms, :settings, :similarity,
|
8
|
+
:locations, :mappings, :match, :max_result_window, :merge_mappings, :routing, :searchable, :search_synonyms, :settings, :similarity,
|
9
9
|
:special_characters, :stem, :stemmer, :stem_conversions, :stem_exclusion, :stemmer_override, :suggest, :synonyms, :text_end,
|
10
10
|
:text_middle, :text_start, :unscope, :word, :word_end, :word_middle, :word_start]
|
11
11
|
raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
|
@@ -66,7 +66,7 @@ module Searchkick
|
|
66
66
|
alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
|
67
67
|
|
68
68
|
def searchkick_index(name: nil)
|
69
|
-
index_name = name || searchkick_index_name
|
69
|
+
index_name = name || searchkick_klass.searchkick_index_name
|
70
70
|
index_name = index_name.call if index_name.respond_to?(:call)
|
71
71
|
index_cache = class_variable_get(:@@searchkick_index_cache)
|
72
72
|
index_cache.fetch(index_name) { Searchkick::Index.new(index_name, searchkick_options) }
|
@@ -88,9 +88,9 @@ module Searchkick
|
|
88
88
|
if options[:index_name]
|
89
89
|
options[:index_name]
|
90
90
|
elsif options[:index_prefix].respond_to?(:call)
|
91
|
-
-> { [options[:index_prefix].call,
|
91
|
+
-> { [options[:index_prefix].call, model_name.plural, Searchkick.env, Searchkick.index_suffix].compact.join("_") }
|
92
92
|
else
|
93
|
-
[options.key?(:index_prefix) ? options[:index_prefix] : Searchkick.index_prefix,
|
93
|
+
[options.key?(:index_prefix) ? options[:index_prefix] : Searchkick.index_prefix, model_name.plural, Searchkick.env, Searchkick.index_suffix].compact.join("_")
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
data/lib/searchkick/query.rb
CHANGED
@@ -199,7 +199,11 @@ module Searchkick
|
|
199
199
|
def handle_error(e)
|
200
200
|
status_code = e.message[1..3].to_i
|
201
201
|
if status_code == 404
|
202
|
-
|
202
|
+
if e.message.include?("No search context found for id")
|
203
|
+
raise MissingIndexError, "No search context found for id"
|
204
|
+
else
|
205
|
+
raise MissingIndexError, "Index missing - run #{reindex_command}"
|
206
|
+
end
|
203
207
|
elsif status_code == 500 && (
|
204
208
|
e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") ||
|
205
209
|
e.message.include?("No query registered for [multi_match]") ||
|
@@ -251,9 +255,15 @@ module Searchkick
|
|
251
255
|
default_limit = searchkick_options[:deep_paging] ? 1_000_000_000 : 10_000
|
252
256
|
per_page = (options[:limit] || options[:per_page] || default_limit).to_i
|
253
257
|
padding = [options[:padding].to_i, 0].max
|
254
|
-
offset = options[:offset] || (page - 1) * per_page + padding
|
258
|
+
offset = (options[:offset] || (page - 1) * per_page + padding).to_i
|
255
259
|
scroll = options[:scroll]
|
256
260
|
|
261
|
+
max_result_window = searchkick_options[:max_result_window]
|
262
|
+
if max_result_window
|
263
|
+
offset = max_result_window if offset > max_result_window
|
264
|
+
per_page = max_result_window - offset if offset + per_page > max_result_window
|
265
|
+
end
|
266
|
+
|
257
267
|
# model and eager loading
|
258
268
|
load = options[:load].nil? ? true : options[:load]
|
259
269
|
|
@@ -363,7 +373,7 @@ module Searchkick
|
|
363
373
|
field_misspellings = misspellings && (!misspellings_fields || misspellings_fields.include?(base_field(field)))
|
364
374
|
|
365
375
|
if field == "_all" || field.end_with?(".analyzed")
|
366
|
-
shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || field_misspellings == false || (!below73? && !track_total_hits?)
|
376
|
+
shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || field_misspellings == false || (!below73? && !track_total_hits?) || match_type == :match_phrase || !below80? || Searchkick.opensearch?
|
367
377
|
qs << shared_options.merge(analyzer: "searchkick_search")
|
368
378
|
|
369
379
|
# searchkick_search and searchkick_search2 are the same for some languages
|
@@ -499,7 +509,7 @@ module Searchkick
|
|
499
509
|
set_highlights(payload, fields) if options[:highlight]
|
500
510
|
|
501
511
|
# timeout shortly after client times out
|
502
|
-
payload[:timeout] ||= "#{Searchkick.search_timeout + 1}
|
512
|
+
payload[:timeout] ||= "#{((Searchkick.search_timeout + 1) * 1000).round}ms"
|
503
513
|
|
504
514
|
# An empty array will cause only the _id and _type for each hit to be returned
|
505
515
|
# https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-source-filtering.html
|
@@ -1160,5 +1170,9 @@ module Searchkick
|
|
1160
1170
|
def below710?
|
1161
1171
|
Searchkick.server_below?("7.10.0")
|
1162
1172
|
end
|
1173
|
+
|
1174
|
+
def below80?
|
1175
|
+
Searchkick.server_below?("8.0.0")
|
1176
|
+
end
|
1163
1177
|
end
|
1164
1178
|
end
|
@@ -10,7 +10,7 @@ module Searchkick
|
|
10
10
|
|
11
11
|
# supports single and multiple ids
|
12
12
|
def push(record_ids)
|
13
|
-
Searchkick.with_redis { |r| r.
|
13
|
+
Searchkick.with_redis { |r| r.call("LPUSH", redis_key, record_ids) }
|
14
14
|
end
|
15
15
|
|
16
16
|
def push_records(records)
|
@@ -34,11 +34,11 @@ module Searchkick
|
|
34
34
|
# TODO use reliable queuing
|
35
35
|
def reserve(limit: 1000)
|
36
36
|
if supports_rpop_with_count?
|
37
|
-
Searchkick.with_redis { |r| r.call("
|
37
|
+
Searchkick.with_redis { |r| r.call("RPOP", redis_key, limit) }.to_a
|
38
38
|
else
|
39
39
|
record_ids = []
|
40
40
|
Searchkick.with_redis do |r|
|
41
|
-
while record_ids.size < limit && (record_id = r.
|
41
|
+
while record_ids.size < limit && (record_id = r.call("RPOP", redis_key))
|
42
42
|
record_ids << record_id
|
43
43
|
end
|
44
44
|
end
|
@@ -47,11 +47,11 @@ module Searchkick
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def clear
|
50
|
-
Searchkick.with_redis { |r| r.
|
50
|
+
Searchkick.with_redis { |r| r.call("DEL", redis_key) }
|
51
51
|
end
|
52
52
|
|
53
53
|
def length
|
54
|
-
Searchkick.with_redis { |r| r.
|
54
|
+
Searchkick.with_redis { |r| r.call("LLEN", redis_key) }
|
55
55
|
end
|
56
56
|
|
57
57
|
private
|
@@ -65,11 +65,16 @@ module Searchkick
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def redis_version
|
68
|
-
@redis_version ||=
|
68
|
+
@redis_version ||=
|
69
|
+
Searchkick.with_redis do |r|
|
70
|
+
info = r.call("INFO")
|
71
|
+
matches = /redis_version:(\S+)/.match(info)
|
72
|
+
Gem::Version.new(matches[1])
|
73
|
+
end
|
69
74
|
end
|
70
75
|
|
71
76
|
def escape(value)
|
72
|
-
value.gsub("|", "||")
|
77
|
+
value.to_s.gsub("|", "||")
|
73
78
|
end
|
74
79
|
end
|
75
80
|
end
|
data/lib/searchkick/relation.rb
CHANGED
@@ -8,6 +8,9 @@ module Searchkick
|
|
8
8
|
delegate :body, :params, to: :query
|
9
9
|
delegate_missing_to :private_execute
|
10
10
|
|
11
|
+
attr_reader :model
|
12
|
+
alias_method :klass, :model
|
13
|
+
|
11
14
|
def initialize(model, term = "*", **options)
|
12
15
|
@model = model
|
13
16
|
@term = term
|
@@ -26,20 +29,22 @@ module Searchkick
|
|
26
29
|
|
27
30
|
def execute
|
28
31
|
Searchkick.warn("The execute method is no longer needed")
|
29
|
-
|
30
|
-
self
|
32
|
+
load
|
31
33
|
end
|
32
34
|
|
35
|
+
# experimental
|
33
36
|
def limit(value)
|
34
37
|
clone.limit!(value)
|
35
38
|
end
|
36
39
|
|
40
|
+
# experimental
|
37
41
|
def limit!(value)
|
38
42
|
check_loaded
|
39
43
|
@options[:limit] = value
|
40
44
|
self
|
41
45
|
end
|
42
46
|
|
47
|
+
# experimental
|
43
48
|
def offset(value = NO_DEFAULT_VALUE)
|
44
49
|
# TODO remove in Searchkick 6
|
45
50
|
if value == NO_DEFAULT_VALUE
|
@@ -49,22 +54,26 @@ module Searchkick
|
|
49
54
|
end
|
50
55
|
end
|
51
56
|
|
57
|
+
# experimental
|
52
58
|
def offset!(value)
|
53
59
|
check_loaded
|
54
60
|
@options[:offset] = value
|
55
61
|
self
|
56
62
|
end
|
57
63
|
|
64
|
+
# experimental
|
58
65
|
def page(value)
|
59
66
|
clone.page!(value)
|
60
67
|
end
|
61
68
|
|
69
|
+
# experimental
|
62
70
|
def page!(value)
|
63
71
|
check_loaded
|
64
72
|
@options[:page] = value
|
65
73
|
self
|
66
74
|
end
|
67
75
|
|
76
|
+
# experimental
|
68
77
|
def per_page(value = NO_DEFAULT_VALUE)
|
69
78
|
# TODO remove in Searchkick 6
|
70
79
|
if value == NO_DEFAULT_VALUE
|
@@ -74,24 +83,139 @@ module Searchkick
|
|
74
83
|
end
|
75
84
|
end
|
76
85
|
|
86
|
+
# experimental
|
77
87
|
def per_page!(value)
|
78
88
|
check_loaded
|
79
89
|
@options[:per_page] = value
|
80
90
|
self
|
81
91
|
end
|
82
92
|
|
93
|
+
# experimental
|
94
|
+
def where(value = NO_DEFAULT_VALUE)
|
95
|
+
if value == NO_DEFAULT_VALUE
|
96
|
+
Where.new(self)
|
97
|
+
else
|
98
|
+
clone.where!(value)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# experimental
|
103
|
+
def where!(value)
|
104
|
+
check_loaded
|
105
|
+
if @options[:where]
|
106
|
+
@options[:where] = {_and: [@options[:where], ensure_permitted(value)]}
|
107
|
+
else
|
108
|
+
@options[:where] = ensure_permitted(value)
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# experimental
|
114
|
+
def rewhere(value)
|
115
|
+
clone.rewhere!(value)
|
116
|
+
end
|
117
|
+
|
118
|
+
# experimental
|
119
|
+
def rewhere!(value)
|
120
|
+
check_loaded
|
121
|
+
@options[:where] = ensure_permitted(value)
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
# experimental
|
126
|
+
def order(*values)
|
127
|
+
clone.order!(*values)
|
128
|
+
end
|
129
|
+
|
130
|
+
# experimental
|
131
|
+
def order!(*values)
|
132
|
+
values = values.first if values.size == 1 && values.first.is_a?(Array)
|
133
|
+
check_loaded
|
134
|
+
(@options[:order] ||= []).concat(values)
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
# experimental
|
139
|
+
def reorder(*values)
|
140
|
+
clone.reorder!(*values)
|
141
|
+
end
|
142
|
+
|
143
|
+
# experimental
|
144
|
+
def reorder!(*values)
|
145
|
+
check_loaded
|
146
|
+
@options[:order] = values
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
# experimental
|
151
|
+
def select(*values, &block)
|
152
|
+
if block_given?
|
153
|
+
private_execute.select(*values, &block)
|
154
|
+
else
|
155
|
+
clone.select!(*values)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# experimental
|
160
|
+
def select!(*values)
|
161
|
+
check_loaded
|
162
|
+
(@options[:select] ||= []).concat(values)
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
# experimental
|
167
|
+
def reselect(*values)
|
168
|
+
clone.reselect!(*values)
|
169
|
+
end
|
170
|
+
|
171
|
+
# experimental
|
172
|
+
def reselect!(*values)
|
173
|
+
check_loaded
|
174
|
+
@options[:select] = values
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
# experimental
|
179
|
+
def includes(*values)
|
180
|
+
clone.includes!(*values)
|
181
|
+
end
|
182
|
+
|
183
|
+
# experimental
|
184
|
+
def includes!(*values)
|
185
|
+
check_loaded
|
186
|
+
(@options[:includes] ||= []).concat(values)
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
# experimental
|
83
191
|
def only(*keys)
|
84
192
|
Relation.new(@model, @term, **@options.slice(*keys))
|
85
193
|
end
|
86
194
|
|
195
|
+
# experimental
|
87
196
|
def except(*keys)
|
88
197
|
Relation.new(@model, @term, **@options.except(*keys))
|
89
198
|
end
|
90
199
|
|
200
|
+
# experimental
|
201
|
+
def load
|
202
|
+
private_execute
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
91
206
|
def loaded?
|
92
207
|
!@execute.nil?
|
93
208
|
end
|
94
209
|
|
210
|
+
def respond_to_missing?(method_name, include_all)
|
211
|
+
Results.new(nil, nil, nil).respond_to?(method_name, include_all) || super
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO uncomment in 6.0
|
215
|
+
# def to_yaml
|
216
|
+
# private_execute.to_a.to_yaml
|
217
|
+
# end
|
218
|
+
|
95
219
|
private
|
96
220
|
|
97
221
|
def private_execute
|
@@ -108,5 +232,16 @@ module Searchkick
|
|
108
232
|
# reset query since options will change
|
109
233
|
@query = nil
|
110
234
|
end
|
235
|
+
|
236
|
+
# provides *very* basic protection from unfiltered parameters
|
237
|
+
# this is not meant to be comprehensive and may be expanded in the future
|
238
|
+
def ensure_permitted(obj)
|
239
|
+
obj.to_h
|
240
|
+
end
|
241
|
+
|
242
|
+
def initialize_copy(other)
|
243
|
+
super
|
244
|
+
@execute = nil
|
245
|
+
end
|
111
246
|
end
|
112
247
|
end
|
@@ -14,12 +14,17 @@ module Searchkick
|
|
14
14
|
relation = relation.search_import
|
15
15
|
end
|
16
16
|
|
17
|
-
# remove unneeded loading for async
|
18
|
-
if mode == :async
|
17
|
+
# remove unneeded loading for async and queue
|
18
|
+
if mode == :async || mode == :queue
|
19
19
|
if relation.respond_to?(:primary_key)
|
20
|
-
relation = relation.
|
20
|
+
relation = relation.except(:includes, :preload)
|
21
|
+
unless mode == :queue && relation.klass.method_defined?(:search_routing)
|
22
|
+
relation = relation.except(:select).select(relation.primary_key)
|
23
|
+
end
|
21
24
|
elsif relation.respond_to?(:only)
|
22
|
-
|
25
|
+
unless mode == :queue && relation.klass.method_defined?(:search_routing)
|
26
|
+
relation = relation.only(:_id)
|
27
|
+
end
|
23
28
|
end
|
24
29
|
end
|
25
30
|
|
@@ -42,11 +47,11 @@ module Searchkick
|
|
42
47
|
end
|
43
48
|
|
44
49
|
def batches_left
|
45
|
-
Searchkick.with_redis { |r| r.
|
50
|
+
Searchkick.with_redis { |r| r.call("SCARD", batches_key) }
|
46
51
|
end
|
47
52
|
|
48
53
|
def batch_completed(batch_id)
|
49
|
-
Searchkick.with_redis { |r| r.
|
54
|
+
Searchkick.with_redis { |r| r.call("SREM", batches_key, [batch_id]) }
|
50
55
|
end
|
51
56
|
|
52
57
|
private
|
@@ -134,7 +139,7 @@ module Searchkick
|
|
134
139
|
end
|
135
140
|
|
136
141
|
def batch_job(class_name, batch_id, record_ids)
|
137
|
-
Searchkick.with_redis { |r| r.
|
142
|
+
Searchkick.with_redis { |r| r.call("SADD", batches_key, [batch_id]) }
|
138
143
|
Searchkick::BulkReindexJob.perform_later(
|
139
144
|
class_name: class_name,
|
140
145
|
index_name: index.name,
|
data/lib/searchkick/results.rb
CHANGED
@@ -3,6 +3,7 @@ module Searchkick
|
|
3
3
|
include Enumerable
|
4
4
|
extend Forwardable
|
5
5
|
|
6
|
+
# TODO remove klass and options in 6.0
|
6
7
|
attr_reader :klass, :response, :options
|
7
8
|
|
8
9
|
def_delegators :results, :each, :any?, :empty?, :size, :length, :slice, :[], :to_ary
|
@@ -13,6 +14,7 @@ module Searchkick
|
|
13
14
|
@options = options
|
14
15
|
end
|
15
16
|
|
17
|
+
# TODO make private in 6.0
|
16
18
|
def results
|
17
19
|
@results ||= with_hit.map(&:first)
|
18
20
|
end
|
@@ -302,7 +304,7 @@ module Searchkick
|
|
302
304
|
def build_hits
|
303
305
|
@build_hits ||= begin
|
304
306
|
if missing_records.any?
|
305
|
-
Searchkick.warn("Records in search index do not exist in database: #{missing_records.map { |v| v[:id] }.join(", ")}")
|
307
|
+
Searchkick.warn("Records in search index do not exist in database: #{missing_records.map { |v| "#{Array(v[:model]).map(&:model_name).sort.join("/")} #{v[:id]}" }.join(", ")}")
|
306
308
|
end
|
307
309
|
with_hit_and_missing_records[0]
|
308
310
|
end
|
data/lib/searchkick/version.rb
CHANGED
data/lib/searchkick.rb
CHANGED
@@ -10,26 +10,27 @@ require "hashie"
|
|
10
10
|
require "forwardable"
|
11
11
|
|
12
12
|
# modules
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
13
|
+
require_relative "searchkick/controller_runtime"
|
14
|
+
require_relative "searchkick/index"
|
15
|
+
require_relative "searchkick/index_cache"
|
16
|
+
require_relative "searchkick/index_options"
|
17
|
+
require_relative "searchkick/indexer"
|
18
|
+
require_relative "searchkick/hash_wrapper"
|
19
|
+
require_relative "searchkick/log_subscriber"
|
20
|
+
require_relative "searchkick/model"
|
21
|
+
require_relative "searchkick/multi_search"
|
22
|
+
require_relative "searchkick/query"
|
23
|
+
require_relative "searchkick/reindex_queue"
|
24
|
+
require_relative "searchkick/record_data"
|
25
|
+
require_relative "searchkick/record_indexer"
|
26
|
+
require_relative "searchkick/relation"
|
27
|
+
require_relative "searchkick/relation_indexer"
|
28
|
+
require_relative "searchkick/results"
|
29
|
+
require_relative "searchkick/version"
|
30
|
+
require_relative "searchkick/where"
|
30
31
|
|
31
32
|
# integrations
|
32
|
-
|
33
|
+
require_relative "searchkick/railtie" if defined?(Rails)
|
33
34
|
|
34
35
|
module Searchkick
|
35
36
|
# requires faraday
|
@@ -134,8 +135,9 @@ module Searchkick
|
|
134
135
|
@opensearch
|
135
136
|
end
|
136
137
|
|
137
|
-
|
138
|
-
|
138
|
+
# TODO always check true version in Searchkick 6
|
139
|
+
def self.server_below?(version, true_version = false)
|
140
|
+
server_version = !true_version && opensearch? ? "7.10.2" : self.server_version
|
139
141
|
Gem::Version.new(server_version.split("-")[0]) < Gem::Version.new(version.split("-")[0])
|
140
142
|
end
|
141
143
|
|
@@ -283,7 +285,7 @@ module Searchkick
|
|
283
285
|
relation
|
284
286
|
end
|
285
287
|
|
286
|
-
#
|
288
|
+
# public (for reindexing conversions)
|
287
289
|
def self.load_model(class_name, allow_child: false)
|
288
290
|
model = class_name.safe_constantize
|
289
291
|
raise Error, "Could not find class: #{class_name}" unless model
|
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.3.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:
|
11
|
+
date: 2023-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: hashie
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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:
|
@@ -84,14 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
85
|
requirements:
|
85
86
|
- - ">="
|
86
87
|
- !ruby/object:Gem::Version
|
87
|
-
version: '
|
88
|
+
version: '3'
|
88
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
90
|
requirements:
|
90
91
|
- - ">="
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
|
-
rubygems_version: 3.
|
95
|
+
rubygems_version: 3.4.10
|
95
96
|
signing_key:
|
96
97
|
specification_version: 4
|
97
98
|
summary: Intelligent search made easy with Rails and Elasticsearch or OpenSearch
|