elasticband 0.5.0 → 0.6.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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MWU0ZWNlNDdlMmZkM2YzMjBmOTc0MjNlMmZiNWM5MjdiMjY3MDM5NQ==
4
+ YWVjNzAzOTVlY2JlZGI0NmE3ZTRjOTYzNDNjNWUwMzQzZGVmODVkZg==
5
5
  data.tar.gz: !binary |-
6
- ZjI5MGQ5ZGY1MWEwM2IwMGQzODQ0NTBlYTlkZDliZDIzNDk1ZDVlNg==
6
+ MzJkYTMzMzQ5ODE5NWUyMDEyYTViNjg3OWYzNWIyMWE5MmRjMjg3MQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OTk5YjUyZjBkNDNiZTI4MzI3MDI0N2YyNDJiNmU2NWZkNjg1ZDRlYzk1YjFj
10
- NWY4MDliM2M0OThkYzMzNzVkNmE2MTg3N2NlZGMxY2Q4OTZlNjYyZGJlNWQ2
11
- Yzk0MmU2NDQ1NjM0ZGIwMjIwYzEzNjA0NWI4MGEyZGZhOWM0YjU=
9
+ NjRlM2EwMWIyYzU0YzViYjM3OWNjNjRmNjg0M2ZhY2U4OGM3OWU0ZjYzM2Iy
10
+ MjcyZDE4N2IwMTEyZTVmNDlhMDQ2NDE5MDk3MDg0M2M4ZWE2NjYwYzAzYWMy
11
+ YTRmNjU2NTE5Y2Y2ZmY2NmQxN2Y5NWJiOGQ1NWY5MmY1OTVhNjg=
12
12
  data.tar.gz: !binary |-
13
- YjcxNjU3OGFjMGVkZDZjNGM5OGQ1ZWUxNjQ5MDlmYjY3NDVjNDdkYWQzMDU0
14
- YjZkYTg2NmQ5MmFkMDkxOTE3NWYxMDc0NDZiYjk0NTM3OTAwYjE0ZDA4NTcw
15
- NzYwODE5MjlmYzdmMDE4NTJmNmQ0N2I2YmY1NjE1OGJlZjNiMmU=
13
+ ZGQxMjNiYzhjZjUxODdkMThmYjY3YTJkMDE3YmI1ZGE2MTU3NzYyMzQ0NDNi
14
+ M2Q2ZjE2YTBlODY4YzNkNWZhOTU3NmY5ZDcxNmRhZmY2YmRjZGM1ZTcxYzM5
15
+ NmNlZGIwODI0NmQ4NDU3MjFmYjBkYjFmMWYzNDRhN2UzYzM4M2I=
@@ -3,3 +3,6 @@ Style/Documentation:
3
3
 
4
4
  Metrics/LineLength:
5
5
  Max: 110
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 2.0
@@ -7,7 +7,7 @@ require 'elasticband/aggregation/top_hits'
7
7
 
8
8
  module Elasticband
9
9
  class Aggregation
10
- PARSE_AGGREGATIONS = %i(group_by group_max top_hits group_by_filter)
10
+ PARSE_AGGREGATIONS = %i(group_by group_max top_hits group_by_filter).freeze
11
11
 
12
12
  attr_accessor :name
13
13
 
@@ -14,7 +14,7 @@ module Elasticband
14
14
  end
15
15
 
16
16
  def type
17
- fail NotImplementedError
17
+ raise NotImplementedError
18
18
  end
19
19
 
20
20
  private
@@ -1,4 +1,5 @@
1
1
  require 'elasticband/filter/and'
2
+ require 'elasticband/filter/exists'
2
3
  require 'elasticband/filter/not'
3
4
  require 'elasticband/filter/query'
4
5
  require 'elasticband/filter/range'
@@ -9,117 +10,127 @@ require 'elasticband/filter/script'
9
10
 
10
11
  module Elasticband
11
12
  class Filter
12
- PARSE_FILTERS = %i(only except includes range near)
13
+ PARSE_FILTERS = %i(only except exists includes range near).freeze
14
+
15
+ attr_reader :options
16
+
17
+ # Parses filter options to a Elasticsearch syntax
18
+ #
19
+ # #### Options
20
+ #
21
+ # * `only:` Filter the search results where the condition is `true`
22
+ # * `except`: Filter the search results where the condition is `false`.
23
+ # * `includes:` Filter the search results with a `Match` query.
24
+ # * `range:` Filter the search results where the condition is on the given range.
25
+ # * `near:` Filter the search results where the results are near a geo point.
26
+ # * `script:` Filter the search results where the results match the script.
27
+ #
28
+ # #### Examples
29
+ # ```
30
+ # Filter.parse(only: { status: :published })
31
+ # => { term: { status: :published } }
32
+ #
33
+ # Filter.parse(exists: :status)
34
+ # => { exists: { field: :status } }
35
+ #
36
+ # Filter.parse(except: { company: { id: 1 } })
37
+ # => { not: { term: { status: :published } } }
38
+ #
39
+ # Filter.parse(includes: ['company', on: :description])
40
+ # => { query: { match: { description: 'company' } } }
41
+ #
42
+ # Filter.parse(range: { companies_count: { gt: 1, gteq: 1, lt: 1, lteq: 1 } })
43
+ # => { range: { companies_count: { gt: 1, gte: 1, lt: 1, lte: 1 } } }
44
+ #
45
+ # Filter.parse(near: { latitude: 12, longitude: 34, distance: '5km', type: :arc })
46
+ # => { geo_distance: { location: { lat: 12, lon: 34 } }, distance: '5km', distance_type: :arc }
47
+ #
48
+ # Filter.parse(script: ['(param1 + param2) > 0', param1: 1, param2: 1])
49
+ # => { script: { script: '(param1 + param2) > 0', params: { param1: 1, param2: 1 } } }
50
+ # ```
51
+
52
+ def self.parse(options = {})
53
+ new(options).parse
54
+ end
55
+
56
+ def initialize(options = {})
57
+ @options = options
58
+ end
59
+
60
+ def parse
61
+ return {} if options.blank?
62
+
63
+ join_filters(filters).to_h
64
+ end
13
65
 
14
66
  def to_h
15
67
  { match_all: {} }
16
68
  end
17
69
 
18
- class << self
19
- # Parses filter options to a Elasticsearch syntax
20
- #
21
- # #### Options
22
- #
23
- # * `only:` Filter the search results where the condition is `true`
24
- # * `except`: Filter the search results where the condition is `false`.
25
- # * `includes:` Filter the search results with a `Match` query.
26
- # * `range:` Filter the search results where the condition is on the given range.
27
- # * `near:` Filter the search results where the results are near a geo point.
28
- # * `script:` Filter the search results where the results match the script.
29
- #
30
- # #### Examples
31
- # ```
32
- # Filter.parse(only: { status: :published })
33
- # => { term: { status: :published } }
34
- #
35
- # Filter.parse(except: { company: { id: 1 } })
36
- # => { not: { term: { status: :published } } }
37
- #
38
- # Filter.parse(includes: ['company', on: :description])
39
- # => { query: { match: { description: 'company' } } }
40
- #
41
- # Filter.parse(range: { companies_count: { gt: 1, gteq: 1, lt: 1, lteq: 1 } })
42
- # => { range: { companies_count: { gt: 1, gte: 1, lt: 1, lte: 1 } } }
43
- #
44
- # Filter.parse(near: { latitude: 12, longitude: 34, distance: '5km', type: :arc })
45
- # => { geo_distance: { location: { lat: 12, lon: 34 } }, distance: '5km', distance_type: :arc }
46
- #
47
- # Filter.parse(script: ['(param1 + param2) > 0', param1: 1, param2: 1])
48
- # => { script: { script: '(param1 + param2) > 0', params: { param1: 1, param2: 1 } } }
49
- # ```
50
- def parse(options = {})
51
- return {} if options.blank?
52
-
53
- filters = map_filters(options)
54
-
55
- join_filters(filters).to_h
56
- end
70
+ private
57
71
 
58
- private
72
+ def filters
73
+ only_and_except_filters + exists_filter + includes_filter + range_filter + near_filter + script_filter
74
+ end
59
75
 
60
- def map_filters(options)
61
- filters = only_and_except_filters(options[:only], options[:except])
62
- filters += includes_filter(options[:includes])
63
- filters += range_filter(options[:range])
64
- filters += near_filter(options[:near])
65
- filters += script_filter(options[:script])
76
+ def only_and_except_filters
77
+ parse_filters(options[:only]) + parse_filters(options[:except]).map { |f| Filter::Not.new(f) }
78
+ end
66
79
 
67
- filters
68
- end
80
+ def exists_filter
81
+ return [] if options[:exists].blank?
69
82
 
70
- def only_and_except_filters(only_options, except_options)
71
- parse_filters(only_options) + parse_filters(except_options).map { |f| Filter::Not.new(f) }
72
- end
83
+ [Filter::Exists.new(options[:exists])]
84
+ end
73
85
 
74
- def join_filters(filters)
75
- filters.count > 1 ? Filter::And.new(filters) : filters.first
76
- end
86
+ def includes_filter
87
+ return [] if options[:includes].blank?
88
+
89
+ [Filter::Query.new(Elasticband::Query.parse(*options[:includes]))]
90
+ end
77
91
 
78
- def includes_filter(includes_options)
79
- return [] if includes_options.blank?
92
+ def range_filter
93
+ return [] if options[:range].blank?
80
94
 
81
- [Filter::Query.new(Elasticband::Query.parse(*includes_options))]
82
- end
95
+ [Filter::Range.new(options[:range].keys.first, options[:range].values.first)]
96
+ end
83
97
 
84
- def range_filter(range_options)
85
- return [] if range_options.blank?
98
+ def join_filters(filters)
99
+ filters.count > 1 ? Filter::And.new(filters) : filters.first
100
+ end
86
101
 
87
- [Filter::Range.new(range_options.keys.first, range_options.values.first)]
88
- end
102
+ def near_filter
103
+ return [] if options[:near].blank?
89
104
 
90
- def near_filter(options)
91
- return [] if options.blank?
105
+ [Filter::Near.new(options[:near])]
106
+ end
92
107
 
93
- [Filter::Near.new(options)]
94
- end
108
+ def script_filter
109
+ return [] if options[:script].blank?
95
110
 
96
- def script_filter(script_options)
97
- return [] if script_options.blank?
111
+ [Filter::Script.new(*options[:script])]
112
+ end
98
113
 
99
- [Filter::Script.new(*script_options)]
100
- end
114
+ def parse_filters(filter)
115
+ return [] if filter.blank?
101
116
 
102
- def parse_filters(options)
103
- return [] if options.blank?
117
+ to_dotted_notation(filter).map { |attribute, value| parse_filter(attribute, value) }
118
+ end
104
119
 
105
- to_dotted_notation(options).map { |attribute, value| parse_filter(attribute, value) }
120
+ def parse_filter(attribute, value)
121
+ if value.is_a?(Enumerable)
122
+ Filter::Terms.new(value, attribute)
123
+ else
124
+ Filter::Term.new(value, attribute)
106
125
  end
126
+ end
107
127
 
108
- def parse_filter(attribute, value)
109
- if value.is_a?(Enumerable)
110
- Filter::Terms.new(value, attribute)
128
+ def to_dotted_notation(hash_params, prefix = nil, dotted_hash = {})
129
+ hash_params.each_with_object(dotted_hash) do |(key, val), hash|
130
+ if val.is_a?(Hash)
131
+ to_dotted_notation(val, "#{prefix}#{key}.", hash)
111
132
  else
112
- Filter::Term.new(value, attribute)
113
- end
114
- end
115
-
116
- def to_dotted_notation(hash_params, prefix = nil, dotted_hash = {})
117
- hash_params.each_with_object(dotted_hash) do |(key, val), hash|
118
- if val.is_a?(Hash)
119
- to_dotted_notation(val, "#{prefix}#{key}.", hash)
120
- else
121
- hash["#{prefix}#{key}"] = val
122
- end
133
+ hash["#{prefix}#{key}"] = val
123
134
  end
124
135
  end
125
136
  end
@@ -0,0 +1,15 @@
1
+ module Elasticband
2
+ class Filter
3
+ class Exists < Filter
4
+ attr_accessor :field
5
+
6
+ def initialize(field)
7
+ self.field = field.to_sym
8
+ end
9
+
10
+ def to_h
11
+ { exists: { field: field } }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,7 @@ module Elasticband
3
3
  class Range < Filter
4
4
  attr_accessor :field, :ranges
5
5
 
6
- RANGES = %i(gt gteq lt lteq)
6
+ RANGES = %i(gt gteq lt lteq).freeze
7
7
 
8
8
  def initialize(field, ranges)
9
9
  self.field = field.to_sym
@@ -4,7 +4,7 @@ module Elasticband
4
4
  class BoostMode < ScoreFunction
5
5
  attr_accessor :mode
6
6
 
7
- MODES = %i(multiply replace sum avg max min)
7
+ MODES = %i(multiply replace sum avg max min).freeze
8
8
 
9
9
  def initialize(mode = nil)
10
10
  self.mode = mode
@@ -4,7 +4,7 @@ module Elasticband
4
4
  class ScoreMode < ScoreFunction
5
5
  attr_accessor :mode
6
6
 
7
- MODES = %i(multiply mult sum avg first max min)
7
+ MODES = %i(multiply mult sum avg first max min).freeze
8
8
 
9
9
  def initialize(mode = nil)
10
10
  self.mode = mode
@@ -1,3 +1,3 @@
1
1
  module Elasticband
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'.freeze
3
3
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter::Exists do
4
+ describe '#to_h' do
5
+ subject { described_class.new('user').to_h }
6
+
7
+ it { is_expected.to eq(exists: { field: :user }) }
8
+ end
9
+ end
@@ -27,7 +27,7 @@ RSpec.describe Elasticband::Filter do
27
27
  context 'with a nested attribute' do
28
28
  let(:options) { { only: { company: { id: 1 } } } }
29
29
 
30
- it { is_expected.to eq(term: { 'company.id': 1 }) }
30
+ it { is_expected.to eq(term: { :'company.id' => 1 }) }
31
31
  end
32
32
 
33
33
  context 'with multiple values' do
@@ -57,7 +57,7 @@ RSpec.describe Elasticband::Filter do
57
57
  context 'with a nested attribute' do
58
58
  let(:options) { { except: { company: { id: 1 } } } }
59
59
 
60
- it { is_expected.to eq(not: { term: { 'company.id': 1 } }) }
60
+ it { is_expected.to eq(not: { term: { :'company.id' => 1 } }) }
61
61
  end
62
62
 
63
63
  context 'with multiple values' do
@@ -107,6 +107,13 @@ RSpec.describe Elasticband::Filter do
107
107
  it { is_expected.to eq(filter) }
108
108
  end
109
109
 
110
+ context 'with `:exists` option' do
111
+ let(:options) { { exists: :user } }
112
+ let(:filter) { { exists: { field: :user } } }
113
+
114
+ it { is_expected.to eq(filter) }
115
+ end
116
+
110
117
  context 'with `:script` option' do
111
118
  let(:options) { { script: ['(param1 + param2) > 0', param1: 1, param2: 1] } }
112
119
  let(:filter) { { script: { script: '(param1 + param2) > 0', params: { param1: 1, param2: 1 } } } }
@@ -39,7 +39,7 @@ RSpec.describe Elasticband::Query do
39
39
  context 'with a composed name on `:on` option' do
40
40
  let(:options) { { on: 'company.name' } }
41
41
 
42
- it { is_expected.to eq(match: { 'company.name': 'foo' }) }
42
+ it { is_expected.to eq(match: { :'company.name' => 'foo' }) }
43
43
  end
44
44
  end
45
45
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticband
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Glauber Campinho
@@ -123,6 +123,7 @@ files:
123
123
  - lib/elasticband/aggregation/top_hits.rb
124
124
  - lib/elasticband/filter.rb
125
125
  - lib/elasticband/filter/and.rb
126
+ - lib/elasticband/filter/exists.rb
126
127
  - lib/elasticband/filter/near.rb
127
128
  - lib/elasticband/filter/not.rb
128
129
  - lib/elasticband/filter/query.rb
@@ -155,6 +156,7 @@ files:
155
156
  - spec/aggregation/top_hits_spec.rb
156
157
  - spec/aggregation_spec.rb
157
158
  - spec/filter/and_spec.rb
159
+ - spec/filter/exists_spec.rb
158
160
  - spec/filter/near_spec.rb
159
161
  - spec/filter/not_spec.rb
160
162
  - spec/filter/query_spec.rb