elasticband 0.1.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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +5 -0
  5. data/.travis.yml +13 -0
  6. data/CODE_OF_CONDUCT.md +13 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +21 -0
  9. data/README.md +34 -0
  10. data/Rakefile +8 -0
  11. data/elasticband.gemspec +27 -0
  12. data/lib/elasticband/aggregation/nested.rb +18 -0
  13. data/lib/elasticband/aggregation/terms.rb +23 -0
  14. data/lib/elasticband/aggregation/top_hits.rb +26 -0
  15. data/lib/elasticband/aggregation.rb +56 -0
  16. data/lib/elasticband/filter/and.rb +24 -0
  17. data/lib/elasticband/filter/not.rb +24 -0
  18. data/lib/elasticband/filter/query.rb +24 -0
  19. data/lib/elasticband/filter/term.rb +23 -0
  20. data/lib/elasticband/filter/terms.rb +23 -0
  21. data/lib/elasticband/filter.rb +13 -0
  22. data/lib/elasticband/query/filtered.rb +25 -0
  23. data/lib/elasticband/query/function_score.rb +44 -0
  24. data/lib/elasticband/query/match.rb +25 -0
  25. data/lib/elasticband/query/multi_match.rb +23 -0
  26. data/lib/elasticband/query/score_function/boost_factor.rb +17 -0
  27. data/lib/elasticband/query/score_function/field_value_factor.rb +24 -0
  28. data/lib/elasticband/query/score_function/filtered.rb +19 -0
  29. data/lib/elasticband/query/score_function/script_score.rb +24 -0
  30. data/lib/elasticband/query/score_function.rb +14 -0
  31. data/lib/elasticband/query.rb +127 -0
  32. data/lib/elasticband/search.rb +18 -0
  33. data/lib/elasticband/version.rb +3 -0
  34. data/lib/elasticband.rb +12 -0
  35. data/spec/aggregation/nested_spec.rb +28 -0
  36. data/spec/aggregation/terms_spec.rb +17 -0
  37. data/spec/aggregation/top_hits_spec.rb +26 -0
  38. data/spec/aggregation_spec.rb +48 -0
  39. data/spec/filter/and_spec.rb +31 -0
  40. data/spec/filter/not_spec.rb +21 -0
  41. data/spec/filter/query_spec.rb +21 -0
  42. data/spec/filter/term_spec.rb +17 -0
  43. data/spec/filter/terms_spec.rb +23 -0
  44. data/spec/filter_spec.rb +9 -0
  45. data/spec/query/filtered_spec.rb +37 -0
  46. data/spec/query/function_score_spec.rb +76 -0
  47. data/spec/query/match_spec.rb +23 -0
  48. data/spec/query/multi_match_spec.rb +29 -0
  49. data/spec/query/score_function/boost_factor_spec.rb +9 -0
  50. data/spec/query/score_function/field_value_factor_spec.rb +17 -0
  51. data/spec/query/score_function/filtered_spec.rb +25 -0
  52. data/spec/query/score_function/script_score_spec.rb +17 -0
  53. data/spec/query/score_function_spec.rb +10 -0
  54. data/spec/query_spec.rb +274 -0
  55. data/spec/search_spec.rb +33 -0
  56. data/spec/spec_helper.rb +94 -0
  57. metadata +185 -0
@@ -0,0 +1,127 @@
1
+ require 'elasticband/query/filtered'
2
+ require 'elasticband/query/function_score'
3
+ require 'elasticband/query/match'
4
+ require 'elasticband/query/multi_match'
5
+ require 'elasticband/query/score_function'
6
+
7
+ module Elasticband
8
+ class Query
9
+ def to_h
10
+ { match_all: {} }
11
+ end
12
+
13
+ class << self
14
+ # Parses a query text with options to a Elasticsearch syntax
15
+ #
16
+ # #### Options
17
+ #
18
+ # * `on:` Defines which attributes will searched in documents
19
+ # * `only:` Filter the search results where the condition is `true`
20
+ # * `except`: Filter the search results where the condition is `false`.
21
+ # * `boost_by:` Boosts the score of a query result based on a attribute of the document.
22
+ # This score will be multiplied for the `boost_by` attribute over function `ln2p`.
23
+ # * `boost_where:` Boosts the score of a query result where some condition is `true`.
24
+ # This score will be multiplied by 1000 (arbitrary, based on gem `searchkick`)
25
+ #
26
+ # #### Examples
27
+ # ```
28
+ # Query.parse('foo')
29
+ # => { match: { _all: 'foo' } }
30
+ #
31
+ # Query.parse('foo', on: :name)
32
+ # => { match: { name: 'foo' } }
33
+ #
34
+ # Query.parse('foo', on: %i(name description))
35
+ # => { multi_match: { query: 'foo', fields: [:name, :description] } }
36
+ #
37
+ # Query.parse('foo', only: { status: :published })
38
+ # => { filtered: { query: ..., filter: { term: { status: :published } } } }
39
+ #
40
+ # Query.parse('foo', except: { company: { id: 1 } })
41
+ # => { filtered: { query: ..., filter: { not: { term: { status: :published } } } } }
42
+ #
43
+ # Query.parse('foo', boost_by: :contents_count)
44
+ # => { function_score: { query: ..., field_value_factor: { field: :contents_count, modifier: :ln2p } } }
45
+ #
46
+ # Query.parse('foo', boost_where: { company: { id: 1 } })
47
+ # => {
48
+ # function_score: {
49
+ # query: ...,
50
+ # functions: [
51
+ # { filter: { term: { 'company.id': 1 } }, boost_factor: 1000 }
52
+ # ]
53
+ # }
54
+ # }
55
+ # ```
56
+ def parse(query_text, options = {})
57
+ query = parse_on(query_text, options[:on])
58
+ query = parse_only_and_except(query, options[:only], options[:except])
59
+ query = parse_boost(query, options[:boost_by], options[:boost_where])
60
+ query.to_h
61
+ end
62
+
63
+ private
64
+
65
+ def to_dotted_notation(hash_params, prefix = nil, dotted_hash = {})
66
+ hash_params.each_with_object(dotted_hash) do |(key, val), hash|
67
+ if val.is_a?(Hash)
68
+ to_dotted_notation(val, "#{prefix}#{key}.", hash)
69
+ else
70
+ hash["#{prefix}#{key}"] = val
71
+ end
72
+ end
73
+ end
74
+
75
+ def parse_on(query_text, on_options)
76
+ if on_options.is_a?(Enumerable)
77
+ Query::MultiMatch.new(query_text, on_options)
78
+ else
79
+ Query::Match.new(query_text, on_options)
80
+ end
81
+ end
82
+
83
+ def parse_only_and_except(query, only_options, except_options)
84
+ return query if only_options.blank? && except_options.blank?
85
+
86
+ filter = parse_filters(only_options) + parse_filters(except_options).map { |f| Filter::Not.new(f) }
87
+ filter = join_filters(filter)
88
+
89
+ Query::Filtered.new(filter, query)
90
+ end
91
+
92
+ def join_filters(filters)
93
+ filters.count > 1 ? Filter::And.new(filters) : filters.first
94
+ end
95
+
96
+ def parse_filters(options)
97
+ return [] if options.blank?
98
+
99
+ to_dotted_notation(options).map { |attribute, value| parse_filter(attribute, value) }
100
+ end
101
+
102
+ def parse_filter(attribute, value)
103
+ if value.is_a?(Enumerable)
104
+ Filter::Terms.new(value, attribute)
105
+ else
106
+ Filter::Term.new(value, attribute)
107
+ end
108
+ end
109
+
110
+ def parse_boost(query, boost_by_options, boost_where_options)
111
+ return query if boost_by_options.blank? && boost_where_options.blank?
112
+
113
+ function = parse_boost_function(boost_by_options, boost_where_options)
114
+ Query::FunctionScore.new(query, function)
115
+ end
116
+
117
+ def parse_boost_function(boost_by_options, boost_where_options)
118
+ if boost_by_options.present?
119
+ ScoreFunction::FieldValueFactor.new(boost_by_options, modifier: :ln2p)
120
+ else
121
+ filter = join_filters(parse_filters(boost_where_options))
122
+ ScoreFunction::Filtered.new(filter, ScoreFunction::BoostFactor.new(1_000))
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,18 @@
1
+ module Elasticband
2
+ class Search
3
+ class << self
4
+ # Parses a query text with options to a Elasticsearch search syntax.
5
+ # See Elasticband::Query.parse and Elasticband::Aggregation.parse options for details.
6
+ #
7
+ # #### Examples
8
+ # ```
9
+ # Search.parse('foo', on: :name, group_by: :status)
10
+ # => { query: { match: { name: 'foo' } }, aggs: { status: { terms: { field: :status } } } }
11
+ # ```
12
+ def parse(query_text, options)
13
+ hash = { query: Query.parse(query_text, options), aggs: Aggregation.parse(options) }
14
+ hash.reject { |_, value| value.blank? }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Elasticband
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/object'
3
+ require 'active_support/core_ext/array'
4
+
5
+ require 'elasticband/aggregation'
6
+ require 'elasticband/filter'
7
+ require 'elasticband/query'
8
+ require 'elasticband/search'
9
+ require 'elasticband/version'
10
+
11
+ module Elasticband
12
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Aggregation::Nested do
4
+ describe '#to_h' do
5
+ subject { described_class.new(root_aggregation, nested_aggregation).to_h }
6
+
7
+ let(:root_aggregation) { Elasticband::Aggregation.new(:root_aggregation) }
8
+ let(:nested_aggregation) { Elasticband::Aggregation.new(:nested_aggregation) }
9
+
10
+ before do
11
+ allow(root_aggregation).to receive(:to_h) { { root_aggregation: { key_1: :value_1 } } }
12
+ allow(nested_aggregation).to receive(:to_h) { { nested_aggregation: { key_2: :value_2 } } }
13
+ end
14
+
15
+ it 'returns a nested aggreagation hash' do
16
+ is_expected.to eq(
17
+ root_aggregation: {
18
+ key_1: :value_1,
19
+ aggs: {
20
+ nested_aggregation: {
21
+ key_2: :value_2
22
+ }
23
+ }
24
+ }
25
+ )
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Aggregation::Terms do
4
+ describe '#to_h' do
5
+ context 'without options' do
6
+ subject { described_class.new(:aggregation_name, :field_name).to_h }
7
+
8
+ it { is_expected.to eq(aggregation_name: { terms: { field: :field_name } }) }
9
+ end
10
+
11
+ context 'with options' do
12
+ subject { described_class.new(:aggregation_name, :field_name, size: 1).to_h }
13
+
14
+ it { is_expected.to eq(aggregation_name: { terms: { field: :field_name, size: 1 } }) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Aggregation::TopHits do
4
+ describe '#to_h' do
5
+ subject { described_class.new(:top_hits_aggregation, root_aggregation, 3).to_h }
6
+
7
+ let(:root_aggregation) { Elasticband::Aggregation.new(:root_aggregation) }
8
+
9
+ before do
10
+ allow(root_aggregation).to receive(:to_h) { { root_aggregation: { terms: { field: :field_name } } } }
11
+ end
12
+
13
+ it 'returns a nested aggreagation hash' do
14
+ is_expected.to eq(
15
+ root_aggregation: {
16
+ terms: { field: :field_name },
17
+ aggs: {
18
+ top_hits_aggregation: {
19
+ top_hits: { size: 3 }
20
+ }
21
+ }
22
+ }
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Aggregation do
4
+ describe '#to_h' do
5
+ context 'without a aggregation_hash' do
6
+ subject { described_class.new(:aggregation_name).to_h }
7
+
8
+ it { is_expected.to eq(aggregation_name: {}) }
9
+ end
10
+
11
+ context 'with a aggregation_hash' do
12
+ subject { described_class.new(:aggregation_name).to_h(key: :value) }
13
+
14
+ it { is_expected.to eq(aggregation_name: { key: :value }) }
15
+ end
16
+ end
17
+
18
+ describe '.parse' do
19
+ subject { described_class.parse(options) }
20
+
21
+ context 'with `:group_by` option' do
22
+ context 'with the field_name' do
23
+ let(:options) { { group_by: :status } }
24
+
25
+ it { is_expected.to eq(status: { terms: { field: :status } }) }
26
+ end
27
+
28
+ context 'with an array with field_name and :top_hits option' do
29
+ let(:options) { { group_by: [:status, top_hits: 3] } }
30
+
31
+ it 'returns the `terms` aggregation with a `top_hits` nested' do
32
+ is_expected.to eq(
33
+ status: {
34
+ terms: { field: :status },
35
+ aggs: { top_status: { top_hits: { size: 3 } } }
36
+ }
37
+ )
38
+ end
39
+ end
40
+
41
+ context 'with an array with field_name and other options' do
42
+ let(:options) { { group_by: [:status, size: 3] } }
43
+
44
+ it { is_expected.to eq(status: { terms: { field: :status, size: 3 } }) }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter::And do
4
+ describe '#to_h' do
5
+ let(:filter_1) { Elasticband::Filter.new }
6
+ let(:filter_2) { Elasticband::Filter.new }
7
+
8
+ before do
9
+ allow(filter_1).to receive(:to_h) { 'filter_1' }
10
+ allow(filter_2).to receive(:to_h) { 'filter_2' }
11
+ end
12
+
13
+ context 'with only one filter' do
14
+ subject { described_class.new(filter_1).to_h }
15
+
16
+ it { is_expected.to eq(and: ['filter_1']) }
17
+ end
18
+
19
+ context 'with multiple filters' do
20
+ subject { described_class.new([filter_1, filter_2]).to_h }
21
+
22
+ it { is_expected.to eq(and: %w(filter_1 filter_2)) }
23
+ end
24
+
25
+ context 'with options' do
26
+ subject { described_class.new(filter_1, _cache: true).to_h }
27
+
28
+ it { is_expected.to eq(and: { filter: ['filter_1'], _cache: true }) }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter::Not do
4
+ describe '#to_h' do
5
+ let(:other_filter) { Elasticband::Filter.new }
6
+
7
+ before { allow(other_filter).to receive(:to_h) { 'other_filter' } }
8
+
9
+ context 'without options' do
10
+ subject { described_class.new(other_filter).to_h }
11
+
12
+ it { is_expected.to eq(not: 'other_filter') }
13
+ end
14
+
15
+ context 'with options' do
16
+ subject { described_class.new(other_filter, _cache: true).to_h }
17
+
18
+ it { is_expected.to eq(not: { filter: 'other_filter', _cache: true }) }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter::Query do
4
+ describe '#to_h' do
5
+ let(:query) { Elasticband::Query.new }
6
+
7
+ before { allow(query).to receive(:to_h) { 'query' } }
8
+
9
+ context 'without options' do
10
+ subject { described_class.new(query).to_h }
11
+
12
+ it { is_expected.to eq(query: 'query') }
13
+ end
14
+
15
+ context 'with options' do
16
+ subject { described_class.new(query, _cache: true).to_h }
17
+
18
+ it { is_expected.to eq(fquery: { query: 'query', _cache: true }) }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter::Term do
4
+ describe '#to_h' do
5
+ context 'without options' do
6
+ subject { described_class.new('filter', :field_name).to_h }
7
+
8
+ it { is_expected.to eq(term: { field_name: 'filter' }) }
9
+ end
10
+
11
+ context 'with options' do
12
+ subject { described_class.new('filter', :field_name, _cache: true).to_h }
13
+
14
+ it { is_expected.to eq(term: { field_name: 'filter', _cache: true }) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter::Terms do
4
+ describe '#to_h' do
5
+ context 'with only one value' do
6
+ subject { described_class.new('filter', :field_name).to_h }
7
+
8
+ it { is_expected.to eq(terms: { field_name: ['filter'] }) }
9
+ end
10
+
11
+ context 'with multiple values' do
12
+ subject { described_class.new(%w(filter_1 filter_2), :field_name).to_h }
13
+
14
+ it { is_expected.to eq(terms: { field_name: %w(filter_1 filter_2) }) }
15
+ end
16
+
17
+ context 'with options' do
18
+ subject { described_class.new('filter', :field_name, _cache: true).to_h }
19
+
20
+ it { is_expected.to eq(terms: { field_name: ['filter'], _cache: true }) }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Filter do
4
+ describe '#to_h' do
5
+ subject { described_class.new.to_h }
6
+
7
+ it { is_expected.to eq(match_all: {}) }
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::Filtered do
4
+ describe '#to_h' do
5
+ let(:other_query) { Elasticband::Query.new }
6
+ let(:filter) { Elasticband::Filter.new }
7
+
8
+ before do
9
+ allow(other_query).to receive(:to_h) { 'query' }
10
+ allow(filter).to receive(:to_h) { 'filter' }
11
+ end
12
+
13
+ context 'with only filter' do
14
+ subject { described_class.new(filter).to_h }
15
+
16
+ it { is_expected.to eq(filtered: { filter: 'filter' }) }
17
+ end
18
+
19
+ context 'with filter and query' do
20
+ subject { described_class.new(filter, other_query).to_h }
21
+
22
+ it { is_expected.to eq(filtered: { query: 'query', filter: 'filter' }) }
23
+ end
24
+
25
+ context 'with filter and options' do
26
+ subject { described_class.new(filter, nil, strategy: :leap_frog).to_h }
27
+
28
+ it { is_expected.to eq(filtered: { filter: 'filter', strategy: :leap_frog }) }
29
+ end
30
+
31
+ context 'with filter, query and options' do
32
+ subject { described_class.new(filter, other_query, strategy: :leap_frog).to_h }
33
+
34
+ it { is_expected.to eq(filtered: { query: 'query', filter: 'filter', strategy: :leap_frog }) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::FunctionScore do
4
+ describe '#to_h' do
5
+ let(:other_query) { Elasticband::Query.new }
6
+ let(:filter) { Elasticband::Filter.new }
7
+ let(:score_function_1) { Elasticband::Query::ScoreFunction.new }
8
+ let(:score_function_2) { Elasticband::Query::ScoreFunction.new }
9
+
10
+ before do
11
+ allow(other_query).to receive(:to_h) { 'query' }
12
+ allow(filter).to receive(:to_h) { 'filter' }
13
+ allow(score_function_1).to receive(:to_h) { { score_function_1: 'score_function_1' } }
14
+ allow(score_function_2).to receive(:to_h) { { score_function_2: 'score_function_2' } }
15
+ end
16
+
17
+ context 'with a single function' do
18
+ context 'with a query' do
19
+ subject { described_class.new(other_query, score_function_1).to_h }
20
+
21
+ it { is_expected.to eq(function_score: { query: 'query', score_function_1: 'score_function_1' }) }
22
+ end
23
+
24
+ context 'with filter' do
25
+ subject { described_class.new(filter, score_function_1).to_h }
26
+
27
+ it { is_expected.to eq(function_score: { filter: 'filter', score_function_1: 'score_function_1' }) }
28
+ end
29
+ end
30
+
31
+ context 'with multiple functions' do
32
+ subject { described_class.new(other_query, [score_function_1, score_function_2]).to_h }
33
+
34
+ it 'returns a hash with the query/filter and an array with the functions' do
35
+ is_expected.to eq(
36
+ function_score: {
37
+ query: 'query',
38
+ functions: [
39
+ { score_function_1: 'score_function_1' },
40
+ { score_function_2: 'score_function_2' }
41
+ ]
42
+ }
43
+ )
44
+ end
45
+ end
46
+
47
+ context 'with options' do
48
+ subject { described_class.new(other_query, score_function_1, boost: 1.2).to_h }
49
+
50
+ it 'returns a hash with the query, function and options' do
51
+ is_expected.to eq(
52
+ function_score: {
53
+ query: 'query',
54
+ score_function_1: 'score_function_1',
55
+ boost: 1.2
56
+ }
57
+ )
58
+ end
59
+ end
60
+
61
+ context 'with a filtered function' do
62
+ let(:filtered_function) { Elasticband::Query::ScoreFunction::Filtered.new(filter, score_function_1) }
63
+
64
+ subject { described_class.new(other_query, filtered_function).to_h }
65
+
66
+ it 'returns a hash with the query/filter and an array with the function' do
67
+ is_expected.to eq(
68
+ function_score: {
69
+ query: 'query',
70
+ functions: [{ filter: 'filter', score_function_1: 'score_function_1' }]
71
+ }
72
+ )
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::Match do
4
+ describe '#to_h' do
5
+ context 'without field' do
6
+ subject { described_class.new('q').to_h }
7
+
8
+ it { is_expected.to eq(match: { _all: 'q' }) }
9
+ end
10
+
11
+ context 'with a field and no options' do
12
+ subject { described_class.new('q', :field_name).to_h }
13
+
14
+ it { is_expected.to eq(match: { field_name: 'q' }) }
15
+ end
16
+
17
+ context 'with options' do
18
+ subject { described_class.new('q', :field_name, operator: :and).to_h }
19
+
20
+ it { is_expected.to eq(match: { field_name: { query: 'q', operator: :and } }) }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::MultiMatch do
4
+ describe '#to_h' do
5
+ context 'without field' do
6
+ subject { described_class.new('q').to_h }
7
+
8
+ it { is_expected.to eq(multi_match: { query: 'q', fields: [:_all] }) }
9
+ end
10
+
11
+ context 'with a single field' do
12
+ subject { described_class.new('q', :field_name).to_h }
13
+
14
+ it { is_expected.to eq(multi_match: { query: 'q', fields: [:field_name] }) }
15
+ end
16
+
17
+ context 'with multiple fields' do
18
+ subject { described_class.new('q', [:field_name_1, :field_name_2]).to_h }
19
+
20
+ it { is_expected.to eq(multi_match: { query: 'q', fields: [:field_name_1, :field_name_2] }) }
21
+ end
22
+
23
+ context 'with options' do
24
+ subject { described_class.new('q', :field_name, operator: :and).to_h }
25
+
26
+ it { is_expected.to eq(multi_match: { query: 'q', operator: :and, fields: [:field_name] }) }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::ScoreFunction::BoostFactor do
4
+ describe '#to_h' do
5
+ subject { described_class.new(1.2).to_h }
6
+
7
+ it { is_expected.to eq(boost_factor: 1.2) }
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::ScoreFunction::FieldValueFactor do
4
+ describe '#to_h' do
5
+ context 'without options' do
6
+ subject { described_class.new(:field_name).to_h }
7
+
8
+ it { is_expected.to eq(field_value_factor: { field: :field_name }) }
9
+ end
10
+
11
+ context 'with options' do
12
+ subject { described_class.new(:field_name, factor: 1.2).to_h }
13
+
14
+ it { is_expected.to eq(field_value_factor: { field: :field_name, factor: 1.2 }) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::ScoreFunction::Filtered do
4
+ describe '#to_h' do
5
+ let(:other_score_function) { Elasticband::Query::ScoreFunction.new }
6
+ let(:filter) { Elasticband::Filter.new }
7
+
8
+ before do
9
+ allow(other_score_function).to receive(:to_h) { { function_name: 'score_function' } }
10
+ allow(filter).to receive(:to_h) { 'filter' }
11
+ end
12
+
13
+ context 'without options' do
14
+ subject { described_class.new(filter, other_score_function).to_h }
15
+
16
+ it { is_expected.to eq(filter: 'filter', function_name: 'score_function') }
17
+ end
18
+
19
+ context 'with options' do
20
+ subject { described_class.new(filter, other_score_function, weight: 2).to_h }
21
+
22
+ it { is_expected.to eq(filter: 'filter', function_name: 'score_function', weight: 2) }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::ScoreFunction::ScriptScore do
4
+ describe '#to_h' do
5
+ context 'without options' do
6
+ subject { described_class.new('script').to_h }
7
+
8
+ it { is_expected.to eq(script_score: { script: 'script' }) }
9
+ end
10
+
11
+ context 'with options' do
12
+ subject { described_class.new('script', params: { param_1: :value_1 }).to_h }
13
+
14
+ it { is_expected.to eq(script_score: { script: 'script', params: { param_1: :value_1 } }) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Elasticband::Query::ScoreFunction do
4
+ describe '#to_h' do
5
+ subject { described_class.new.to_h }
6
+
7
+ it { is_expected.to be_a(Hash) }
8
+ it { is_expected.to be_empty }
9
+ end
10
+ end