search_flip 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cda3eb59fdf7fabf986510c923cbded7f6a1219d734be60a406265d7ec685893
4
- data.tar.gz: a30be3c6195e1864b4a37899e079e096129692ec0bb5ac0f78a0cceaf15468a2
3
+ metadata.gz: b506132a3198cb96d7afad2e2a6b7b3eb3af73bdb18452459045379d4dde41bc
4
+ data.tar.gz: 31354cd6f2f715529351fb74e1577382f7c245e381d95f411bbbb1c0a0aeef1d
5
5
  SHA512:
6
- metadata.gz: df70d9f06aecbd84e7a5cb1ec16ffdd408a53da7a08f15fac4761a2e70b38b667ee4688abe7d81195eb6389e6decf2b591689a7002b75ca28cae16f8fa5bb88b
7
- data.tar.gz: f903399ac8baa4b4a41ebcf3d378b64fbae236a553dc975b90b3c13a9b50e39bdbc6508a6098884510205ae42a55bf057a94f2fa73a4906a5d053d4f86ac7ba8
6
+ metadata.gz: 6da9d91a867a2e50cd9ca3abb4784e41019a9980e3c78a6e55c1297190666a2e9bf6b7d0d43aa5431b1252eba5eb3de68d40491377788db4c09dc40579318ed2
7
+ data.tar.gz: e9be5d2dd82077f093eb07550cdf469d2aba1dfc8f205c26d96ae95e4cbe0be7d0e36b1bc44f9667a3151212cfb949761ef8fe8f8c5fd2a11cc7b10a8d29948e
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- # SearchFlip
2
+ # search_flip
3
3
 
4
4
  **Full-Featured ElasticSearch Ruby Client with a Chainable DSL**
5
5
 
@@ -235,11 +235,17 @@ CommentIndex.where(username: "mrkamel").total_entries
235
235
  CommentIndex.aggregate(:username).aggregations(:username)
236
236
  # => {1=>#<SearchFlip::Result doc_count=37 ...>, 2=>... }
237
237
  ...
238
+ ```
239
+
240
+ Please note that you can check the request that will be send to Elasticsearch
241
+ by simply calling `#request` on the query:
238
242
 
243
+ ```ruby
239
244
  CommentIndex.search("hello world").sort(id: "desc").aggregate(:username).request
240
245
  # => {:query=>{:bool=>{:must=>[{:query_string=>{:query=>"hello world", :default_operator=>:AND}}]}}, ...}
241
246
  ```
242
247
 
248
+
243
249
  delete records:
244
250
 
245
251
  ```ruby
@@ -292,7 +298,7 @@ within `update_aliases`.
292
298
  Please note that `with_settings(index_name: '...')` returns an anonymous, i.e.
293
299
  temporary, class inherting from UserIndex and overwriting `index_name`.
294
300
 
295
- ## Advanced Usage
301
+ ## Chainable Methods
296
302
 
297
303
  SearchFlip supports even more advanced usages, like e.g. post filters, filtered
298
304
  aggregations or nested aggregations via simple to use API methods.
@@ -303,8 +309,7 @@ SearchFlip provides powerful methods to query/filter Elasticsearch:
303
309
 
304
310
  * `where`
305
311
 
306
- Feels like ActiveRecord's `where` and performs a bool filter clause
307
- to the request:
312
+ Feels like ActiveRecord's `where` and adds a bool filter clause to the request:
308
313
 
309
314
  ```ruby
310
315
  CommentIndex.where(reviewed: true)
@@ -371,13 +376,14 @@ CommentIndex.must_not(term: { state: "approved" })
371
376
 
372
377
  * `search`
373
378
 
374
- Adds a query string query, with AND as default filter:
379
+ Adds a query string query, with AND as default operator:
375
380
 
376
381
  ```ruby
377
- CommentIndex.search("harry potter")
382
+ CommentIndex.search("hello world")
378
383
  CommentIndex.search("state:approved")
379
384
  CommentIndex.search("username:a*")
380
385
  CommentIndex.search("state:approved OR state:rejected")
386
+ CommentIndex.search("hello world", default_operator: "OR")
381
387
  ```
382
388
 
383
389
  * `exists`
@@ -430,9 +436,9 @@ query = OrderIndex.aggregate(:username, order: { revenue: "desc" }) do |aggregat
430
436
  end
431
437
  ```
432
438
 
433
- Generally, aggregation results returned by ElasticSearch are wrapped in a
434
- `SearchFlip::Result`, which wraps a `Hashie::Mash`such that you can access them
435
- via:
439
+ Generally, aggregation results returned by ElasticSearch are returned as a
440
+ `SearchFlip::Result`, which basically is `Hashie::Mash`such that you can access
441
+ them via:
436
442
 
437
443
  ```ruby
438
444
  query.aggregations(:username)["mrkamel"].revenue.value
@@ -488,8 +494,8 @@ query.results[0]._hit.highlight.title # => "<em>hello</em> world"
488
494
 
489
495
  ### Other Criteria Methods
490
496
 
491
- There are so many chainable criteria methods to make your life easier.
492
- For a full list, checkout the reference docs.
497
+ There are even more chainable criteria methods to make your life easier. For a
498
+ full list, checkout the reference docs.
493
499
 
494
500
  * `source`
495
501
 
@@ -512,21 +518,6 @@ CommentIndex.paginate(page: 3, per_page: 50)
512
518
  CommentIndex.page(3).per(50)
513
519
  ```
514
520
 
515
- * `scroll`
516
-
517
- You can as well use the underlying scroll API directly, ie. without using higher
518
- level pagination:
519
-
520
- ```ruby
521
- query = CommentIndex.scroll(timeout: "5m")
522
-
523
- until query.records.empty?
524
- # ...
525
-
526
- query = query.scroll(id: query.scroll_id, timeout: "5m")
527
- end
528
- ```
529
-
530
521
  * `profile`
531
522
 
532
523
  Use `#profile` to enable query profiling:
@@ -577,8 +568,8 @@ end
577
568
 
578
569
  * `find_results_in_batches`
579
570
 
580
- Used like `find_in_batches`, but yielding the raw results instead of database
581
- records. Again, the batch size and scroll API timeout can be specified.
571
+ Used like `find_in_batches`, but yielding the raw results (as
572
+ `SearchFlip::Result` objects) instead of database records.
582
573
 
583
574
  ```ruby
584
575
  CommentIndex.search("hello world").find_results_in_batches(batch_size: 100) do |batch|
@@ -588,8 +579,7 @@ end
588
579
 
589
580
  * `find_each`
590
581
 
591
- Like `#find_in_batches`, use `#find_each` to fetch records in batches, but yields
592
- one record at a time.
582
+ Like `#find_in_batches` but yielding one record at a time.
593
583
 
594
584
  ```ruby
595
585
  CommentIndex.search("hello world").find_each(batch_size: 100) do |record|
@@ -597,6 +587,31 @@ CommentIndex.search("hello world").find_each(batch_size: 100) do |record|
597
587
  end
598
588
  ```
599
589
 
590
+ * `find_each_result`
591
+
592
+ Like `#find_results_in_batches`, but yielding one record at a time.
593
+
594
+ ```ruby
595
+ CommentIndex.search("hello world").find_each_result(batch_size: 100) do |batch|
596
+ # ...
597
+ end
598
+ ```
599
+
600
+ * `scroll`
601
+
602
+ You can as well use the underlying scroll API directly, ie. without using higher
603
+ level scrolling:
604
+
605
+ ```ruby
606
+ query = CommentIndex.scroll(timeout: "5m")
607
+
608
+ until query.records.empty?
609
+ # ...
610
+
611
+ query = query.scroll(id: query.scroll_id, timeout: "5m")
612
+ end
613
+ ```
614
+
600
615
  * `failsafe`
601
616
 
602
617
  Use `#failsafe` to prevent any exceptions from being raised for query string
@@ -622,14 +637,6 @@ CommentIndex.where(approved: true).merge(CommentIndex.search("hello"))
622
637
  # equivalent to: CommentIndex.where(approved: true).search("hello")
623
638
  ```
624
639
 
625
- * `unscope`
626
-
627
- You can even remove certain already added scopes via `#unscope`:
628
-
629
- ```ruby
630
- CommentIndex.aggregate(:username).search("hello world").unscope(:search, :aggregate)
631
- ```
632
-
633
640
  * `timeout`
634
641
 
635
642
  Specify a timeout to limit query processing time:
@@ -649,6 +656,33 @@ CommentIndex.terminate_after(10).execute
649
656
 
650
657
  For further details and a full list of methods, check out the reference docs.
651
658
 
659
+ * `custom`
660
+
661
+ You can add a custom clause to the request via `custom`
662
+
663
+ ```ruby
664
+ CommentIndex.custom(custom_clause: '...')
665
+ ```
666
+
667
+ This can be useful for Elasticsearch features not yet supported via criteria
668
+ methods by SearchFlip, custom plugin clauses, etc.
669
+
670
+ ### Custom Criteria Methods
671
+
672
+ To add custom criteria methods, you can add class methods to your index class.
673
+
674
+ ```ruby
675
+ class HotelIndex
676
+ # ...
677
+
678
+ def self.where_geo(lat:, lon:, distance:)
679
+ filter(geo_distance: { distance: distance, location: { lat: lat, lon: lon } })
680
+ end
681
+ end
682
+
683
+ HotelIndex.search("bed and breakfast").where_geo(lat: 53.57532, lon: 10.01534, distance: '50km').aggregate(:rating)
684
+ ```
685
+
652
686
  ## Using multiple Elasticsearch clusters
653
687
 
654
688
  To use multiple Elasticsearch clusters, specify a connection within your
@@ -669,6 +703,26 @@ end
669
703
  This allows to use different clusters per index e.g. when migrating indices to
670
704
  new versions of Elasticsearch.
671
705
 
706
+ You can specify basic auth, additional headers, etc via:
707
+
708
+ ```ruby
709
+ http_client = SearchFlip::HTTPClient.new
710
+
711
+ # Basic Auth
712
+ http_client = http_client.basic_auth(user: "username", pass: "password")
713
+
714
+ # Raw Auth Header
715
+ http_client = http_client.auth("Bearer VGhlIEhUVFAgR2VtLCBST0NLUw")
716
+
717
+ # Proxy Settings
718
+ http_client = http_client.via("proxy.host", 8080)
719
+
720
+ # Custom headers
721
+ http_client = http_client.headers(key: "value")
722
+
723
+ SearchFlip::Connection.new(base_url: "...", http_client: http_client)
724
+ ```
725
+
672
726
  ## Routing and other index-time options
673
727
 
674
728
  Override `index_options` in case you want to use routing or pass other
@@ -712,8 +766,9 @@ class MyIndex
712
766
  end
713
767
  ```
714
768
 
715
- Thus, simply add your custom implementation of those methods that work with
716
- whatever ORM you use.
769
+ Thus, if your ORM supports `.find_each`, `#id` and `#where` you are already
770
+ good to go. Otherwise, simply add your custom implementation of those methods
771
+ that work with whatever ORM you use.
717
772
 
718
773
  ## Date and Timestamps in JSON
719
774
 
@@ -764,10 +819,11 @@ require "search_flip/to_json"
764
819
 
765
820
  ## Feature Support
766
821
 
767
- * `#post_search` and `#profile` are only supported from up to ElasticSearch
822
+ * `#post_search` and `#profile` are only supported from up to Elasticsearch
768
823
  version >= 2.
769
- * for ElasticSearch 2.x, the delete-by-query plugin is required to delete
824
+ * for Elasticsearch 2.x, the delete-by-query plugin is required to delete
770
825
  records via queries
826
+ * `#track_total_hits` is only available with Elasticsearch >= 7
771
827
 
772
828
  ## Keeping your Models and Indices in Sync
773
829
 
data/UPDATING.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  ## Update 1.x to 2.x
5
5
 
6
- * **[BREAKING]** No longer include the mapping in `SearchFlip::Index.mapping`
6
+ * **[BREAKING]** No longer include the `type_name` in `SearchFlip::Index.mapping`
7
7
 
8
8
  1.x:
9
9
 
@@ -22,7 +22,7 @@ module SearchFlip
22
22
  attr_accessor :target, :profile_value, :source_value, :sort_values, :highlight_values, :suggest_values,
23
23
  :offset_value, :limit_value, :includes_values, :eager_load_values, :preload_values, :failsafe_value,
24
24
  :scroll_args, :custom_value, :terminate_after_value, :timeout_value, :preference_value,
25
- :search_type_value, :routing_value
25
+ :search_type_value, :routing_value, :track_total_hits_value
26
26
 
27
27
  # Creates a new criteria while merging the attributes (constraints,
28
28
  # settings, etc) of the current criteria with the attributes of another one
@@ -51,6 +51,7 @@ module SearchFlip
51
51
  criteria.preference_value = other.preference_value if other.preference_value
52
52
  criteria.search_type_value = other.search_type_value if other.search_type_value
53
53
  criteria.routing_value = other.routing_value if other.routing_value
54
+ criteria.track_total_hits_value = other.track_total_hits_value unless other.track_total_hits_value.nil?
54
55
 
55
56
  criteria.sort_values = (criteria.sort_values || []) + other.sort_values if other.sort_values
56
57
  criteria.includes_values = (criteria.includes_values || []) + other.includes_values if other.includes_values
@@ -74,6 +75,23 @@ module SearchFlip
74
75
  end
75
76
  end
76
77
 
78
+ # Specifies if or how many hits should be counted/tracked. Check out the
79
+ # elasticsearch docs for futher details.
80
+ #
81
+ # @example
82
+ # CommentIndex.track_total_hits(true)
83
+ # CommentIndex.track_total_hits(10_000)
84
+ #
85
+ # @param value The value for track_total_hits
86
+ #
87
+ # @return [SearchFlip::Criteria] A newly created extended criteria
88
+
89
+ def track_total_hits(value)
90
+ fresh.tap do |criteria|
91
+ criteria.track_total_hits_value = value
92
+ end
93
+ end
94
+
77
95
  # Specifies a preference value for the request. Check out the elasticsearch
78
96
  # docs for further details.
79
97
  #
@@ -167,6 +185,8 @@ module SearchFlip
167
185
  # @return [SearchFlip::Criteria] A newly created extended criteria
168
186
 
169
187
  def unscope(*scopes)
188
+ warn "[DEPRECATION] unscope is deprecated"
189
+
170
190
  unknown = scopes - [:search, :post_search, :sort, :highlight, :suggest, :custom, :aggregate]
171
191
 
172
192
  raise(ArgumentError, "Can't unscope #{unknown.join(", ")}") if unknown.size > 0
@@ -260,6 +280,7 @@ module SearchFlip
260
280
 
261
281
  res.update from: offset_value_with_default, size: limit_value_with_default
262
282
 
283
+ res[:track_total_hits] = track_total_hits_value unless track_total_hits_value.nil?
263
284
  res[:timeout] = timeout_value if timeout_value
264
285
  res[:terminate_after] = terminate_after_value if terminate_after_value
265
286
  res[:highlight] = highlight_values if highlight_values
@@ -254,7 +254,8 @@ module SearchFlip
254
254
  :includes, :eager_load, :preload, :sort, :resort, :order, :reorder, :offset, :limit, :paginate,
255
255
  :page, :per, :search, :highlight, :suggest, :custom, :find_in_batches, :find_results_in_batches,
256
256
  :find_each, :find_each_result, :failsafe, :total_entries, :total_count, :timeout, :terminate_after,
257
- :records, :results, :should, :should_not, :must, :must_not, :preference, :search_type, :routing
257
+ :records, :results, :should, :should_not, :must, :must_not, :preference, :search_type, :routing,
258
+ :track_total_hits
258
259
 
259
260
  # Override to specify the type name used within ElasticSearch. Recap,
260
261
  # this gem uses an individual index for each index class, because
@@ -1,5 +1,5 @@
1
1
 
2
2
  module SearchFlip
3
- VERSION = "2.1.0"
3
+ VERSION = "2.2.0"
4
4
  end
5
5
 
data/logo.svg ADDED
@@ -0,0 +1,96 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Generated by IcoMoon.io -->
3
+
4
+ <svg
5
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6
+ xmlns:cc="http://creativecommons.org/ns#"
7
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ xmlns:svg="http://www.w3.org/2000/svg"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ version="1.1"
13
+ width="300"
14
+ height="50"
15
+ viewBox="0 0 299.99998 50"
16
+ id="svg4869"
17
+ sodipodi:docname="logo.svg"
18
+ inkscape:export-filename="/home/hkf/projects/search_flip/logo.png"
19
+ inkscape:export-xdpi="299.34824"
20
+ inkscape:export-ydpi="299.34824"
21
+ inkscape:version="0.92.1 r15371">
22
+ <metadata
23
+ id="metadata4875">
24
+ <rdf:RDF>
25
+ <cc:Work
26
+ rdf:about="">
27
+ <dc:format>image/svg+xml</dc:format>
28
+ <dc:type
29
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
30
+ <dc:title>arrow-long-up</dc:title>
31
+ </cc:Work>
32
+ </rdf:RDF>
33
+ </metadata>
34
+ <defs
35
+ id="defs4873" />
36
+ <sodipodi:namedview
37
+ pagecolor="#ffffff"
38
+ bordercolor="#666666"
39
+ borderopacity="1"
40
+ objecttolerance="10"
41
+ gridtolerance="10"
42
+ guidetolerance="10"
43
+ inkscape:pageopacity="0"
44
+ inkscape:pageshadow="2"
45
+ inkscape:window-width="1920"
46
+ inkscape:window-height="971"
47
+ id="namedview4871"
48
+ showgrid="false"
49
+ fit-margin-top="0"
50
+ fit-margin-left="0"
51
+ fit-margin-right="0"
52
+ fit-margin-bottom="0"
53
+ inkscape:zoom="2.085965"
54
+ inkscape:cx="91.987216"
55
+ inkscape:cy="-41.606043"
56
+ inkscape:window-x="0"
57
+ inkscape:window-y="31"
58
+ inkscape:window-maximized="1"
59
+ inkscape:current-layer="svg4869" />
60
+ <title
61
+ id="title4865">arrow-long-up</title>
62
+ <path
63
+ d="M 39.922489,4.151999 36.230955,18.361869 32.37339,14.504302 10.209748,36.667943 7.4576271,33.915823 29.621268,11.75218 25.712619,7.843531 Z"
64
+ id="path4867"
65
+ inkscape:connector-curvature="0"
66
+ style="stroke-width:1.80568123"
67
+ inkscape:export-xdpi="325.4834"
68
+ inkscape:export-ydpi="325.4834"
69
+ sodipodi:nodetypes="cccccccc"
70
+ inkscape:export-filename="/home/hkf/projects/search_flip/text4908.png" />
71
+ <path
72
+ d="M 17.625202,45.376517 31.473864,41.323778 27.948317,37.696072 50.111962,15.532428 47.418207,12.940838 25.254566,35.10448 21.677941,31.527854 Z"
73
+ id="path4867-7"
74
+ inkscape:connector-curvature="0"
75
+ style="stroke-width:1.74257004"
76
+ inkscape:export-xdpi="325.4834"
77
+ inkscape:export-ydpi="325.4834"
78
+ sodipodi:nodetypes="cccccccc"
79
+ inkscape:export-filename="/home/hkf/projects/search_flip/text4908.png" />
80
+ <text
81
+ xml:space="preserve"
82
+ style="font-style:normal;font-weight:normal;font-size:96.44345093px;line-height:1.25;font-family:sans-serif;letter-spacing:-0.65099335px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2.41108632"
83
+ x="59.037937"
84
+ y="37.501484"
85
+ id="text4908"
86
+ inkscape:export-filename="/home/hkf/projects/search_flip/text4908.png"
87
+ inkscape:export-xdpi="325.4834"
88
+ inkscape:export-ydpi="325.4834"
89
+ inkscape:transform-center-x="-2.0432889"
90
+ inkscape:transform-center-y="-9.8078108"><tspan
91
+ sodipodi:role="line"
92
+ id="tspan4906"
93
+ x="59.037937"
94
+ y="37.501484"
95
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:51.43650818px;line-height:6;font-family:'times new roman';-inkscape-font-specification:'times new roman, ';letter-spacing:0px;word-spacing:-19.48158455px;stroke-width:2.41108632">search_flip</tspan></text>
96
+ </svg>
@@ -36,7 +36,7 @@ RSpec.describe SearchFlip::Criteria do
36
36
  methods = [
37
37
  :profile_value, :failsafe_value, :terminate_after_value, :timeout_value,
38
38
  :offset_value, :limit_value, :scroll_args, :source_value, :preference_value,
39
- :search_type_value, :routing_value
39
+ :search_type_value, :routing_value, :track_total_hits_value
40
40
  ]
41
41
 
42
42
  methods.each do |method|
@@ -1025,6 +1025,16 @@ RSpec.describe SearchFlip::Criteria do
1025
1025
  end
1026
1026
  end
1027
1027
 
1028
+ describe "#track_total_hits" do
1029
+ it "is added to the request" do
1030
+ if ProductIndex.connection.version.to_i >= 7
1031
+ query = ProductIndex.track_total_hits(false)
1032
+ expect(query.request[:track_total_hits]).to eq(false)
1033
+ expect { query.execute }.not_to raise_error
1034
+ end
1035
+ end
1036
+ end
1037
+
1028
1038
  describe "#custom" do
1029
1039
  it "adds a custom entry to the request" do
1030
1040
  request = ProductIndex.custom(custom_key: "custom_value").request
@@ -14,7 +14,8 @@ RSpec.describe SearchFlip::Index do
14
14
  :find_in_batches, :highlight, :suggest, :custom, :find_each, :failsafe,
15
15
  :total_entries, :total_count, :terminate_after, :timeout, :records, :results,
16
16
  :should, :should_not, :must, :must_not, :find_each_result,
17
- :find_results_in_batches, :preference, :search_type, :routing
17
+ :find_results_in_batches, :preference, :search_type, :routing,
18
+ :track_total_hits
18
19
  ]
19
20
 
20
21
  methods.each do |method|
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: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Vetter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-10 00:00:00.000000000 Z
11
+ date: 2019-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -199,6 +199,7 @@ files:
199
199
  - lib/search_flip/result.rb
200
200
  - lib/search_flip/to_json.rb
201
201
  - lib/search_flip/version.rb
202
+ - logo.svg
202
203
  - search_flip.gemspec
203
204
  - spec/delegate_matcher.rb
204
205
  - spec/search_flip/aggregation_spec.rb