search_flip 3.0.0.beta2 → 3.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -0
- data/README.md +8 -0
- data/lib/search_flip/aggregatable.rb +8 -1
- data/lib/search_flip/aggregation.rb +27 -5
- data/lib/search_flip/criteria.rb +9 -235
- data/lib/search_flip/customable.rb +34 -0
- data/lib/search_flip/explainable.rb +28 -0
- data/lib/search_flip/highlightable.rb +49 -0
- data/lib/search_flip/paginatable.rb +93 -0
- data/lib/search_flip/sortable.rb +69 -0
- data/lib/search_flip/sourceable.rb +30 -0
- data/lib/search_flip/version.rb +1 -1
- data/lib/search_flip.rb +6 -0
- data/search_flip.gemspec +1 -1
- data/spec/search_flip/aggregation_spec.rb +178 -24
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 417bf711946ace4812485ea2efdfefdeb36414e7e8e8d8ed24b10b4807050be9
|
4
|
+
data.tar.gz: c8a9f7392d77f15c4714c40756c032a2bb3bc12c363a9e956a6b9e184c8e4c17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c62c6d3bc17ec18da5b304dfbddbafc29e35013bcc6b0b906858380b01a263b2e534986cd41f9e90d13de99820f9cbf11a173802910737bf33d8ae5653230af1
|
7
|
+
data.tar.gz: 3b6ee6d16086e451f6f6e6d7c718d6a30c30115d91c6ba9e69113faa19b9abc35d04c8c179dc9ca5ac49418877c0c5828435d71c1dab7b71d4afe2e31da88762
|
data/CHANGELOG.md
CHANGED
@@ -21,6 +21,8 @@
|
|
21
21
|
* `Connection#freeze_index`, `Connection#unfreeze_index`, `Index#freeze_index`
|
22
22
|
and `Index#unfreeze_index` added
|
23
23
|
* Added `SearchFlip::Result.from_hit`
|
24
|
+
* Added support for `source`, `sort`, `page`, `per`, `paginate`, `explain`, and
|
25
|
+
`highlight` to aggregations
|
24
26
|
|
25
27
|
## v2.3.1
|
26
28
|
|
data/README.md
CHANGED
@@ -505,6 +505,14 @@ end
|
|
505
505
|
query.aggregations(:average_price).average_price.value
|
506
506
|
```
|
507
507
|
|
508
|
+
Even various criteria for top hits aggregations can be specified elegantly:
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
query = ProductIndex.aggregate(sponsored: { top_hits: {} }) do |aggregation|
|
512
|
+
aggregation.sort(:rank).highlight(:title).source([:id, :title])
|
513
|
+
end
|
514
|
+
```
|
515
|
+
|
508
516
|
Checkout [Aggregatable](http://www.rubydoc.info/github/mrkamel/search_flip/SearchFlip/Aggregatable)
|
509
517
|
as well as [Aggregation](http://www.rubydoc.info/github/mrkamel/search_flip/SearchFlip/Aggregation)
|
510
518
|
for a complete API reference.
|
@@ -57,7 +57,14 @@ module SearchFlip
|
|
57
57
|
if block
|
58
58
|
aggregation = yield(SearchFlip::Aggregation.new(target: target))
|
59
59
|
|
60
|
-
field_or_hash.is_a?(Hash)
|
60
|
+
if field_or_hash.is_a?(Hash)
|
61
|
+
value = field_or_hash.values.first
|
62
|
+
value = value.values.first if value.is_a?(Hash) && !value.empty?
|
63
|
+
|
64
|
+
value.merge!(aggregation.to_hash)
|
65
|
+
else
|
66
|
+
hash[field_or_hash].merge!(aggregation.to_hash)
|
67
|
+
end
|
61
68
|
end
|
62
69
|
|
63
70
|
criteria.aggregation_values = (aggregation_values || {}).merge(hash)
|
@@ -6,6 +6,12 @@ module SearchFlip
|
|
6
6
|
class Aggregation
|
7
7
|
include Filterable
|
8
8
|
include Aggregatable
|
9
|
+
include Paginatable
|
10
|
+
include Highlightable
|
11
|
+
include Explainable
|
12
|
+
include Sourceable
|
13
|
+
include Sortable
|
14
|
+
include Customable
|
9
15
|
|
10
16
|
attr_reader :target
|
11
17
|
|
@@ -40,6 +46,15 @@ module SearchFlip
|
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
49
|
+
res.update(from: offset_value_with_default, size: limit_value_with_default) if offset_value || limit_value
|
50
|
+
|
51
|
+
res[:explain] = explain_value unless explain_value.nil?
|
52
|
+
res[:highlight] = highlight_values if highlight_values
|
53
|
+
res[:sort] = sort_values if sort_values
|
54
|
+
res[:_source] = source_value unless source_value.nil?
|
55
|
+
|
56
|
+
res.update(custom_value) if custom_value
|
57
|
+
|
43
58
|
res
|
44
59
|
end
|
45
60
|
|
@@ -56,11 +71,9 @@ module SearchFlip
|
|
56
71
|
|
57
72
|
fresh.tap do |aggregation|
|
58
73
|
unsupported_methods = [
|
59
|
-
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :
|
60
|
-
:
|
61
|
-
:
|
62
|
-
:post_must_not_values, :post_filter_values, :preference_value,
|
63
|
-
:search_type_value, :routing_value
|
74
|
+
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :scroll_args,
|
75
|
+
:suggest_values, :includes_values, :preload_values, :eager_load_values, :post_must_values,
|
76
|
+
:post_must_not_values, :post_filter_values, :preference_value, :search_type_value, :routing_value
|
64
77
|
]
|
65
78
|
|
66
79
|
unsupported_methods.each do |unsupported_method|
|
@@ -69,10 +82,19 @@ module SearchFlip
|
|
69
82
|
end
|
70
83
|
end
|
71
84
|
|
85
|
+
aggregation.source_value = other.source_value if other.source_value
|
86
|
+
aggregation.offset_value = other.offset_value if other.offset_value
|
87
|
+
aggregation.limit_value = other.limit_value if other.limit_value
|
88
|
+
aggregation.scroll_args = other.scroll_args if other.scroll_args
|
89
|
+
aggregation.explain_value = other.explain_value unless other.explain_value.nil?
|
90
|
+
|
91
|
+
aggregation.sort_values = (aggregation.sort_values || []) + other.sort_values if other.sort_values
|
72
92
|
aggregation.must_values = (aggregation.must_values || []) + other.must_values if other.must_values
|
73
93
|
aggregation.must_not_values = (aggregation.must_not_values || []) + other.must_not_values if other.must_not_values
|
74
94
|
aggregation.filter_values = (aggregation.filter_values || []) + other.filter_values if other.filter_values
|
75
95
|
|
96
|
+
aggregation.highlight_values = (aggregation.highlight_values || {}).merge(other.highlight_values) if other.highlight_values
|
97
|
+
aggregation.custom_value = (aggregation.custom_value || {}).merge(other.custom_value) if other.custom_value
|
76
98
|
aggregation.aggregation_values = (aggregation.aggregation_values || {}).merge(other.aggregation_values) if other.aggregation_values
|
77
99
|
end
|
78
100
|
end
|
data/lib/search_flip/criteria.rb
CHANGED
@@ -13,15 +13,20 @@ module SearchFlip
|
|
13
13
|
# CommentIndex.sort("_doc").find_each { |comment| "..." }
|
14
14
|
|
15
15
|
class Criteria
|
16
|
+
include Sortable
|
17
|
+
include Sourceable
|
18
|
+
include Highlightable
|
19
|
+
include Explainable
|
20
|
+
include Paginatable
|
21
|
+
include Customable
|
16
22
|
include Filterable
|
17
23
|
include PostFilterable
|
18
24
|
include Aggregatable
|
19
25
|
extend Forwardable
|
20
26
|
|
21
|
-
attr_accessor :target, :profile_value, :source_value, :
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:routing_value, :track_total_hits_value, :explain_value
|
27
|
+
attr_accessor :target, :profile_value, :source_value, :suggest_values, :includes_values,
|
28
|
+
:eager_load_values, :preload_values, :failsafe_value, :scroll_args, :terminate_after_value,
|
29
|
+
:timeout_value, :preference_value, :search_type_value, :routing_value, :track_total_hits_value
|
25
30
|
|
26
31
|
# Creates a new criteria while merging the attributes (constraints,
|
27
32
|
# settings, etc) of the current criteria with the attributes of another one
|
@@ -71,22 +76,6 @@ module SearchFlip
|
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
74
|
-
# Specifies whether or not to enable explanation for each hit on how
|
75
|
-
# its score was computed.
|
76
|
-
#
|
77
|
-
# @example
|
78
|
-
# CommentIndex.explain(true)
|
79
|
-
#
|
80
|
-
# @param value [Boolean] The value for explain
|
81
|
-
#
|
82
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
83
|
-
|
84
|
-
def explain(value)
|
85
|
-
fresh.tap do |criteria|
|
86
|
-
criteria.explain_value = value
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
79
|
# Specifies if or how many hits should be counted/tracked. Check out the
|
91
80
|
# elasticsearch docs for futher details.
|
92
81
|
#
|
@@ -271,43 +260,6 @@ module SearchFlip
|
|
271
260
|
res
|
272
261
|
end
|
273
262
|
|
274
|
-
# Adds highlighting of the given fields to the request.
|
275
|
-
#
|
276
|
-
# @example
|
277
|
-
# CommentIndex.highlight([:title, :message])
|
278
|
-
# CommentIndex.highlight(:title).highlight(:description)
|
279
|
-
# CommentIndex.highlight(:title, require_field_match: false)
|
280
|
-
# CommentIndex.highlight(title: { type: "fvh" })
|
281
|
-
#
|
282
|
-
# @example
|
283
|
-
# query = CommentIndex.highlight(:title).search("hello")
|
284
|
-
# query.results[0].highlight.title # => "<em>hello</em> world"
|
285
|
-
#
|
286
|
-
# @param fields [Hash, Array, String, Symbol] The fields to highligt.
|
287
|
-
# Supports raw Elasticsearch values by passing a Hash.
|
288
|
-
#
|
289
|
-
# @param options [Hash] Extra highlighting options. Check out the Elasticsearch
|
290
|
-
# docs for further details.
|
291
|
-
#
|
292
|
-
# @return [SearchFlip::Criteria] A new criteria including the highlighting
|
293
|
-
|
294
|
-
def highlight(fields, options = {})
|
295
|
-
fresh.tap do |criteria|
|
296
|
-
criteria.highlight_values = (criteria.highlight_values || {}).merge(options)
|
297
|
-
|
298
|
-
hash =
|
299
|
-
if fields.is_a?(Hash)
|
300
|
-
fields
|
301
|
-
elsif fields.is_a?(Array)
|
302
|
-
fields.each_with_object({}) { |field, h| h[field] = {} }
|
303
|
-
else
|
304
|
-
{ fields => {} }
|
305
|
-
end
|
306
|
-
|
307
|
-
criteria.highlight_values[:fields] = (criteria.highlight_values[:fields] || {}).merge(hash)
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
263
|
# Adds a suggestion section with the given name to the request.
|
312
264
|
#
|
313
265
|
# @example
|
@@ -397,24 +349,6 @@ module SearchFlip
|
|
397
349
|
true
|
398
350
|
end
|
399
351
|
|
400
|
-
# Use to specify which fields of the source document you want Elasticsearch
|
401
|
-
# to return for each matching result.
|
402
|
-
#
|
403
|
-
# @example
|
404
|
-
# CommentIndex.source([:id, :message]).search("hello world")
|
405
|
-
# CommentIndex.source(exclude: "description")
|
406
|
-
# CommentIndex.source(false)
|
407
|
-
#
|
408
|
-
# @param value Pass any allowed value to restrict the returned source
|
409
|
-
#
|
410
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
411
|
-
|
412
|
-
def source(value)
|
413
|
-
fresh.tap do |criteria|
|
414
|
-
criteria.source_value = value
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
352
|
# Specify associations of the target model you want to include via
|
419
353
|
# ActiveRecord's or other ORM's mechanisms when records get fetched from
|
420
354
|
# the database.
|
@@ -472,166 +406,6 @@ module SearchFlip
|
|
472
406
|
end
|
473
407
|
end
|
474
408
|
|
475
|
-
# Specify the sort order you want Elasticsearch to use for sorting the
|
476
|
-
# results. When you call this multiple times, the sort orders are appended
|
477
|
-
# to the already existing ones. The sort arguments get passed to
|
478
|
-
# Elasticsearch without modifications, such that you can use sort by
|
479
|
-
# script, etc here as well.
|
480
|
-
#
|
481
|
-
# @example Default usage
|
482
|
-
# CommentIndex.sort(:user_id, :id)
|
483
|
-
#
|
484
|
-
# # Same as
|
485
|
-
#
|
486
|
-
# CommentIndex.sort(:user_id).sort(:id)
|
487
|
-
#
|
488
|
-
# @example Default hash usage
|
489
|
-
# CommentIndex.sort(user_id: "asc").sort(id: "desc")
|
490
|
-
#
|
491
|
-
# # Same as
|
492
|
-
#
|
493
|
-
# CommentIndex.sort({ user_id: "asc" }, { id: "desc" })
|
494
|
-
#
|
495
|
-
# @example Sort by native script
|
496
|
-
# CommentIndex.sort("_script" => "sort_script", lang: "native", order: "asc", type: "number")
|
497
|
-
#
|
498
|
-
# @param args The sort values that get passed to Elasticsearch
|
499
|
-
#
|
500
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
501
|
-
|
502
|
-
def sort(*args)
|
503
|
-
fresh.tap do |criteria|
|
504
|
-
criteria.sort_values = (sort_values || []) + args
|
505
|
-
end
|
506
|
-
end
|
507
|
-
|
508
|
-
alias_method :order, :sort
|
509
|
-
|
510
|
-
# Specify the sort order you want Elasticsearch to use for sorting the
|
511
|
-
# results with already existing sort orders being removed.
|
512
|
-
#
|
513
|
-
# @example
|
514
|
-
# CommentIndex.sort(user_id: "asc").resort(id: "desc")
|
515
|
-
#
|
516
|
-
# # Same as
|
517
|
-
#
|
518
|
-
# CommentIndex.sort(id: "desc")
|
519
|
-
#
|
520
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
521
|
-
#
|
522
|
-
# @see #sort See #sort for more details
|
523
|
-
|
524
|
-
def resort(*args)
|
525
|
-
fresh.tap do |criteria|
|
526
|
-
criteria.sort_values = args
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
alias_method :reorder, :resort
|
531
|
-
|
532
|
-
# Adds a fully custom field/section to the request, such that upcoming or
|
533
|
-
# minor Elasticsearch features as well as other custom requirements can be
|
534
|
-
# used without having yet specialized criteria methods.
|
535
|
-
#
|
536
|
-
# @note Use with caution, because using #custom will potentiall override
|
537
|
-
# other sections like +aggregations+, +query+, +sort+, etc if you use the
|
538
|
-
# the same section names.
|
539
|
-
#
|
540
|
-
# @example
|
541
|
-
# CommentIndex.custom(section: { argument: "value" }).request
|
542
|
-
# => {:section=>{:argument=>"value"},...}
|
543
|
-
#
|
544
|
-
# @param hash [Hash] The custom section that is added to the request
|
545
|
-
#
|
546
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
547
|
-
|
548
|
-
def custom(hash)
|
549
|
-
fresh.tap do |criteria|
|
550
|
-
criteria.custom_value = (custom_value || {}).merge(hash)
|
551
|
-
end
|
552
|
-
end
|
553
|
-
|
554
|
-
# Sets the request offset, ie SearchFlip's from parameter that is used
|
555
|
-
# to skip results in the result set from being returned.
|
556
|
-
#
|
557
|
-
# @example
|
558
|
-
# CommentIndex.offset(100)
|
559
|
-
#
|
560
|
-
# @param value [Fixnum] The offset value, ie the number of results that are
|
561
|
-
# skipped in the result set
|
562
|
-
#
|
563
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
564
|
-
|
565
|
-
def offset(value)
|
566
|
-
fresh.tap do |criteria|
|
567
|
-
criteria.offset_value = value.to_i
|
568
|
-
end
|
569
|
-
end
|
570
|
-
|
571
|
-
# @api private
|
572
|
-
#
|
573
|
-
# Returns the offset value or, if not yet set, the default limit value (0).
|
574
|
-
#
|
575
|
-
# @return [Fixnum] The offset value
|
576
|
-
|
577
|
-
def offset_value_with_default
|
578
|
-
(offset_value || 0).to_i
|
579
|
-
end
|
580
|
-
|
581
|
-
# Sets the request limit, ie Elasticsearch's size parameter that is used
|
582
|
-
# to restrict the results that get returned.
|
583
|
-
#
|
584
|
-
# @example
|
585
|
-
# CommentIndex.limit(100)
|
586
|
-
#
|
587
|
-
# @param value [Fixnum] The limit value, ie the max number of results that
|
588
|
-
# should be returned
|
589
|
-
#
|
590
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
591
|
-
|
592
|
-
def limit(value)
|
593
|
-
fresh.tap do |criteria|
|
594
|
-
criteria.limit_value = value.to_i
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
# @api private
|
599
|
-
#
|
600
|
-
# Returns the limit value or, if not yet set, the default limit value (30).
|
601
|
-
#
|
602
|
-
# @return [Fixnum] The limit value
|
603
|
-
|
604
|
-
def limit_value_with_default
|
605
|
-
(limit_value || 30).to_i
|
606
|
-
end
|
607
|
-
|
608
|
-
# Sets pagination parameters for the criteria by using offset and limit,
|
609
|
-
# ie Elasticsearch's from and size parameters.
|
610
|
-
#
|
611
|
-
# @example
|
612
|
-
# CommentIndex.paginate(page: 3)
|
613
|
-
# CommentIndex.paginate(page: 5, per_page: 60)
|
614
|
-
#
|
615
|
-
# @param page [#to_i] The current page
|
616
|
-
# @param per_page [#to_i] The number of results per page
|
617
|
-
#
|
618
|
-
# @return [SearchFlip::Criteria] A newly created extended criteria
|
619
|
-
|
620
|
-
def paginate(page: 1, per_page: 30)
|
621
|
-
page = [page.to_i, 1].max
|
622
|
-
per_page = per_page.to_i
|
623
|
-
|
624
|
-
offset((page - 1) * per_page).limit(per_page)
|
625
|
-
end
|
626
|
-
|
627
|
-
def page(value)
|
628
|
-
paginate(page: value, per_page: limit_value_with_default)
|
629
|
-
end
|
630
|
-
|
631
|
-
def per(value)
|
632
|
-
paginate(page: 1 + (offset_value_with_default / limit_value_with_default), per_page: value)
|
633
|
-
end
|
634
|
-
|
635
409
|
# Fetches the records specified by the criteria in batches using the
|
636
410
|
# ElasicSearch scroll API and yields each batch. The batch size and scroll
|
637
411
|
# API timeout can be specified. Check out the Elasticsearch docs for
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SearchFlip
|
2
|
+
# The SearchFlip::Sortable mixin provides the chainable #custom method to
|
3
|
+
# add arbitrary sections to the elasticsearch request
|
4
|
+
|
5
|
+
module Customable
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :custom_value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adds a fully custom field/section to the request, such that upcoming or
|
13
|
+
# minor Elasticsearch features as well as other custom requirements can be
|
14
|
+
# used without having yet specialized criteria methods.
|
15
|
+
#
|
16
|
+
# @note Use with caution, because using #custom will potentiall override
|
17
|
+
# other sections like +aggregations+, +query+, +sort+, etc if you use the
|
18
|
+
# the same section names.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# CommentIndex.custom(section: { argument: "value" }).request
|
22
|
+
# => {:section=>{:argument=>"value"},...}
|
23
|
+
#
|
24
|
+
# @param hash [Hash] The custom section that is added to the request
|
25
|
+
#
|
26
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
27
|
+
|
28
|
+
def custom(hash)
|
29
|
+
fresh.tap do |criteria|
|
30
|
+
criteria.custom_value = (custom_value || {}).merge(hash)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SearchFlip
|
2
|
+
# The SearchFlip::Sortable mixin provides the chainable #explain method to
|
3
|
+
# control elasticsearch query explanations
|
4
|
+
|
5
|
+
module Explainable
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :explain_value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Specifies whether or not to enable explanation for each hit on how
|
13
|
+
# its score was computed.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# CommentIndex.explain(true)
|
17
|
+
#
|
18
|
+
# @param value [Boolean] The value for explain
|
19
|
+
#
|
20
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
21
|
+
|
22
|
+
def explain(value)
|
23
|
+
fresh.tap do |criteria|
|
24
|
+
criteria.explain_value = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module SearchFlip
|
2
|
+
# The SearchFlip::Sortable mixin provides the chainable #highlight method to
|
3
|
+
# use elasticsearch highlighting
|
4
|
+
|
5
|
+
module Highlightable
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :highlight_values
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adds highlighting of the given fields to the request.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# CommentIndex.highlight([:title, :message])
|
16
|
+
# CommentIndex.highlight(:title).highlight(:description)
|
17
|
+
# CommentIndex.highlight(:title, require_field_match: false)
|
18
|
+
# CommentIndex.highlight(title: { type: "fvh" })
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# query = CommentIndex.highlight(:title).search("hello")
|
22
|
+
# query.results[0].highlight.title # => "<em>hello</em> world"
|
23
|
+
#
|
24
|
+
# @param fields [Hash, Array, String, Symbol] The fields to highligt.
|
25
|
+
# Supports raw Elasticsearch values by passing a Hash.
|
26
|
+
#
|
27
|
+
# @param options [Hash] Extra highlighting options. Check out the Elasticsearch
|
28
|
+
# docs for further details.
|
29
|
+
#
|
30
|
+
# @return [SearchFlip::Criteria] A new criteria including the highlighting
|
31
|
+
|
32
|
+
def highlight(fields, options = {})
|
33
|
+
fresh.tap do |criteria|
|
34
|
+
criteria.highlight_values = (criteria.highlight_values || {}).merge(options)
|
35
|
+
|
36
|
+
hash =
|
37
|
+
if fields.is_a?(Hash)
|
38
|
+
fields
|
39
|
+
elsif fields.is_a?(Array)
|
40
|
+
fields.each_with_object({}) { |field, h| h[field] = {} }
|
41
|
+
else
|
42
|
+
{ fields => {} }
|
43
|
+
end
|
44
|
+
|
45
|
+
criteria.highlight_values[:fields] = (criteria.highlight_values[:fields] || {}).merge(hash)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module SearchFlip
|
2
|
+
# The SearchFlip::Paginatable mixin provides chainable methods to allow
|
3
|
+
# paginating the search results
|
4
|
+
|
5
|
+
module Paginatable
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :offset_value, :limit_value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the request offset, ie SearchFlip's from parameter that is used
|
13
|
+
# to skip results in the result set from being returned.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# CommentIndex.offset(100)
|
17
|
+
#
|
18
|
+
# @param value [Fixnum] The offset value, ie the number of results that are
|
19
|
+
# skipped in the result set
|
20
|
+
#
|
21
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
22
|
+
|
23
|
+
def offset(value)
|
24
|
+
fresh.tap do |criteria|
|
25
|
+
criteria.offset_value = value.to_i
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
#
|
31
|
+
# Returns the offset value or, if not yet set, the default limit value (0).
|
32
|
+
#
|
33
|
+
# @return [Fixnum] The offset value
|
34
|
+
|
35
|
+
def offset_value_with_default
|
36
|
+
(offset_value || 0).to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sets the request limit, ie Elasticsearch's size parameter that is used
|
40
|
+
# to restrict the results that get returned.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# CommentIndex.limit(100)
|
44
|
+
#
|
45
|
+
# @param value [Fixnum] The limit value, ie the max number of results that
|
46
|
+
# should be returned
|
47
|
+
#
|
48
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
49
|
+
|
50
|
+
def limit(value)
|
51
|
+
fresh.tap do |criteria|
|
52
|
+
criteria.limit_value = value.to_i
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
#
|
58
|
+
# Returns the limit value or, if not yet set, the default limit value (30).
|
59
|
+
#
|
60
|
+
# @return [Fixnum] The limit value
|
61
|
+
|
62
|
+
def limit_value_with_default
|
63
|
+
(limit_value || 30).to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sets pagination parameters for the criteria by using offset and limit,
|
67
|
+
# ie Elasticsearch's from and size parameters.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# CommentIndex.paginate(page: 3)
|
71
|
+
# CommentIndex.paginate(page: 5, per_page: 60)
|
72
|
+
#
|
73
|
+
# @param page [#to_i] The current page
|
74
|
+
# @param per_page [#to_i] The number of results per page
|
75
|
+
#
|
76
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
77
|
+
|
78
|
+
def paginate(page: 1, per_page: 30)
|
79
|
+
page = [page.to_i, 1].max
|
80
|
+
per_page = per_page.to_i
|
81
|
+
|
82
|
+
offset((page - 1) * per_page).limit(per_page)
|
83
|
+
end
|
84
|
+
|
85
|
+
def page(value)
|
86
|
+
paginate(page: value, per_page: limit_value_with_default)
|
87
|
+
end
|
88
|
+
|
89
|
+
def per(value)
|
90
|
+
paginate(page: 1 + (offset_value_with_default / limit_value_with_default), per_page: value)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module SearchFlip
|
2
|
+
# The SearchFlip::Sortable mixin provides the chainable methods #sort as
|
3
|
+
# well as #resort
|
4
|
+
|
5
|
+
module Sortable
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :sort_values
|
9
|
+
|
10
|
+
alias_method :order, :sort
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Specify the sort order you want Elasticsearch to use for sorting the
|
15
|
+
# results. When you call this multiple times, the sort orders are appended
|
16
|
+
# to the already existing ones. The sort arguments get passed to
|
17
|
+
# Elasticsearch without modifications, such that you can use sort by
|
18
|
+
# script, etc here as well.
|
19
|
+
#
|
20
|
+
# @example Default usage
|
21
|
+
# CommentIndex.sort(:user_id, :id)
|
22
|
+
#
|
23
|
+
# # Same as
|
24
|
+
#
|
25
|
+
# CommentIndex.sort(:user_id).sort(:id)
|
26
|
+
#
|
27
|
+
# @example Default hash usage
|
28
|
+
# CommentIndex.sort(user_id: "asc").sort(id: "desc")
|
29
|
+
#
|
30
|
+
# # Same as
|
31
|
+
#
|
32
|
+
# CommentIndex.sort({ user_id: "asc" }, { id: "desc" })
|
33
|
+
#
|
34
|
+
# @example Sort by native script
|
35
|
+
# CommentIndex.sort("_script" => "sort_script", lang: "native", order: "asc", type: "number")
|
36
|
+
#
|
37
|
+
# @param args The sort values that get passed to Elasticsearch
|
38
|
+
#
|
39
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
40
|
+
|
41
|
+
def sort(*args)
|
42
|
+
fresh.tap do |criteria|
|
43
|
+
criteria.sort_values = (sort_values || []) + args
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Specify the sort order you want Elasticsearch to use for sorting the
|
48
|
+
# results with already existing sort orders being removed.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# CommentIndex.sort(user_id: "asc").resort(id: "desc")
|
52
|
+
#
|
53
|
+
# # Same as
|
54
|
+
#
|
55
|
+
# CommentIndex.sort(id: "desc")
|
56
|
+
#
|
57
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
58
|
+
#
|
59
|
+
# @see #sort See #sort for more details
|
60
|
+
|
61
|
+
def resort(*args)
|
62
|
+
fresh.tap do |criteria|
|
63
|
+
criteria.sort_values = args
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
alias_method :reorder, :resort
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SearchFlip
|
2
|
+
# The SearchFlip::Sortable mixin provides the chainable #source method to
|
3
|
+
# use elasticsearch source filtering
|
4
|
+
|
5
|
+
module Sourceable
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
attr_accessor :source_value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Use to specify which fields of the source document you want Elasticsearch
|
13
|
+
# to return for each matching result.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# CommentIndex.source([:id, :message]).search("hello world")
|
17
|
+
# CommentIndex.source(exclude: "description")
|
18
|
+
# CommentIndex.source(false)
|
19
|
+
#
|
20
|
+
# @param value Pass any allowed value to restrict the returned source
|
21
|
+
#
|
22
|
+
# @return [SearchFlip::Criteria] A newly created extended criteria
|
23
|
+
|
24
|
+
def source(value)
|
25
|
+
fresh.tap do |criteria|
|
26
|
+
criteria.source_value = value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/search_flip/version.rb
CHANGED
data/lib/search_flip.rb
CHANGED
@@ -14,6 +14,12 @@ require "search_flip/config"
|
|
14
14
|
require "search_flip/connection"
|
15
15
|
require "search_flip/bulk"
|
16
16
|
require "search_flip/filterable"
|
17
|
+
require "search_flip/customable"
|
18
|
+
require "search_flip/explainable"
|
19
|
+
require "search_flip/highlightable"
|
20
|
+
require "search_flip/paginatable"
|
21
|
+
require "search_flip/sortable"
|
22
|
+
require "search_flip/sourceable"
|
17
23
|
require "search_flip/post_filterable"
|
18
24
|
require "search_flip/aggregatable"
|
19
25
|
require "search_flip/aggregation"
|
data/search_flip.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.post_install_message = <<~MESSAGE
|
22
22
|
Thanks for using search_flip!
|
23
|
-
When upgrading
|
23
|
+
When upgrading to 3.x, please check out
|
24
24
|
https://github.com/mrkamel/search_flip/blob/master/UPDATING.md
|
25
25
|
MESSAGE
|
26
26
|
|
@@ -271,8 +271,8 @@ RSpec.describe SearchFlip::Aggregation do
|
|
271
271
|
|
272
272
|
ProductIndex.import [product1, product2, product3, product4]
|
273
273
|
|
274
|
-
query = ProductIndex.aggregate(categories: {}) do |
|
275
|
-
|
274
|
+
query = ProductIndex.aggregate(categories: {}) do |aggregation|
|
275
|
+
aggregation.merge(ProductIndex.where(price: 100..200)).aggregate(:category)
|
276
276
|
end
|
277
277
|
|
278
278
|
result = query.aggregations(:categories).category.buckets.each_with_object({}) do |bucket, hash|
|
@@ -282,33 +282,24 @@ RSpec.describe SearchFlip::Aggregation do
|
|
282
282
|
expect(result).to eq("category1" => 2, "category2" => 1)
|
283
283
|
end
|
284
284
|
|
285
|
-
describe "
|
286
|
-
|
287
|
-
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :offset_value, :limit_value,
|
288
|
-
:scroll_args, :highlight_values, :suggest_values, :custom_value, :source_value, :sort_values,
|
289
|
-
:includes_values, :preload_values, :eager_load_values, :post_must_values,
|
290
|
-
:post_must_not_values, :post_filter_values, :preference_value,
|
291
|
-
:search_type_value, :routing_value
|
292
|
-
]
|
285
|
+
describe "assignments" do
|
286
|
+
methods = [:offset_value, :limit_value, :source_value, :explain_value]
|
293
287
|
|
294
|
-
|
295
|
-
it "
|
296
|
-
|
297
|
-
|
298
|
-
criteria = SearchFlip::Criteria.new(target: TestIndex)
|
299
|
-
criteria.send("#{unsupported_method}=", "value")
|
288
|
+
methods.each do |method|
|
289
|
+
it "replaces the values" do
|
290
|
+
aggregation = SearchFlip::Aggregation.new(target: TestIndex)
|
291
|
+
aggregation.send("#{method}=", "value1")
|
300
292
|
|
301
|
-
|
302
|
-
|
303
|
-
end
|
293
|
+
criteria = SearchFlip::Criteria.new(target: TestIndex)
|
294
|
+
criteria.send("#{method}=", "value2")
|
304
295
|
|
305
|
-
expect(
|
296
|
+
expect(aggregation.merge(criteria).send(method)).to eq("value2")
|
306
297
|
end
|
307
298
|
end
|
308
299
|
end
|
309
300
|
|
310
301
|
describe "array concatenations" do
|
311
|
-
methods = [:must_values, :must_not_values, :filter_values]
|
302
|
+
methods = [:sort_values, :must_values, :must_not_values, :filter_values]
|
312
303
|
|
313
304
|
methods.each do |method|
|
314
305
|
it "concatenates the values for #{method}" do
|
@@ -326,7 +317,7 @@ RSpec.describe SearchFlip::Aggregation do
|
|
326
317
|
end
|
327
318
|
|
328
319
|
describe "hash merges" do
|
329
|
-
methods = [:aggregation_values]
|
320
|
+
methods = [:highlight_values, :custom_value, :aggregation_values]
|
330
321
|
|
331
322
|
methods.each do |method|
|
332
323
|
it "merges the values for #{method}" do
|
@@ -342,6 +333,29 @@ RSpec.describe SearchFlip::Aggregation do
|
|
342
333
|
end
|
343
334
|
end
|
344
335
|
end
|
336
|
+
|
337
|
+
describe "unsupported methods" do
|
338
|
+
unsupported_methods = [
|
339
|
+
:profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :scroll_args,
|
340
|
+
:suggest_values, :includes_values, :preload_values, :eager_load_values, :post_must_values,
|
341
|
+
:post_must_not_values, :post_filter_values, :preference_value, :search_type_value, :routing_value
|
342
|
+
]
|
343
|
+
|
344
|
+
unsupported_methods.each do |unsupported_method|
|
345
|
+
it "raises a NotSupportedError #{unsupported_method}" do
|
346
|
+
block = lambda do
|
347
|
+
aggregation = SearchFlip::Aggregation.new(target: TestIndex)
|
348
|
+
|
349
|
+
criteria = SearchFlip::Criteria.new(target: TestIndex)
|
350
|
+
criteria.send("#{unsupported_method}=", "value")
|
351
|
+
|
352
|
+
aggregation.merge(criteria)
|
353
|
+
end
|
354
|
+
|
355
|
+
expect(&block).to raise_error(SearchFlip::NotSupportedError)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
345
359
|
end
|
346
360
|
|
347
361
|
describe "#respond_to?" do
|
@@ -369,8 +383,8 @@ RSpec.describe SearchFlip::Aggregation do
|
|
369
383
|
|
370
384
|
temp_index.import [product1, product2, product3, product4]
|
371
385
|
|
372
|
-
query = temp_index.aggregate(categories: {}) do |
|
373
|
-
|
386
|
+
query = temp_index.aggregate(categories: {}) do |aggregation|
|
387
|
+
aggregation.merge(temp_index.with_price_range(100..200)).aggregate(:category)
|
374
388
|
end
|
375
389
|
|
376
390
|
result = query.aggregations(:categories).category.buckets.each_with_object({}) do |bucket, hash|
|
@@ -380,4 +394,144 @@ RSpec.describe SearchFlip::Aggregation do
|
|
380
394
|
expect(result).to eq("category1" => 2, "category2" => 1)
|
381
395
|
end
|
382
396
|
end
|
397
|
+
|
398
|
+
describe "#explain" do
|
399
|
+
it "returns the explaination" do
|
400
|
+
ProductIndex.import create(:product)
|
401
|
+
|
402
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
403
|
+
aggregation.explain(true)
|
404
|
+
end
|
405
|
+
|
406
|
+
expect(query.aggregations("top_hits").hits.hits.first.key?("_explanation")).to eq(true)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe "#custom" do
|
411
|
+
it "adds a custom entry to the request" do
|
412
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
413
|
+
aggregation.custom(custom_key: "custom_value")
|
414
|
+
end
|
415
|
+
|
416
|
+
expect(query.request[:aggregations][:top_hits][:top_hits][:custom_key]).to eq("custom_value")
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
describe "#highlight" do
|
421
|
+
it "adds a custom entry to the request" do
|
422
|
+
ProductIndex.import create(:product, title: "Title highlight")
|
423
|
+
|
424
|
+
query = ProductIndex.search("title:highlight").aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
425
|
+
aggregation.highlight([:title])
|
426
|
+
end
|
427
|
+
|
428
|
+
expect(query.aggregations("top_hits").hits.hits.first.highlight.title).to be_present
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
describe "#page" do
|
433
|
+
it "returns the respective result window" do
|
434
|
+
product1, product2 = create_list(:product, 2)
|
435
|
+
|
436
|
+
ProductIndex.import [product1, product2]
|
437
|
+
|
438
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
439
|
+
aggregation.sort(:id).per(1).page(2)
|
440
|
+
end
|
441
|
+
|
442
|
+
expect(query.aggregations("top_hits").hits.hits.first._id).to eq(product2.id.to_s)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
describe "#per" do
|
447
|
+
it "returns the respective result window" do
|
448
|
+
ProductIndex.import create_list(:product, 2)
|
449
|
+
|
450
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
451
|
+
aggregation.per(1)
|
452
|
+
end
|
453
|
+
|
454
|
+
expect(query.aggregations("top_hits").hits.hits.size).to eq(1)
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
describe "#paginate" do
|
459
|
+
it "returns the respective result window" do
|
460
|
+
product1, product2 = create_list(:product, 2)
|
461
|
+
|
462
|
+
ProductIndex.import [product1, product2]
|
463
|
+
|
464
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
465
|
+
aggregation.sort(:id).paginate(page: 2, per_page: 1)
|
466
|
+
end
|
467
|
+
|
468
|
+
expect(query.aggregations("top_hits").hits.hits.first._id).to eq(product2.id.to_s)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe "#limit" do
|
473
|
+
it "returns the respective result window" do
|
474
|
+
ProductIndex.import create_list(:product, 2)
|
475
|
+
|
476
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
477
|
+
aggregation.limit(1)
|
478
|
+
end
|
479
|
+
|
480
|
+
expect(query.aggregations("top_hits").hits.hits.size).to eq(1)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe "#offset" do
|
485
|
+
it "returns the respective result window" do
|
486
|
+
product1, product2 = create_list(:product, 2)
|
487
|
+
|
488
|
+
ProductIndex.import [product1, product2]
|
489
|
+
|
490
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
491
|
+
aggregation.sort(:id).limit(1).offset(1)
|
492
|
+
end
|
493
|
+
|
494
|
+
expect(query.aggregations("top_hits").hits.hits.first._id).to eq(product2.id.to_s)
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
describe "#sort" do
|
499
|
+
it "returns the results in the specified order" do
|
500
|
+
product1, product2 = create_list(:product, 2)
|
501
|
+
|
502
|
+
ProductIndex.import [product1, product2]
|
503
|
+
|
504
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
505
|
+
aggregation.sort(id: "desc")
|
506
|
+
end
|
507
|
+
|
508
|
+
expect(query.aggregations("top_hits").hits.hits.map(&:_id)).to eq([product2, product1].map(&:id).map(&:to_s))
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
describe "#resort" do
|
513
|
+
it "overrides the previous sorting and returns the results in the specified order" do
|
514
|
+
product1, product2 = create_list(:product, 2)
|
515
|
+
|
516
|
+
ProductIndex.import [product1, product2]
|
517
|
+
|
518
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
519
|
+
aggregation.sort(id: "desc").resort(:id)
|
520
|
+
end
|
521
|
+
|
522
|
+
expect(query.aggregations("top_hits").hits.hits.map(&:_id)).to eq([product1, product2].map(&:id).map(&:to_s))
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
describe "#source" do
|
527
|
+
it "returns the specified fields only" do
|
528
|
+
ProductIndex.import create(:product)
|
529
|
+
|
530
|
+
query = ProductIndex.aggregate(top_hits: { top_hits: {} }) do |aggregation|
|
531
|
+
aggregation.source([:id, :title])
|
532
|
+
end
|
533
|
+
|
534
|
+
expect(query.aggregations("top_hits").hits.hits.first._source.keys).to eq(["id", "title"])
|
535
|
+
end
|
536
|
+
end
|
383
537
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: search_flip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Vetter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -202,16 +202,22 @@ files:
|
|
202
202
|
- lib/search_flip/config.rb
|
203
203
|
- lib/search_flip/connection.rb
|
204
204
|
- lib/search_flip/criteria.rb
|
205
|
+
- lib/search_flip/customable.rb
|
205
206
|
- lib/search_flip/exceptions.rb
|
207
|
+
- lib/search_flip/explainable.rb
|
206
208
|
- lib/search_flip/filterable.rb
|
207
209
|
- lib/search_flip/helper.rb
|
210
|
+
- lib/search_flip/highlightable.rb
|
208
211
|
- lib/search_flip/http_client.rb
|
209
212
|
- lib/search_flip/index.rb
|
210
213
|
- lib/search_flip/json.rb
|
211
214
|
- lib/search_flip/model.rb
|
215
|
+
- lib/search_flip/paginatable.rb
|
212
216
|
- lib/search_flip/post_filterable.rb
|
213
217
|
- lib/search_flip/response.rb
|
214
218
|
- lib/search_flip/result.rb
|
219
|
+
- lib/search_flip/sortable.rb
|
220
|
+
- lib/search_flip/sourceable.rb
|
215
221
|
- lib/search_flip/to_json.rb
|
216
222
|
- lib/search_flip/version.rb
|
217
223
|
- search_flip.gemspec
|
@@ -233,7 +239,7 @@ licenses:
|
|
233
239
|
metadata: {}
|
234
240
|
post_install_message: |
|
235
241
|
Thanks for using search_flip!
|
236
|
-
When upgrading
|
242
|
+
When upgrading to 3.x, please check out
|
237
243
|
https://github.com/mrkamel/search_flip/blob/master/UPDATING.md
|
238
244
|
rdoc_options: []
|
239
245
|
require_paths:
|