elasticband 0.1.3 → 0.1.4
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 +8 -8
- data/lib/elasticband/aggregation/filter.rb +23 -0
- data/lib/elasticband/aggregation.rb +18 -2
- data/lib/elasticband/filter.rb +68 -0
- data/lib/elasticband/query.rb +7 -48
- data/lib/elasticband/version.rb +1 -1
- data/spec/aggregation/filter_spec.rb +21 -0
- data/spec/aggregation_spec.rb +9 -0
- data/spec/filter_spec.rb +78 -0
- data/spec/query_spec.rb +10 -212
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZDIyZmVkODQxNTU1MzJkODExOTU3YjQ1MGNhNGVhNDc2YmU4MWZmNw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NmRkZWU0ZWQwOGQxYWZlNzEwZmZlZjU1YjRhZWZiYTVlNWJjYTllNA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDE5ZjIzNTFkMTM0MWNiOTE3NzJjYmVmYzJiNTQ5OWU4YWEwZjI2YTZjMzM3
|
10
|
+
ODVmNzdmODU2YmVhYzk3N2ZjMzNhNTY5MTAxNTg4ODg4Mjg0NDNmYmI3NzJl
|
11
|
+
MDc3ZGMwMGMxMDZmYWQ4NzIzMjYwMTk3Y2JmZjg4MWFhY2E0Zjg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2Q4NjNmOTJhODc4MTZiYTc2ZTMzZWJhYjBiODllNjg1OGQ4MmVlMDVjZTky
|
14
|
+
NmNmMzMyM2JhOWUxMWFjNWFiMGQ3YjI4ZWU1N2IxMzY5NWUxODA1ZmM3MDg4
|
15
|
+
ZWQzNTBiNDZiMjRiOWZhMTAzOThjNDIzMTU4NDAxMWI0NTkxYzE=
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Elasticband
|
2
|
+
class Aggregation
|
3
|
+
class Filter < Aggregation
|
4
|
+
attr_accessor :filter, :options
|
5
|
+
|
6
|
+
def initialize(name, filter, options = {})
|
7
|
+
super(name)
|
8
|
+
self.filter = filter
|
9
|
+
self.options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
super(aggregation_hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def aggregation_hash
|
19
|
+
{ filter: filter.to_h }.merge!(options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'elasticband/aggregation/field_based'
|
2
|
+
require 'elasticband/aggregation/filter'
|
2
3
|
require 'elasticband/aggregation/max'
|
3
4
|
require 'elasticband/aggregation/nested'
|
4
5
|
require 'elasticband/aggregation/terms'
|
@@ -6,6 +7,8 @@ require 'elasticband/aggregation/top_hits'
|
|
6
7
|
|
7
8
|
module Elasticband
|
8
9
|
class Aggregation
|
10
|
+
PARSE_AGGREGATIONS = %i(group_by group_max top_hits group_by_filter)
|
11
|
+
|
9
12
|
attr_accessor :name
|
10
13
|
|
11
14
|
def initialize(name)
|
@@ -17,8 +20,6 @@ module Elasticband
|
|
17
20
|
end
|
18
21
|
|
19
22
|
class << self
|
20
|
-
PARSE_AGGREGATIONS = %i(group_by group_max top_hits)
|
21
|
-
|
22
23
|
# Parses some options to a Elasticsearch syntax, aggregations can be nested in another.
|
23
24
|
#
|
24
25
|
# #### Options
|
@@ -31,6 +32,7 @@ module Elasticband
|
|
31
32
|
# It can receive an array with some attributes:
|
32
33
|
# * `script:` Generates max defined by the script
|
33
34
|
# * `top_hits:` A number of results that should be return inside the group ranked by score.
|
35
|
+
# * `group_by_filter:` Filter and group results with a name using Elasticband::Filter.parse options
|
34
36
|
#
|
35
37
|
# #### Examples
|
36
38
|
# ```
|
@@ -42,6 +44,10 @@ module Elasticband
|
|
42
44
|
#
|
43
45
|
# Aggregation.parse(group_by: [:status, size: 5, top_hits: 3])
|
44
46
|
# => { status: { terms: { field: :status, size: 5 }, aggs: { top_status: { top_hits: 3 } } } }
|
47
|
+
#
|
48
|
+
# Aggregation.parse(group_by_filter: [:published_results, only: { status: :published }])
|
49
|
+
# => { published_results: { filter: { term: { status: :published } } } }
|
50
|
+
# ```
|
45
51
|
def parse(options)
|
46
52
|
merge(*parse_aggregations(options))
|
47
53
|
end
|
@@ -69,6 +75,7 @@ module Elasticband
|
|
69
75
|
when :group_by then parse_field_aggregation(Aggregation::Terms, :by, aggregation_options)
|
70
76
|
when :group_max then parse_field_aggregation(Aggregation::Max, :max, aggregation_options)
|
71
77
|
when :top_hits then parse_top_hits(root_aggregation, aggregation_options)
|
78
|
+
when :group_by_filter then parse_filter(*aggregation_options)
|
72
79
|
end
|
73
80
|
end
|
74
81
|
|
@@ -93,6 +100,15 @@ module Elasticband
|
|
93
100
|
aggregation = Aggregation::TopHits.new(name, size, options.except(*PARSE_AGGREGATIONS))
|
94
101
|
parse_aggregations(options, aggregation)
|
95
102
|
end
|
103
|
+
|
104
|
+
def parse_filter(aggregation_name, options)
|
105
|
+
return {} if options.blank?
|
106
|
+
|
107
|
+
filter = Elasticband::Filter.parse(options)
|
108
|
+
filter_options = options.except(*(PARSE_AGGREGATIONS + Elasticband::Filter::PARSE_FILTERS))
|
109
|
+
aggregation = Aggregation::Filter.new(aggregation_name, filter, filter_options)
|
110
|
+
parse_aggregations(options, aggregation)
|
111
|
+
end
|
96
112
|
end
|
97
113
|
end
|
98
114
|
end
|
data/lib/elasticband/filter.rb
CHANGED
@@ -6,8 +6,76 @@ require 'elasticband/filter/terms'
|
|
6
6
|
|
7
7
|
module Elasticband
|
8
8
|
class Filter
|
9
|
+
PARSE_FILTERS = %i(only except includes)
|
10
|
+
|
9
11
|
def to_h
|
10
12
|
{ match_all: {} }
|
11
13
|
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Parses filter options to a Elasticsearch syntax
|
17
|
+
#
|
18
|
+
# #### Options
|
19
|
+
#
|
20
|
+
# * `only:` Filter the search results where the condition is `true`
|
21
|
+
# * `except`: Filter the search results where the condition is `false`.
|
22
|
+
# * `includes:` Filter the search results with a `Match` query.
|
23
|
+
#
|
24
|
+
# #### Examples
|
25
|
+
# ```
|
26
|
+
# Filter.parse(only: { status: :published })
|
27
|
+
# => { term: { status: :published } }
|
28
|
+
#
|
29
|
+
# Filter.parse(except: { company: { id: 1 } })
|
30
|
+
# => { not: { term: { status: :published } } }
|
31
|
+
#
|
32
|
+
# Filter.parse(includes: ['company', on: :description])
|
33
|
+
# => { query: { match: { description: 'company' } } }
|
34
|
+
# ```
|
35
|
+
def parse(options = {})
|
36
|
+
return {} if options.blank?
|
37
|
+
|
38
|
+
filter = parse_filters(options[:only])
|
39
|
+
filter += parse_filters(options[:except]).map { |f| Filter::Not.new(f) }
|
40
|
+
filter += parse_includes_filter(options[:includes])
|
41
|
+
join_filters(filter).to_h
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def join_filters(filters)
|
47
|
+
filters.count > 1 ? Filter::And.new(filters) : filters.first
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_includes_filter(includes_options)
|
51
|
+
return [] if includes_options.blank?
|
52
|
+
|
53
|
+
[Filter::Query.new(Elasticband::Query.parse(*includes_options))]
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_filters(options)
|
57
|
+
return [] if options.blank?
|
58
|
+
|
59
|
+
to_dotted_notation(options).map { |attribute, value| parse_filter(attribute, value) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_filter(attribute, value)
|
63
|
+
if value.is_a?(Enumerable)
|
64
|
+
Filter::Terms.new(value, attribute)
|
65
|
+
else
|
66
|
+
Filter::Term.new(value, attribute)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_dotted_notation(hash_params, prefix = nil, dotted_hash = {})
|
71
|
+
hash_params.each_with_object(dotted_hash) do |(key, val), hash|
|
72
|
+
if val.is_a?(Hash)
|
73
|
+
to_dotted_notation(val, "#{prefix}#{key}.", hash)
|
74
|
+
else
|
75
|
+
hash["#{prefix}#{key}"] = val
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
12
80
|
end
|
13
81
|
end
|
data/lib/elasticband/query.rb
CHANGED
@@ -11,14 +11,12 @@ module Elasticband
|
|
11
11
|
end
|
12
12
|
|
13
13
|
class << self
|
14
|
-
# Parses a query text with options to a Elasticsearch syntax
|
14
|
+
# Parses a query text with options to a Elasticsearch syntax.
|
15
|
+
# Check Elasticband::Filter.parse for filter options.
|
15
16
|
#
|
16
17
|
# #### Options
|
17
18
|
#
|
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
|
-
# * `includes:` Filter the search results with a `Match` query.
|
19
|
+
# * `on:` Defines which attributes will searched in documents.
|
22
20
|
# * `boost_by:` Boosts the score of a query result based on a attribute of the document.
|
23
21
|
# This score will be multiplied for the `boost_by` attribute over function `ln2p`.
|
24
22
|
# * `boost_where:` Boosts the score of a query result where some condition is `true`.
|
@@ -63,23 +61,13 @@ module Elasticband
|
|
63
61
|
# ```
|
64
62
|
def parse(query_text, options = {})
|
65
63
|
query = parse_on(query_text, options[:on])
|
66
|
-
query = parse_query_filters(query, options
|
64
|
+
query = parse_query_filters(query, options)
|
67
65
|
query = parse_boost(query, options.slice(:boost_by, :boost_where, :boost_function))
|
68
66
|
query.to_h
|
69
67
|
end
|
70
68
|
|
71
69
|
private
|
72
70
|
|
73
|
-
def to_dotted_notation(hash_params, prefix = nil, dotted_hash = {})
|
74
|
-
hash_params.each_with_object(dotted_hash) do |(key, val), hash|
|
75
|
-
if val.is_a?(Hash)
|
76
|
-
to_dotted_notation(val, "#{prefix}#{key}.", hash)
|
77
|
-
else
|
78
|
-
hash["#{prefix}#{key}"] = val
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
71
|
def parse_on(query_text, on_options)
|
84
72
|
return Query.new if query_text.blank?
|
85
73
|
|
@@ -91,38 +79,9 @@ module Elasticband
|
|
91
79
|
end
|
92
80
|
|
93
81
|
def parse_query_filters(query, filter_options)
|
94
|
-
|
95
|
-
|
96
|
-
filter = parse_filters(filter_options[:only])
|
97
|
-
filter += parse_filters(filter_options[:except]).map { |f| Filter::Not.new(f) }
|
98
|
-
filter += parse_includes_filter(filter_options[:includes])
|
99
|
-
filter = join_filters(filter)
|
100
|
-
|
101
|
-
Query::Filtered.new(filter, query)
|
102
|
-
end
|
103
|
-
|
104
|
-
def join_filters(filters)
|
105
|
-
filters.count > 1 ? Filter::And.new(filters) : filters.first
|
106
|
-
end
|
82
|
+
filter = Filter.parse(filter_options)
|
107
83
|
|
108
|
-
|
109
|
-
return [] if includes_options.blank?
|
110
|
-
|
111
|
-
[Filter::Query.new(parse(*includes_options))]
|
112
|
-
end
|
113
|
-
|
114
|
-
def parse_filters(options)
|
115
|
-
return [] if options.blank?
|
116
|
-
|
117
|
-
to_dotted_notation(options).map { |attribute, value| parse_filter(attribute, value) }
|
118
|
-
end
|
119
|
-
|
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)
|
125
|
-
end
|
84
|
+
filter.blank? ? query : Query::Filtered.new(filter, query)
|
126
85
|
end
|
127
86
|
|
128
87
|
def parse_boost(query, boost_options)
|
@@ -136,7 +95,7 @@ module Elasticband
|
|
136
95
|
if boost_options[:boost_by].present?
|
137
96
|
ScoreFunction::FieldValueFactor.new(boost_options[:boost_by], modifier: :ln2p)
|
138
97
|
elsif boost_options[:boost_where].present?
|
139
|
-
filter =
|
98
|
+
filter = Filter.parse(only: boost_options[:boost_where])
|
140
99
|
ScoreFunction::Filtered.new(filter, ScoreFunction::BoostFactor.new(1_000))
|
141
100
|
else
|
142
101
|
boost_function, boost_function_params = boost_options[:boost_function]
|
data/lib/elasticband/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Elasticband::Aggregation::Filter do
|
4
|
+
describe '#to_h' do
|
5
|
+
let(:filter) { Elasticband::Filter.new }
|
6
|
+
|
7
|
+
before { allow(filter).to receive(:to_h) { 'filter' } }
|
8
|
+
|
9
|
+
context 'without options' do
|
10
|
+
subject { described_class.new(:aggregation_name, filter).to_h }
|
11
|
+
|
12
|
+
it { is_expected.to eq(aggregation_name: { filter: 'filter' }) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with options' do
|
16
|
+
subject { described_class.new(:aggregation_name, filter, _cache: true).to_h }
|
17
|
+
|
18
|
+
it { is_expected.to eq(aggregation_name: { filter: 'filter', _cache: true }) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/aggregation_spec.rb
CHANGED
@@ -65,6 +65,15 @@ RSpec.describe Elasticband::Aggregation do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
context 'with `:group_by_filter` option' do
|
69
|
+
let(:options) { { group_by_filter: [:published_results, filter_options] } }
|
70
|
+
let(:filter_options) { { only: { status: :published } } }
|
71
|
+
|
72
|
+
before { expect(Elasticband::Filter).to receive(:parse).with(filter_options) { { name: 'filter' } } }
|
73
|
+
|
74
|
+
it { is_expected.to eq(published_results: { filter: { name: 'filter' } }) }
|
75
|
+
end
|
76
|
+
|
68
77
|
context 'with more than one aggregation' do
|
69
78
|
let(:options) { { group_by: :status, group_max: :price } }
|
70
79
|
|
data/spec/filter_spec.rb
CHANGED
@@ -6,4 +6,82 @@ RSpec.describe Elasticband::Filter do
|
|
6
6
|
|
7
7
|
it { is_expected.to eq(match_all: {}) }
|
8
8
|
end
|
9
|
+
|
10
|
+
describe '.parse' do
|
11
|
+
subject { described_class.parse(options) }
|
12
|
+
|
13
|
+
context 'with `:only/:except` option' do
|
14
|
+
context 'with only `:only` option' do
|
15
|
+
context 'with a single clause' do
|
16
|
+
let(:options) { { only: { status: :published } } }
|
17
|
+
|
18
|
+
it { is_expected.to eq(term: { status: :published }) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with multiple clauses' do
|
22
|
+
let(:options) { { only: { status: :published, company_id: 1 } } }
|
23
|
+
|
24
|
+
it { is_expected.to eq(and: [{ term: { status: :published } }, term: { company_id: 1 }]) }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with a nested attribute' do
|
28
|
+
let(:options) { { only: { company: { id: 1 } } } }
|
29
|
+
|
30
|
+
it { is_expected.to eq(term: { 'company.id': 1 }) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with multiple values' do
|
34
|
+
let(:options) { { only: { status: %i(published rejected) } } }
|
35
|
+
|
36
|
+
it { is_expected.to eq(terms: { status: %i(published rejected) }) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with only `:except` option' do
|
41
|
+
context 'with a single clause' do
|
42
|
+
let(:options) { { except: { status: :published } } }
|
43
|
+
|
44
|
+
it { is_expected.to eq(not: { term: { status: :published } }) }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with multiple clauses' do
|
48
|
+
let(:options) { { except: { status: :published, company_id: 1 } } }
|
49
|
+
|
50
|
+
it 'returns a filtered query with a `not` filter wrapping an `and` filter' do
|
51
|
+
is_expected.to eq(
|
52
|
+
and: [{ not: { term: { status: :published } } }, not: { term: { company_id: 1 } }]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'with a nested attribute' do
|
58
|
+
let(:options) { { except: { company: { id: 1 } } } }
|
59
|
+
|
60
|
+
it { is_expected.to eq(not: { term: { 'company.id': 1 } }) }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with multiple values' do
|
64
|
+
let(:options) { { except: { status: %i(published rejected) } } }
|
65
|
+
|
66
|
+
it { is_expected.to eq(not: { terms: { status: %i(published rejected) } }) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with both options' do
|
71
|
+
let(:options) { { only: { status: :published }, except: { company_id: 1 } } }
|
72
|
+
|
73
|
+
it { is_expected.to eq(and: [{ term: { status: :published } }, not: { term: { company_id: 1 } }]) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with `:includes` option' do
|
78
|
+
let(:options) { { includes: ['bar', on: :description] } }
|
79
|
+
|
80
|
+
before do
|
81
|
+
expect(Elasticband::Query).to receive(:parse).with('bar', on: :description) { { name: 'query' } }
|
82
|
+
end
|
83
|
+
|
84
|
+
it { is_expected.to eq(query: { name: 'query' }) }
|
85
|
+
end
|
86
|
+
end
|
9
87
|
end
|
data/spec/query_spec.rb
CHANGED
@@ -43,162 +43,13 @@ RSpec.describe Elasticband::Query do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
context 'with
|
47
|
-
|
48
|
-
context 'with a single clause' do
|
49
|
-
let(:options) { { only: { status: :published } } }
|
46
|
+
context 'with filters options' do
|
47
|
+
let(:options) { { only: { status: :published } } }
|
50
48
|
|
51
|
-
|
52
|
-
is_expected.to eq(
|
53
|
-
filtered: {
|
54
|
-
query: { match: { _all: 'foo' } },
|
55
|
-
filter: { term: { status: :published } }
|
56
|
-
}
|
57
|
-
)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
context 'with multiple clauses' do
|
62
|
-
let(:options) { { only: { status: :published, company_id: 1 } } }
|
63
|
-
|
64
|
-
it 'returns a filtered query with an `and` filter' do
|
65
|
-
is_expected.to eq(
|
66
|
-
filtered: {
|
67
|
-
query: { match: { _all: 'foo' } },
|
68
|
-
filter: {
|
69
|
-
and: [
|
70
|
-
{ term: { status: :published } },
|
71
|
-
term: { company_id: 1 }
|
72
|
-
]
|
73
|
-
}
|
74
|
-
}
|
75
|
-
)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
context 'with a nested attribute' do
|
80
|
-
let(:options) { { only: { company: { id: 1 } } } }
|
81
|
-
|
82
|
-
it 'returns a filtered query with a `term` filter on dotted notation' do
|
83
|
-
is_expected.to eq(
|
84
|
-
filtered: {
|
85
|
-
query: { match: { _all: 'foo' } },
|
86
|
-
filter: { term: { 'company.id': 1 } }
|
87
|
-
}
|
88
|
-
)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
context 'with multiple values' do
|
93
|
-
let(:options) { { only: { status: %i(published rejected) } } }
|
94
|
-
|
95
|
-
it 'returns a filtered query with a `terms` filter' do
|
96
|
-
is_expected.to eq(
|
97
|
-
filtered: {
|
98
|
-
query: { match: { _all: 'foo' } },
|
99
|
-
filter: { terms: { status: %i(published rejected) } }
|
100
|
-
}
|
101
|
-
)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context 'with only `:except` option' do
|
107
|
-
context 'with a single clause' do
|
108
|
-
let(:options) { { except: { status: :published } } }
|
109
|
-
|
110
|
-
it 'returns a filtered query with a `not` filter wrapping a `term` filter' do
|
111
|
-
is_expected.to eq(
|
112
|
-
filtered: {
|
113
|
-
query: { match: { _all: 'foo' } },
|
114
|
-
filter: { not: { term: { status: :published } } }
|
115
|
-
}
|
116
|
-
)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
context 'with multiple clauses' do
|
121
|
-
let(:options) { { except: { status: :published, company_id: 1 } } }
|
122
|
-
|
123
|
-
it 'returns a filtered query with a `not` filter wrapping an `and` filter' do
|
124
|
-
is_expected.to eq(
|
125
|
-
filtered: {
|
126
|
-
query: { match: { _all: 'foo' } },
|
127
|
-
filter: {
|
128
|
-
and: [
|
129
|
-
{ not: { term: { status: :published } } },
|
130
|
-
not: { term: { company_id: 1 } }
|
131
|
-
]
|
132
|
-
}
|
133
|
-
}
|
134
|
-
)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
context 'with a nested attribute' do
|
139
|
-
let(:options) { { except: { company: { id: 1 } } } }
|
140
|
-
|
141
|
-
it 'returns a filtered query with a `not` filter wrapping a `term` filter on dotted notation' do
|
142
|
-
is_expected.to eq(
|
143
|
-
filtered: {
|
144
|
-
query: { match: { _all: 'foo' } },
|
145
|
-
filter: { not: { term: { 'company.id': 1 } } }
|
146
|
-
}
|
147
|
-
)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
context 'with multiple values' do
|
152
|
-
let(:options) { { except: { status: %i(published rejected) } } }
|
49
|
+
before { expect(Elasticband::Filter).to receive(:parse).with(options) { { name: 'filter' } } }
|
153
50
|
|
154
|
-
|
155
|
-
|
156
|
-
filtered: {
|
157
|
-
query: { match: { _all: 'foo' } },
|
158
|
-
filter: { not: { terms: { status: %i(published rejected) } } }
|
159
|
-
}
|
160
|
-
)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
context 'with both options' do
|
166
|
-
let(:options) { { only: { status: :published }, except: { company_id: 1 } } }
|
167
|
-
|
168
|
-
it 'returns a filtered query combining the filters' do
|
169
|
-
is_expected.to eq(
|
170
|
-
filtered: {
|
171
|
-
query: { match: { _all: 'foo' } },
|
172
|
-
filter: {
|
173
|
-
and: [
|
174
|
-
{ term: { status: :published } },
|
175
|
-
not: { term: { company_id: 1 } }
|
176
|
-
]
|
177
|
-
}
|
178
|
-
}
|
179
|
-
)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
context 'with `:includes` option' do
|
185
|
-
let(:options) { { includes: ['bar', on: :description] } }
|
186
|
-
|
187
|
-
it 'returns a filtered query with query filter' do
|
188
|
-
is_expected.to eq(
|
189
|
-
filtered: {
|
190
|
-
query: { match: { _all: 'foo' } },
|
191
|
-
filter: {
|
192
|
-
query: { match: { description: 'bar' } }
|
193
|
-
}
|
194
|
-
}
|
195
|
-
)
|
196
|
-
end
|
197
|
-
|
198
|
-
it 'calls `.parse` for the includes option' do
|
199
|
-
allow(described_class).to receive(:parse).with('foo', options).and_call_original
|
200
|
-
expect(described_class).to receive(:parse).with('bar', on: :description).and_call_original
|
201
|
-
subject
|
51
|
+
it 'return a filtered query' do
|
52
|
+
is_expected.to eq(filtered: { query: { match: { _all: 'foo' } }, filter: { name: 'filter' } })
|
202
53
|
end
|
203
54
|
end
|
204
55
|
|
@@ -256,65 +107,12 @@ RSpec.describe Elasticband::Query do
|
|
256
107
|
context 'with `:boost_where` option' do
|
257
108
|
context 'with a regular attribute' do
|
258
109
|
let(:options) { { boost_where: { status: :published } } }
|
110
|
+
let(:filter_options) { { only: { status: :published } } }
|
259
111
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
query: { match: { _all: 'foo' } },
|
264
|
-
functions: [
|
265
|
-
{
|
266
|
-
filter: { term: { status: :published } },
|
267
|
-
boost_factor: 1000
|
268
|
-
}
|
269
|
-
]
|
270
|
-
}
|
271
|
-
)
|
112
|
+
before do
|
113
|
+
allow(Elasticband::Filter).to receive(:parse)
|
114
|
+
expect(Elasticband::Filter).to receive(:parse).with(filter_options) { { name: 'filter' } }
|
272
115
|
end
|
273
|
-
end
|
274
|
-
|
275
|
-
context 'with a multiple attributes' do
|
276
|
-
let(:options) { { boost_where: { status: :published, company_id: 1 } } }
|
277
|
-
|
278
|
-
it 'returns a function score query with a `boost_factor` filtered function' do
|
279
|
-
is_expected.to eq(
|
280
|
-
function_score: {
|
281
|
-
query: { match: { _all: 'foo' } },
|
282
|
-
functions: [
|
283
|
-
{
|
284
|
-
filter: {
|
285
|
-
and: [
|
286
|
-
{ term: { status: :published } },
|
287
|
-
term: { company_id: 1 }
|
288
|
-
]
|
289
|
-
},
|
290
|
-
boost_factor: 1000
|
291
|
-
}
|
292
|
-
]
|
293
|
-
}
|
294
|
-
)
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
context 'with a nested attribute' do
|
299
|
-
let(:options) { { boost_where: { company: { id: 1 } } } }
|
300
|
-
|
301
|
-
it 'returns a function score query with a `boost_factor` filtered function' do
|
302
|
-
is_expected.to eq(
|
303
|
-
function_score: {
|
304
|
-
query: { match: { _all: 'foo' } },
|
305
|
-
functions: [
|
306
|
-
{
|
307
|
-
filter: { term: { 'company.id': 1 } },
|
308
|
-
boost_factor: 1000
|
309
|
-
}
|
310
|
-
]
|
311
|
-
}
|
312
|
-
)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
context 'with multiple values' do
|
317
|
-
let(:options) { { boost_where: { status: %i(published rejected) } } }
|
318
116
|
|
319
117
|
it 'returns a function score query with a `boost_factor` filtered function' do
|
320
118
|
is_expected.to eq(
|
@@ -322,7 +120,7 @@ RSpec.describe Elasticband::Query do
|
|
322
120
|
query: { match: { _all: 'foo' } },
|
323
121
|
functions: [
|
324
122
|
{
|
325
|
-
filter: {
|
123
|
+
filter: { name: 'filter' },
|
326
124
|
boost_factor: 1000
|
327
125
|
}
|
328
126
|
]
|
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.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Glauber Campinho
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/elasticband.rb
|
117
117
|
- lib/elasticband/aggregation.rb
|
118
118
|
- lib/elasticband/aggregation/field_based.rb
|
119
|
+
- lib/elasticband/aggregation/filter.rb
|
119
120
|
- lib/elasticband/aggregation/max.rb
|
120
121
|
- lib/elasticband/aggregation/nested.rb
|
121
122
|
- lib/elasticband/aggregation/terms.rb
|
@@ -139,6 +140,7 @@ files:
|
|
139
140
|
- lib/elasticband/search.rb
|
140
141
|
- lib/elasticband/version.rb
|
141
142
|
- spec/aggregation/field_based_spec.rb
|
143
|
+
- spec/aggregation/filter_spec.rb
|
142
144
|
- spec/aggregation/max_spec.rb
|
143
145
|
- spec/aggregation/nested_spec.rb
|
144
146
|
- spec/aggregation/terms_spec.rb
|