search_flip 1.1.0 → 2.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 62b02c26a3579fdda9d1eca222c6c1eb19b28382
4
- data.tar.gz: fc3fe009dc1c8d84d03ab497c5302922474282c5
2
+ SHA256:
3
+ metadata.gz: 823b618d9cd15618f5e44b441644f281f1b5598feb11f52db75a538df78761ba
4
+ data.tar.gz: 3812f77dbcd670d76f9ae641f574035ff847c97b6c151f70f3fa663edf9619a6
5
5
  SHA512:
6
- metadata.gz: f6cee1c3258c0ff0662cec18d72313552706054e55a08c2f14003999c6d0d4bb910112507da340ad9dc1510b9f3f4b422ce11b0ec1b7eea8481450d430436bfc
7
- data.tar.gz: 9062e11098ab36c5063f992284a1d94ea27b422fabb0d4e94aa5b1c5b37da8c0508326d20566ab155f649034e2f924e739795b4ab89ab06ca6b2fa107f0e3bd3
6
+ metadata.gz: 2698a48a6c9e880a57776bd9bd39b21fdcbace96c5e1e50d12f81064dd3fb095d35c528c792d4f40951a9debde33dae4620928934e98596b3a32b2e9bb406c80
7
+ data.tar.gz: c4ba2b7c24f4096c98e3458e6c289c1ea7f9519f3f29ebf87d882f4d7600839c8ee96eb3c07efb76ff1fe3b738b39a0f7d9036a3682d86bc5751c71f26acdcc2
data/.rubocop.yml ADDED
@@ -0,0 +1,110 @@
1
+ Style/FrozenStringLiteralComment:
2
+ Enabled: false
3
+
4
+ Style/UnneededPercentQ:
5
+ Enabled: false
6
+
7
+ Style/BracesAroundHashParameters:
8
+ Enabled: false
9
+
10
+ Style/PercentLiteralDelimiters:
11
+ Enabled: false
12
+
13
+ Style/SpecialGlobalVars:
14
+ Enabled: false
15
+
16
+ Security/Eval:
17
+ Enabled: false
18
+
19
+ Style/WordArray:
20
+ Enabled: false
21
+
22
+ Style/ClassAndModuleChildren:
23
+ Enabled: false
24
+
25
+ Style/TrivialAccessors:
26
+ Enabled: false
27
+
28
+ Style/Alias:
29
+ Enabled: false
30
+
31
+ Style/StringLiteralsInInterpolation:
32
+ EnforcedStyle: double_quotes
33
+
34
+ Metrics/ClassLength:
35
+ Enabled: false
36
+
37
+ Naming/UncommunicativeMethodParamName:
38
+ Enabled: false
39
+
40
+ Style/SymbolArray:
41
+ Enabled: false
42
+
43
+ Layout/RescueEnsureAlignment:
44
+ Enabled: false
45
+
46
+ Layout/LeadingBlankLines:
47
+ Enabled: false
48
+
49
+ Metrics/LineLength:
50
+ Max: 150
51
+
52
+ Metrics/MethodLength:
53
+ Max: 60
54
+
55
+ Metrics/ModuleLength:
56
+ Enabled: false
57
+
58
+ Style/ZeroLengthPredicate:
59
+ Enabled: false
60
+
61
+ Layout/TrailingBlankLines:
62
+ Enabled: false
63
+
64
+ Metrics/PerceivedComplexity:
65
+ Enabled: false
66
+
67
+ Metrics/AbcSize:
68
+ Enabled: false
69
+
70
+ Metrics/CyclomaticComplexity:
71
+ Enabled: false
72
+
73
+ Metrics/BlockLength:
74
+ Max: 30
75
+
76
+ Metrics/BlockNesting:
77
+ Enabled: false
78
+
79
+ Style/NumericPredicate:
80
+ Enabled: false
81
+
82
+ Naming/AccessorMethodName:
83
+ Enabled: false
84
+
85
+ Naming/MemoizedInstanceVariableName:
86
+ Enabled: false
87
+
88
+ Style/StringLiterals:
89
+ EnforcedStyle: double_quotes
90
+
91
+ Style/Documentation:
92
+ Enabled: false
93
+
94
+ Naming/ConstantName:
95
+ Enabled: false
96
+
97
+ Style/MutableConstant:
98
+ Enabled: false
99
+
100
+ Layout/MultilineMethodCallIndentation:
101
+ EnforcedStyle: indented
102
+
103
+ Layout/AlignParameters:
104
+ EnforcedStyle: with_fixed_indentation
105
+
106
+ Lint/UnusedMethodArgument:
107
+ Enabled: false
108
+
109
+ Layout/IndentArray:
110
+ EnforcedStyle: consistent
data/.travis.yml CHANGED
@@ -1,36 +1,17 @@
1
1
 
2
+ sudo: false
3
+ language: ruby
4
+ env:
5
+ - ES_IMAGE=elasticsearch:1
6
+ - ES_IMAGE=plainpicture/elasticsearch:2.4.1_delete-by-query
7
+ - ES_IMAGE=elasticsearch:5.4
8
+ - ES_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:6.5.0
2
9
  rvm:
3
- - 2.3.3
4
10
  - ruby-head
5
-
6
- dist: trusty
7
-
8
- jdk:
9
- - openjdk8
10
-
11
- env:
12
- - ES_VERSION=1
13
- - ES_VERSION=2
14
- - ES_VERSION=5
15
- - ES_VERSION=6
16
-
11
+ before_install:
12
+ - docker-compose up -d
13
+ - sleep 10
17
14
  install:
18
15
  - travis_retry bundle install
19
- - sh -c "if [ '$ES_VERSION' = '6' ]; then (curl -s https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.2.2.tar.gz | tar xz -C /tmp); fi"
20
- - sh -c "if [ '$ES_VERSION' = '6' ]; then /tmp/elasticsearch-6.2.2/bin/elasticsearch -d; fi"
21
- - sh -c "if [ '$ES_VERSION' = '5' ]; then (curl -s https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.0.tar.gz | tar xz -C /tmp); fi"
22
- - sh -c "if [ '$ES_VERSION' = '5' ]; then /tmp/elasticsearch-5.4.0/bin/elasticsearch -d; fi"
23
- - sh -c "if [ '$ES_VERSION' = '2' ]; then (curl -s https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.1/elasticsearch-2.4.1.tar.gz | tar xz -C /tmp); fi"
24
- - sh -c "if [ '$ES_VERSION' = '2' ]; then /tmp/elasticsearch-2.4.1/bin/plugin install delete-by-query; fi"
25
- - sh -c "if [ '$ES_VERSION' = '2' ]; then /tmp/elasticsearch-2.4.1/bin/elasticsearch -d; fi"
26
- - sh -c "if [ '$ES_VERSION' = '1' ]; then (curl -s https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.4.tar.gz | tar xz -C /tmp); fi"
27
- - sh -c "if [ '$ES_VERSION' = '1' ]; then /tmp/elasticsearch-1.7.4/bin/elasticsearch -d; fi"
28
-
29
- before_script:
30
- - sleep 30
31
-
32
- script:
33
- - rake test --trace
34
-
35
- sudo: false
16
+ script: rake test --trace
36
17
 
data/CHANGELOG.md CHANGED
@@ -1,10 +1,22 @@
1
1
 
2
2
  # CHANGELOG
3
3
 
4
- * Version 1.1.0
4
+ ## v2.0.0
5
5
 
6
- - Added `Criteria#find_results_in_batches` to scroll through the raw results
7
- - Fixed bug in `Criteria#find_in_batches` which possibly stopped scrolling too early
8
- - Added delegation for `should`, `should_not`, `must` and `must_not`
9
- - Migrated To FactoryBot
6
+ * **BREAKING**: Migration steps
7
+ * Change `SearchFlip.version` to `SearchFlip::Connection#version`
8
+ * Change `SearchFlip.msearch` to `SearchFlip::Connection#msearch`
9
+ * Change `SearchFlip.aliases` to `SearchFlip::Connection#update_aliases`
10
+ * Change `SearchFlip::Criteria#execute(base_url: '...')` to `SearchFlip::Criteria#execute(connection: SearchFlip::Connection.new(base_url: '...'))`
11
+ * Added `SearchFlip::Connection`
12
+ * Added `SearchFlip::Connection#update_aliases`
13
+ * Added `SearchFlip::Connection#get_aliases`
14
+ * Added `SearchFlip::Connection#alias_exists?`
15
+
16
+ ## v1.1.0
17
+
18
+ * Added `Criteria#find_results_in_batches` to scroll through the raw results
19
+ * Fixed bug in `Criteria#find_in_batches` which possibly stopped scrolling too early
20
+ * Added delegation for `should`, `should_not`, `must` and `must_not`
21
+ * Migrated To FactoryBot
10
22
 
data/README.md CHANGED
@@ -501,6 +501,24 @@ CommentIndex.terminate_after(10).execute
501
501
 
502
502
  For further details and a full list of methods, check out the reference docs.
503
503
 
504
+ ## Using multiple Elasticsearch clusters
505
+
506
+ To use multiple Elasticsearch clusters, specify a connection within your
507
+ indices:
508
+
509
+ ```ruby
510
+ class MyIndex
511
+ include SearchFlip::Index
512
+
513
+ def self.connection
514
+ @connection ||= SearchFlip::Connection.new(base_url: "http://elasticsearch.host:9200")
515
+ end
516
+ end
517
+ ```
518
+
519
+ This allows to use different clusters per index e.g. when migrating indices to
520
+ new versions of Elasticsearch.
521
+
504
522
  ## Non-ActiveRecord models
505
523
 
506
524
  SearchFlip ships with built-in support for ActiveRecord models, but using
@@ -618,3 +636,17 @@ model changes.
618
636
  4. Push to the branch (`git push origin my-new-feature`)
619
637
  5. Create new Pull Request
620
638
 
639
+ ## Running the test suite
640
+
641
+ Running the tests is super easy. The test suite uses sqlite, such that you only
642
+ need to install ElasticSearch. You can install ElasticSearch on your own, or
643
+ you can e.g. use docker-compose:
644
+
645
+ ```
646
+ $ cd search_flip
647
+ $ sudo ES_IMAGE=elasticsearch:5.4 docker-compose up
648
+ $ rake test
649
+ ```
650
+
651
+ That's it.
652
+
@@ -0,0 +1,6 @@
1
+ version: '2'
2
+ services:
3
+ elasticsearch:
4
+ image: $ES_IMAGE
5
+ ports:
6
+ - 9200:9200
data/lib/search_flip.rb CHANGED
@@ -6,9 +6,11 @@ require "oj"
6
6
  require "set"
7
7
 
8
8
  require "search_flip/version"
9
+ require "search_flip/exceptions"
9
10
  require "search_flip/json"
10
11
  require "search_flip/http_client"
11
12
  require "search_flip/config"
13
+ require "search_flip/connection"
12
14
  require "search_flip/bulk"
13
15
  require "search_flip/filterable"
14
16
  require "search_flip/post_filterable"
@@ -36,47 +38,5 @@ module SearchFlip
36
38
  "#{self.class.name} (#{code}): #{body}"
37
39
  end
38
40
  end
39
-
40
- # Uses the ElasticSearch Multi Search API to execute multiple search requests
41
- # within a single request. Raises SearchFlip::ResponseError in case any
42
- # errors occur.
43
- #
44
- # @example
45
- # SearchFlip.msearch [ProductIndex.match_all, CommentIndex.match_all]
46
- #
47
- # @param criterias [Array<SearchFlip::Criteria>] An array of search
48
- # queries to execute in parallel
49
- #
50
- # @return [Array<SearchFlip::Response>] An array of responses
51
-
52
- def self.msearch(criterias)
53
- payload = criterias.flat_map do |criteria|
54
- [SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, type: criteria.target.type_name), SearchFlip::JSON.generate(criteria.request)]
55
- end
56
-
57
- payload = payload.join("\n")
58
- payload << "\n"
59
-
60
- SearchFlip::HTTPClient.headers(accept: "application/json", content_type: "application/x-ndjson").post("#{SearchFlip::Config[:base_url]}/_msearch", body: payload).parse["responses"].map.with_index do |response, index|
61
- SearchFlip::Response.new(criterias[index], response)
62
- end
63
- end
64
-
65
- # Used to manipulate, ie add and remove index aliases. Raises an
66
- # SearchFlip::ResponseError in case any errors occur.
67
- #
68
- # @example
69
- # ElasticSearch.post_aliases(actions: [
70
- # { remove: { index: "test1", alias: "alias1" }},
71
- # { add: { index: "test2", alias: "alias1" }}
72
- # ])
73
- #
74
- # @param payload [Hash] The raw request payload
75
- #
76
- # @return [SearchFlip::Response] The raw response
77
-
78
- def self.aliases(payload)
79
- SearchFlip::HTTPClient.headers(accept: "application/json", content_type: "application/json").post("#{SearchFlip::Config[:base_url]}/_aliases", body: SearchFlip::JSON.generate(payload))
80
- end
81
41
  end
82
42
 
@@ -56,7 +56,7 @@ module SearchFlip
56
56
  hash = field_or_hash.is_a?(Hash) ? field_or_hash : { field_or_hash => { terms: { field: field_or_hash }.merge(options) } }
57
57
 
58
58
  if block
59
- aggregation = block.call(SearchFlip::Aggregation.new)
59
+ aggregation = yield(SearchFlip::Aggregation.new(target: target))
60
60
 
61
61
  field_or_hash.is_a?(Hash) ? hash[field_or_hash.keys.first].merge!(aggregation.to_hash) : hash[field_or_hash].merge!(aggregation.to_hash)
62
62
  end
@@ -8,6 +8,12 @@ module SearchFlip
8
8
  include SearchFlip::Filterable
9
9
  include SearchFlip::Aggregatable
10
10
 
11
+ attr_reader :target
12
+
13
+ def initialize(target:)
14
+ @target = target
15
+ end
16
+
11
17
  # @api private
12
18
  #
13
19
  # Converts the aggregation to a hash format that can be used in the request.
@@ -19,20 +25,20 @@ module SearchFlip
19
25
  res[:aggregations] = aggregation_values if aggregation_values
20
26
 
21
27
  if must_values || search_values || must_not_values || should_values || filter_values
22
- if SearchFlip.version.to_i >= 2
28
+ if target.connection.version.to_i >= 2
23
29
  res[:filter] = {
24
- bool: {}.
25
- merge(must_values || search_values ? { must: (must_values || []) + (search_values || []) } : {}).
26
- merge(must_not_values ? { must_not: must_not_values } : {}).
27
- merge(should_values ? { should: should_values } : {}).
28
- merge(filter_values ? { filter: filter_values } : {})
30
+ bool: {}
31
+ .merge(must_values || search_values ? { must: (must_values || []) + (search_values || []) } : {})
32
+ .merge(must_not_values ? { must_not: must_not_values } : {})
33
+ .merge(should_values ? { should: should_values } : {})
34
+ .merge(filter_values ? { filter: filter_values } : {})
29
35
  }
30
36
  else
31
37
  filters = (filter_values || []) + (must_not_values || []).map { |must_not_value| { not: must_not_value } }
32
38
 
33
- queries = {}.
34
- merge(must_values || search_values ? { must: (must_values || []) + (search_values || []) } : {}).
35
- merge(should_values ? { should: should_values } : {})
39
+ queries = {}
40
+ .merge(must_values || search_values ? { must: (must_values || []) + (search_values || []) } : {})
41
+ .merge(should_values ? { should: should_values } : {})
36
42
 
37
43
  filters_and_queries = filters + (queries.size > 0 ? [bool: queries] : [])
38
44
 
@@ -113,7 +113,10 @@ module SearchFlip
113
113
  end
114
114
 
115
115
  def upload
116
- response = SearchFlip::HTTPClient.headers(accept: "application/json", content_type: "application/x-ndjson").put(url, body: @payload, params: ignore_errors ? {} : { filter_path: "errors" })
116
+ response =
117
+ SearchFlip::HTTPClient
118
+ .headers(accept: "application/json", content_type: "application/x-ndjson")
119
+ .put(url, body: @payload, params: ignore_errors ? {} : { filter_path: "errors" })
117
120
 
118
121
  return if options[:raise] == false
119
122
 
@@ -121,13 +124,13 @@ module SearchFlip
121
124
 
122
125
  return unless parsed_response["errors"]
123
126
 
124
- raise(SearchFlip::Bulk::Error, response[0 .. 30]) unless ignore_errors
127
+ raise(SearchFlip::Bulk::Error, response[0..30]) unless ignore_errors
125
128
 
126
129
  parsed_response["items"].each do |item|
127
- item.each do |_, _item|
128
- status = _item["status"]
130
+ item.each do |_, element|
131
+ status = element["status"]
129
132
 
130
- raise(SearchFlip::Bulk::Error, SearchFlip::JSON.generate(_item)) if !status.between?(200, 299) && !ignore_errors.include?(status)
133
+ raise(SearchFlip::Bulk::Error, SearchFlip::JSON.generate(element)) if !status.between?(200, 299) && !ignore_errors.include?(status)
131
134
  end
132
135
  end
133
136
  ensure
@@ -1,16 +1,5 @@
1
1
 
2
2
  module SearchFlip
3
- # Queries and returns the ElasticSearch version used.
4
- #
5
- # @example
6
- # SearchFlip.version # => e.g. 2.4.1
7
- #
8
- # @return [String] The ElasticSearch version
9
-
10
- def self.version
11
- @version ||= SearchFlip::HTTPClient.get("#{Config[:base_url]}/").parse["version"]["number"]
12
- end
13
-
14
3
  Config = {
15
4
  index_prefix: nil,
16
5
  base_url: "http://127.0.0.1:9200",
@@ -0,0 +1,131 @@
1
+
2
+ module SearchFlip
3
+ class Connection
4
+ attr_reader :base_url
5
+
6
+ def initialize(base_url: SearchFlip::Config[:base_url])
7
+ @base_url = base_url
8
+ end
9
+
10
+ # Queries and returns the ElasticSearch version used.
11
+ #
12
+ # @example
13
+ # connection.version # => e.g. 2.4.1
14
+ #
15
+ # @return [String] The ElasticSearch version
16
+
17
+ def version
18
+ @version ||= SearchFlip::HTTPClient.get("#{base_url}/").parse["version"]["number"]
19
+ end
20
+
21
+ # Uses the ElasticSearch Multi Search API to execute multiple search requests
22
+ # within a single request. Raises SearchFlip::ResponseError in case any
23
+ # errors occur.
24
+ #
25
+ # @example
26
+ # connection.msearch [ProductIndex.match_all, CommentIndex.match_all]
27
+ #
28
+ # @param criterias [Array<SearchFlip::Criteria>] An array of search
29
+ # queries to execute in parallel
30
+ #
31
+ # @return [Array<SearchFlip::Response>] An array of responses
32
+
33
+ def msearch(criterias)
34
+ payload = criterias.flat_map do |criteria|
35
+ [
36
+ SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, type: criteria.target.type_name),
37
+ SearchFlip::JSON.generate(criteria.request)
38
+ ]
39
+ end
40
+
41
+ payload = payload.join("\n")
42
+ payload << "\n"
43
+
44
+ raw_response =
45
+ SearchFlip::HTTPClient
46
+ .headers(accept: "application/json", content_type: "application/x-ndjson")
47
+ .post("#{base_url}/_msearch", body: payload)
48
+
49
+ raw_response.parse["responses"].map.with_index do |response, index|
50
+ SearchFlip::Response.new(criterias[index], response)
51
+ end
52
+ end
53
+
54
+ # Used to manipulate, ie add and remove index aliases. Raises an
55
+ # SearchFlip::ResponseError in case any errors occur.
56
+ #
57
+ # @example
58
+ # connection.update_aliases(actions: [
59
+ # { remove: { index: "test1", alias: "alias1" }},
60
+ # { add: { index: "test2", alias: "alias1" }}
61
+ # ])
62
+ #
63
+ # @param payload [Hash] The raw request payload
64
+ #
65
+ # @return [Hash] The raw response
66
+
67
+ def update_aliases(payload)
68
+ SearchFlip::HTTPClient
69
+ .headers(accept: "application/json", content_type: "application/json")
70
+ .post("#{base_url}/_aliases", body: SearchFlip::JSON.generate(payload))
71
+ .parse
72
+ end
73
+
74
+ # Fetches information about the specified index aliases. Raises
75
+ # SearchFlip::ResponseError in case any errors occur.
76
+ #
77
+ # @example
78
+ # connection.get_aliases(alias_name: "some_alias")
79
+ # connection.get_aliases(index_name: "index1,index2")
80
+ #
81
+ # @param alias_name [String] The alias or comma separated list of alias names
82
+ # @param index_name [String] The index or comma separated list of index names
83
+ #
84
+ # @return [Hash] The raw response
85
+
86
+ def get_aliases(index_name: "*", alias_name: "*")
87
+ res = SearchFlip::HTTPClient
88
+ .headers(accept: "application/json", content_type: "application/json")
89
+ .get("#{base_url}/#{index_name}/_alias/#{alias_name}")
90
+ .parse
91
+
92
+ Hashie::Mash.new(res)
93
+ end
94
+
95
+ # Returns whether or not the associated ElasticSearch alias already
96
+ # exists.
97
+ #
98
+ # @example
99
+ # connection.alias_exists?("some_alias")
100
+ #
101
+ # @return [Boolean] Whether or not the alias exists
102
+
103
+ def alias_exists?(alias_name)
104
+ SearchFlip::HTTPClient
105
+ .headers(accept: "application/json", content_type: "application/json")
106
+ .get("#{base_url}/_alias/#{alias_name}")
107
+
108
+ true
109
+ rescue SearchFlip::ResponseError => e
110
+ return false if e.code == 404
111
+
112
+ raise e
113
+ end
114
+
115
+ # Fetches information about the specified indices. Raises
116
+ # SearchFlip::ResponseError in case any errors occur.
117
+ #
118
+ # @example
119
+ # connection.get_indices('prefix*')
120
+ #
121
+ # @return [Array] The raw response
122
+
123
+ def get_indices(name = "*")
124
+ SearchFlip::HTTPClient
125
+ .headers(accept: "application/json", content_type: "application/json")
126
+ .get("#{base_url}/_cat/indices/#{name}")
127
+ .parse
128
+ end
129
+ end
130
+ end
131
+