stretchy 0.2.0 → 0.3.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 +4 -4
- data/lib/stretchy/boosts/base.rb +4 -1
- data/lib/stretchy/boosts/field_decay_boost.rb +47 -0
- data/lib/stretchy/boosts/filter_boost.rb +3 -0
- data/lib/stretchy/boosts/random_boost.rb +2 -0
- data/lib/stretchy/builders/match_builder.rb +22 -6
- data/lib/stretchy/builders/where_builder.rb +99 -32
- data/lib/stretchy/clauses/base.rb +22 -3
- data/lib/stretchy/clauses/boost_clause.rb +17 -0
- data/lib/stretchy/clauses/boost_match_clause.rb +2 -0
- data/lib/stretchy/clauses/boost_where_clause.rb +2 -0
- data/lib/stretchy/clauses/match_clause.rb +30 -6
- data/lib/stretchy/clauses/where_clause.rb +41 -30
- data/lib/stretchy/filters/and_filter.rb +3 -5
- data/lib/stretchy/filters/base.rb +3 -1
- data/lib/stretchy/filters/bool_filter.rb +2 -0
- data/lib/stretchy/filters/exists_filter.rb +2 -0
- data/lib/stretchy/filters/geo_filter.rb +8 -10
- data/lib/stretchy/filters/not_filter.rb +2 -0
- data/lib/stretchy/filters/or_filter.rb +22 -0
- data/lib/stretchy/filters/query_filter.rb +3 -0
- data/lib/stretchy/filters/range_filter.rb +7 -10
- data/lib/stretchy/filters/terms_filter.rb +2 -0
- data/lib/stretchy/queries/base.rb +3 -1
- data/lib/stretchy/queries/bool_query.rb +2 -0
- data/lib/stretchy/queries/filtered_query.rb +3 -0
- data/lib/stretchy/queries/function_score_query.rb +3 -0
- data/lib/stretchy/queries/match_all_query.rb +2 -0
- data/lib/stretchy/queries/match_query.rb +2 -0
- data/lib/stretchy/results/base.rb +3 -1
- data/lib/stretchy/results/null_results.rb +2 -0
- data/lib/stretchy/types/base.rb +19 -0
- data/lib/stretchy/types/geo_point.rb +30 -0
- data/lib/stretchy/types/range.rb +52 -0
- data/lib/stretchy/utils/client_actions.rb +4 -1
- data/lib/stretchy/utils/contract.rb +6 -2
- data/lib/stretchy/version.rb +1 -1
- data/lib/stretchy.rb +4 -22
- metadata +7 -3
- data/lib/stretchy/boosts/geo_boost.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 124aff086e4d044125b18f641bce3e440a3bec66
|
4
|
+
data.tar.gz: ee1b4ddda1669c13c7c17a9189fd1cda072f77b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a4375aca6e56a4755a693b3d346f945fe6d7aebe819064cbbeeb7e80e66588645ab17be24f8ed88b1ca89738d8eb6f9755cf2f79ccd35c9a60c8a862fd160e3
|
7
|
+
data.tar.gz: dac8575ff96c77ea601051da8fb08489c19c3b17245deed2ff6be48ed58fd2ad9054848025c52a43980a7112ac3277ad604c73ae0f1b1904a6d7eedab42d0715
|
data/lib/stretchy/boosts/base.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
require 'stretchy/utils/contract'
|
2
|
+
|
1
3
|
module Stretchy
|
2
4
|
module Boosts
|
3
5
|
class Base
|
4
6
|
|
5
7
|
include Stretchy::Utils::Contract
|
6
8
|
|
7
|
-
DEFAULT_WEIGHT
|
9
|
+
DEFAULT_WEIGHT = 1.2
|
10
|
+
DEFAULT_DECAY_FN = :gauss
|
8
11
|
|
9
12
|
def initialize
|
10
13
|
raise "Override this in subclass"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'stretchy/boosts/base'
|
2
|
+
require 'stretchy/types/geo_point'
|
3
|
+
|
4
|
+
module Stretchy
|
5
|
+
module Boosts
|
6
|
+
class FieldDecayBoost < Base
|
7
|
+
|
8
|
+
attr_reader :field, :origin, :scale, :offset, :decay, :decay_amount, :weight
|
9
|
+
|
10
|
+
contract field: {type: :field, required: true},
|
11
|
+
origin: {required: true, type: [Numeric, Time, Date, Stretchy::Types::GeoPoint]},
|
12
|
+
scale: {required: true},
|
13
|
+
decay: {type: Numeric},
|
14
|
+
type: {type: :decay}
|
15
|
+
|
16
|
+
def initialize(options = {})
|
17
|
+
@field = options[:field]
|
18
|
+
@origin = options[:origin]
|
19
|
+
@scale = options[:scale]
|
20
|
+
|
21
|
+
@offset = options[:offset]
|
22
|
+
@type = options[:type] || DEFAULT_DECAY_FN
|
23
|
+
@weight = options[:weight] || DEFAULT_WEIGHT
|
24
|
+
@decay = options[:decay]
|
25
|
+
|
26
|
+
validate!
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_search
|
30
|
+
json = {
|
31
|
+
origin: @origin,
|
32
|
+
scale: @scale,
|
33
|
+
}
|
34
|
+
json[:offset] = @offset if @offset
|
35
|
+
json[:decay] = @decay if @decay
|
36
|
+
|
37
|
+
{
|
38
|
+
@type => {
|
39
|
+
@field => json
|
40
|
+
},
|
41
|
+
weight: @weight
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -2,21 +2,25 @@ module Stretchy
|
|
2
2
|
module Builders
|
3
3
|
class MatchBuilder
|
4
4
|
|
5
|
-
attr_accessor :matches, :antimatches
|
5
|
+
attr_accessor :matches, :antimatches, :shouldmatches, :shouldnotmatches
|
6
6
|
|
7
7
|
def initialize
|
8
|
-
@matches
|
9
|
-
@antimatches
|
8
|
+
@matches = Hash.new { [] }
|
9
|
+
@antimatches = Hash.new { [] }
|
10
|
+
@shouldmatches = Hash.new { [] }
|
11
|
+
@shouldnotmatches = Hash.new { [] }
|
10
12
|
end
|
11
13
|
|
12
14
|
def any?
|
13
|
-
@matches.any? || @antimatches.any?
|
15
|
+
@matches.any? || @antimatches.any? || @shouldmatches.any? || @shouldnotmatches.any?
|
14
16
|
end
|
15
17
|
|
16
18
|
def build
|
17
19
|
return Stretchy::Queries::MatchAllQuery.new unless any?
|
18
20
|
|
19
|
-
if @matches.count > 1
|
21
|
+
if @matches.count > 1 || @antimatches.any? ||
|
22
|
+
@shouldmatches.any? || @shouldnotmatches.any?
|
23
|
+
|
20
24
|
bool_query
|
21
25
|
else
|
22
26
|
field, strings = @matches.first
|
@@ -27,10 +31,22 @@ module Stretchy
|
|
27
31
|
def bool_query
|
28
32
|
Stretchy::Queries::BoolQuery.new(
|
29
33
|
must: to_queries(@matches),
|
30
|
-
must_not: to_queries(@antimatches)
|
34
|
+
must_not: to_queries(@antimatches),
|
35
|
+
should: build_should
|
31
36
|
)
|
32
37
|
end
|
33
38
|
|
39
|
+
def build_should
|
40
|
+
if @shouldnotmatches.any?
|
41
|
+
Stretchy::Queries::BoolQuery.new(
|
42
|
+
must: to_queries(@shouldmatches),
|
43
|
+
must_not: to_queries(@shouldnotmatches)
|
44
|
+
)
|
45
|
+
else
|
46
|
+
to_queries(@shouldmatches)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
34
50
|
private
|
35
51
|
|
36
52
|
def to_queries(matches)
|
@@ -2,19 +2,31 @@ module Stretchy
|
|
2
2
|
module Builders
|
3
3
|
class WhereBuilder
|
4
4
|
|
5
|
-
attr_accessor :terms,
|
6
|
-
:
|
5
|
+
attr_accessor :terms, :antiterms, :shouldterms, :shouldnotterms,
|
6
|
+
:exists, :antiexists, :shouldexists, :shouldnotexists,
|
7
|
+
:ranges, :antiranges, :shouldranges, :shouldnotranges,
|
8
|
+
:geos, :antigeos, :shouldgeos, :shouldnotgeos
|
7
9
|
|
8
10
|
def initialize(options = {})
|
9
|
-
@terms
|
10
|
-
@antiterms
|
11
|
-
@
|
12
|
-
@
|
13
|
-
|
14
|
-
@
|
15
|
-
|
16
|
-
@
|
17
|
-
@
|
11
|
+
@terms = Hash.new { [] }
|
12
|
+
@antiterms = Hash.new { [] }
|
13
|
+
@shouldterms = Hash.new { [] }
|
14
|
+
@shouldnotterms = Hash.new { [] }
|
15
|
+
|
16
|
+
@ranges = {}
|
17
|
+
@antiranges = {}
|
18
|
+
@shouldranges = {}
|
19
|
+
@shouldnotranges = {}
|
20
|
+
|
21
|
+
@geos = {}
|
22
|
+
@antigeos = {}
|
23
|
+
@shouldgeos = {}
|
24
|
+
@shouldnotgeos = {}
|
25
|
+
|
26
|
+
@exists = []
|
27
|
+
@antiexists = []
|
28
|
+
@shouldexists = []
|
29
|
+
@shouldnotexists = []
|
18
30
|
end
|
19
31
|
|
20
32
|
def build
|
@@ -34,32 +46,47 @@ module Stretchy
|
|
34
46
|
end
|
35
47
|
|
36
48
|
def must_nots?
|
37
|
-
@antiterms.any? || @
|
49
|
+
@antiterms.any? || @antiexists.any? ||
|
38
50
|
@antiranges.any? || @antigeos.any?
|
39
51
|
end
|
40
52
|
|
53
|
+
def shoulds?
|
54
|
+
@shouldterms.any? ||
|
55
|
+
@shouldranges.any? ||
|
56
|
+
@shouldgeos.any? ||
|
57
|
+
@shouldexists.any?
|
58
|
+
end
|
59
|
+
|
60
|
+
def should_nots?
|
61
|
+
@shouldnotterms.any? ||
|
62
|
+
@shouldnotranges.any? ||
|
63
|
+
@shouldnotgeos.any? ||
|
64
|
+
@shouldnotexists.any?
|
65
|
+
end
|
66
|
+
|
41
67
|
def use_bool?
|
42
|
-
musts? && must_nots?
|
68
|
+
(musts? && must_nots?) || shoulds? || should_nots?
|
43
69
|
end
|
44
70
|
|
45
71
|
def any?
|
46
|
-
musts? || must_nots?
|
72
|
+
musts? || must_nots? || shoulds? || should_nots?
|
47
73
|
end
|
48
74
|
|
49
75
|
def bool_filter
|
50
76
|
Stretchy::Filters::BoolFilter.new(
|
51
77
|
must: build_filters(
|
52
|
-
terms:
|
78
|
+
terms: @terms,
|
53
79
|
exists: @exists,
|
54
80
|
ranges: @ranges,
|
55
|
-
geos:
|
81
|
+
geos: @geos
|
56
82
|
),
|
57
83
|
must_not: build_filters(
|
58
|
-
terms:
|
59
|
-
exists: @
|
84
|
+
terms: @antiterms,
|
85
|
+
exists: @antiexists,
|
60
86
|
ranges: @antiranges,
|
61
|
-
geos:
|
62
|
-
)
|
87
|
+
geos: @antigeos
|
88
|
+
),
|
89
|
+
should: build_should
|
63
90
|
)
|
64
91
|
end
|
65
92
|
|
@@ -82,20 +109,63 @@ module Stretchy
|
|
82
109
|
def not_filter
|
83
110
|
filter = build_filters(
|
84
111
|
terms: @antiterms,
|
85
|
-
exists: @
|
112
|
+
exists: @antiexists,
|
86
113
|
ranges: @antiranges,
|
87
114
|
geos: @antigeos
|
88
115
|
)
|
89
|
-
filter = Stretchy::Filters::
|
116
|
+
filter = Stretchy::Filters::OrFilter.new(filter) if filter.count > 1
|
90
117
|
Stretchy::Filters::NotFilter.new(filter)
|
91
118
|
end
|
92
119
|
|
120
|
+
def build_should
|
121
|
+
if shoulds? && should_nots?
|
122
|
+
Stretchy::Filters::BoolFilter.new(
|
123
|
+
must: build_filters(
|
124
|
+
terms: @shouldterms,
|
125
|
+
exists: @shouldexists,
|
126
|
+
ranges: @shouldranges,
|
127
|
+
geos: @shouldgeos
|
128
|
+
),
|
129
|
+
must_not: build_filters(
|
130
|
+
terms: @shouldnotterms,
|
131
|
+
exists: @shouldnotexists,
|
132
|
+
ranges: @shouldnotranges,
|
133
|
+
geos: @shouldnotgeos
|
134
|
+
)
|
135
|
+
)
|
136
|
+
elsif should_nots?
|
137
|
+
filters = build_filters(
|
138
|
+
terms: @shouldnotterms,
|
139
|
+
exists: @shouldnotexists,
|
140
|
+
ranges: @shouldnotranges,
|
141
|
+
geos: @shouldnotgeos
|
142
|
+
)
|
143
|
+
if filters.count > 1
|
144
|
+
filters = Stretchy::Filters::OrFilter.new(filters)
|
145
|
+
else
|
146
|
+
filters = filters.first
|
147
|
+
end
|
148
|
+
|
149
|
+
Stretchy::Filters::NotFilter.new(filters)
|
150
|
+
else
|
151
|
+
filters = build_filters(
|
152
|
+
terms: @shouldterms,
|
153
|
+
exists: @shouldexists,
|
154
|
+
ranges: @shouldranges,
|
155
|
+
geos: @shouldgeos
|
156
|
+
)
|
157
|
+
filters = Stretchy::Filters::AndFilter.new(filters) if filters.count > 1
|
158
|
+
filters
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
93
162
|
def build_filters(options = {})
|
94
163
|
filters = []
|
95
|
-
terms
|
96
|
-
ranges
|
97
|
-
geos
|
98
|
-
|
164
|
+
terms = Hash(options[:terms])
|
165
|
+
ranges = Hash(options[:ranges])
|
166
|
+
geos = Hash(options[:geos])
|
167
|
+
near_fields = Hash(options[:near_fields])
|
168
|
+
exists = Array(options[:exists])
|
99
169
|
|
100
170
|
filters << Stretchy::Filters::TermsFilter.new(terms) if terms.any?
|
101
171
|
|
@@ -103,18 +173,15 @@ module Stretchy
|
|
103
173
|
Stretchy::Filters::ExistsFilter.new(field)
|
104
174
|
end
|
105
175
|
|
106
|
-
filters += ranges.map do |field,
|
107
|
-
Stretchy::Filters::RangeFilter.new(
|
108
|
-
field: field,
|
109
|
-
min: values[:min],
|
110
|
-
max: values[:max]
|
111
|
-
)
|
176
|
+
filters += ranges.map do |field, value|
|
177
|
+
Stretchy::Filters::RangeFilter.new(field: field, stretchy_range: value)
|
112
178
|
end
|
113
179
|
|
114
180
|
filters += geos.map do |field, values|
|
115
181
|
Stretchy::Filters::GeoFilter.new(
|
116
182
|
field: field,
|
117
183
|
distance: values[:distance],
|
184
|
+
geo_point: values[:geo_point],
|
118
185
|
lat: values[:lat],
|
119
186
|
lng: values[:lng]
|
120
187
|
)
|
@@ -10,9 +10,8 @@ module Stretchy
|
|
10
10
|
attr_accessor :match_builder, :where_builder, :boost_builder,
|
11
11
|
:aggregate_builder, :inverse, :type, :index_name
|
12
12
|
|
13
|
-
alias :inverse? :inverse
|
14
|
-
|
15
13
|
delegate [:response, :results, :ids, :hits, :took, :shards, :total, :max_score] => :query_results
|
14
|
+
delegate [:range, :geo] => :where
|
16
15
|
|
17
16
|
def initialize(base_or_opts = nil, options = {})
|
18
17
|
if base_or_opts && !base_or_opts.is_a?(Hash)
|
@@ -70,6 +69,26 @@ module Stretchy
|
|
70
69
|
BoostClause.new(self, options)
|
71
70
|
end
|
72
71
|
|
72
|
+
def not(opts_or_string = {}, opts = {})
|
73
|
+
if opts_or_string.is_a?(Hash)
|
74
|
+
WhereClause.new(self, opts_or_string.merge(inverse: true))
|
75
|
+
else
|
76
|
+
MatchClause.new(self, opts_or_string, opts.merge(inverse: true))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def should(opts_or_string = {}, opts = {})
|
81
|
+
if opts_or_string.is_a?(Hash)
|
82
|
+
WhereClause.new(self, opts_or_string.merge(should: true))
|
83
|
+
else
|
84
|
+
MatchClause.new(self, opts_or_string, opts.merge(should: true))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def inverse?
|
89
|
+
!!@inverse
|
90
|
+
end
|
91
|
+
|
73
92
|
def to_search
|
74
93
|
return @to_search if @to_search
|
75
94
|
|
@@ -92,4 +111,4 @@ module Stretchy
|
|
92
111
|
|
93
112
|
end
|
94
113
|
end
|
95
|
-
end
|
114
|
+
end
|
@@ -1,7 +1,13 @@
|
|
1
|
+
require 'stretchy/clauses/base'
|
2
|
+
|
1
3
|
module Stretchy
|
2
4
|
module Clauses
|
3
5
|
class BoostClause < Base
|
4
6
|
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
delegate [:geo, :range] => :where
|
10
|
+
|
5
11
|
def initialize(base, options = {})
|
6
12
|
super(base)
|
7
13
|
@inverse = options.delete(:inverse)
|
@@ -15,6 +21,17 @@ module Stretchy
|
|
15
21
|
BoostWhereClause.new(self, options)
|
16
22
|
end
|
17
23
|
|
24
|
+
def near(options = {})
|
25
|
+
if options[:lat] || options[:latitude] ||
|
26
|
+
options[:lng] || options[:longitude] || options[:lon]
|
27
|
+
|
28
|
+
options[:origin] = Stretchy::Types::GeoPoint.new(options)
|
29
|
+
end
|
30
|
+
@boost_builder.functions << Stretchy::Boosts::FieldDecayBoost.new(options)
|
31
|
+
self
|
32
|
+
end
|
33
|
+
alias :geo :near
|
34
|
+
|
18
35
|
def random(*args)
|
19
36
|
@boost_builder.functions << Stretchy::Boosts::RandomBoost.new(*args)
|
20
37
|
self
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'stretchy/clauses/base'
|
2
|
+
|
1
3
|
module Stretchy
|
2
4
|
module Clauses
|
3
5
|
class MatchClause < Base
|
@@ -10,15 +12,21 @@ module Stretchy
|
|
10
12
|
super(base)
|
11
13
|
if opts_or_str.is_a?(Hash)
|
12
14
|
@inverse = opts_or_str.delete(:inverse) || options.delete(:inverse)
|
15
|
+
@should = opts_or_str.delete(:should) || options.delete(:should)
|
13
16
|
add_params(options.merge(opts_or_str))
|
14
17
|
else
|
15
18
|
@inverse = options.delete(:inverse)
|
19
|
+
@should = options.delete(:should)
|
16
20
|
add_params(options.merge('_all' => opts_or_str))
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
20
24
|
def not(opts_or_str = {}, options = {})
|
21
|
-
self.class.new(self, opts_or_str, options.merge(inverse:
|
25
|
+
self.class.new(self, opts_or_str, options.merge(inverse: true, should: should?))
|
26
|
+
end
|
27
|
+
|
28
|
+
def should(opts_or_str = {}, options = {})
|
29
|
+
self.class.new(self, opts_or_str, options.merge(should: true))
|
22
30
|
end
|
23
31
|
|
24
32
|
def to_boost(weight = nil)
|
@@ -31,8 +39,28 @@ module Stretchy
|
|
31
39
|
)
|
32
40
|
end
|
33
41
|
|
42
|
+
def should?
|
43
|
+
!!@should
|
44
|
+
end
|
45
|
+
|
34
46
|
private
|
35
47
|
|
48
|
+
def get_storage
|
49
|
+
if inverse?
|
50
|
+
if should?
|
51
|
+
@match_builder.shouldnotmatches
|
52
|
+
else
|
53
|
+
@match_builder.antimatches
|
54
|
+
end
|
55
|
+
else
|
56
|
+
if should?
|
57
|
+
@match_builder.shouldmatches
|
58
|
+
else
|
59
|
+
@match_builder.matches
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
36
64
|
def add_params(params = {})
|
37
65
|
case params
|
38
66
|
when Hash
|
@@ -45,11 +73,7 @@ module Stretchy
|
|
45
73
|
end
|
46
74
|
|
47
75
|
def add_param(field, param)
|
48
|
-
|
49
|
-
@match_builder.antimatches[field] += Array(param)
|
50
|
-
else
|
51
|
-
@match_builder.matches[field] += Array(param)
|
52
|
-
end
|
76
|
+
get_storage[field] += Array(param)
|
53
77
|
end
|
54
78
|
|
55
79
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'stretchy/clauses/base'
|
2
|
+
|
1
3
|
module Stretchy
|
2
4
|
module Clauses
|
3
5
|
class WhereClause < Base
|
@@ -9,32 +11,33 @@ module Stretchy
|
|
9
11
|
def initialize(base, options = {})
|
10
12
|
super(base)
|
11
13
|
@inverse = options.delete(:inverse)
|
14
|
+
@should = options.delete(:should)
|
12
15
|
add_params(options)
|
13
16
|
end
|
14
17
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
case options
|
19
|
-
when Hash
|
20
|
-
min = options[:min]
|
21
|
-
max = options[:max]
|
22
|
-
store = inverse? ? @where_builder.antiranges : @where_builder.ranges
|
23
|
-
store[field] = { min: min, max: max }
|
24
|
-
when Range
|
25
|
-
add_param(field, options)
|
26
|
-
end
|
18
|
+
def should?
|
19
|
+
!!@should
|
20
|
+
end
|
27
21
|
|
22
|
+
def range(field, options = {})
|
23
|
+
get_storage(:ranges)[field] = Stretchy::Types::Range.new(options)
|
28
24
|
self
|
29
25
|
end
|
30
26
|
|
31
27
|
def geo(field, options = {})
|
32
|
-
|
28
|
+
get_storage(:geos)[field] = {
|
29
|
+
distance: options[:distance],
|
30
|
+
geo_point: Stretchy::Types::GeoPoint.new(options)
|
31
|
+
}
|
33
32
|
self
|
34
33
|
end
|
35
34
|
|
36
35
|
def not(options = {})
|
37
|
-
self.class.new(self, options.merge(inverse:
|
36
|
+
self.class.new(self, options.merge(inverse: true, should: should?))
|
37
|
+
end
|
38
|
+
|
39
|
+
def should(options = {})
|
40
|
+
self.class.new(self, options.merge(should: true))
|
38
41
|
end
|
39
42
|
|
40
43
|
def to_boost(weight = nil)
|
@@ -68,6 +71,26 @@ module Stretchy
|
|
68
71
|
end
|
69
72
|
|
70
73
|
private
|
74
|
+
|
75
|
+
def get_storage(builder_field, is_inverse = nil)
|
76
|
+
is_inverse = inverse? if is_inverse.nil?
|
77
|
+
field = builder_field.to_s
|
78
|
+
if inverse? || is_inverse
|
79
|
+
if should?
|
80
|
+
field = "shouldnot#{field}"
|
81
|
+
else
|
82
|
+
field = "anti#{field}"
|
83
|
+
end
|
84
|
+
else
|
85
|
+
field = "should#{field}" if should?
|
86
|
+
end
|
87
|
+
|
88
|
+
if field =~ /match/
|
89
|
+
@match_builder.send(field)
|
90
|
+
else
|
91
|
+
@where_builder.send(field)
|
92
|
+
end
|
93
|
+
end
|
71
94
|
|
72
95
|
def add_params(options = {})
|
73
96
|
options.each do |field, param|
|
@@ -83,28 +106,16 @@ module Stretchy
|
|
83
106
|
end
|
84
107
|
end
|
85
108
|
|
86
|
-
def add_geo(field, options = {})
|
87
|
-
store = inverse? ? @where_builder.antigeos : @where_builder.geos
|
88
|
-
store[field] = options
|
89
|
-
end
|
90
|
-
|
91
109
|
def add_param(field, param)
|
92
110
|
case param
|
93
111
|
when nil
|
94
|
-
|
95
|
-
store << field
|
112
|
+
get_storage(:exists, true) << field
|
96
113
|
when String, Symbol
|
97
|
-
|
98
|
-
@match_builder.antimatches[field] += Array(param)
|
99
|
-
else
|
100
|
-
@match_builder.matches[field] += Array(param)
|
101
|
-
end
|
114
|
+
get_storage(:matches)[field] += Array(param)
|
102
115
|
when Range
|
103
|
-
|
104
|
-
store[field] = { min: param.min, max: param.max }
|
116
|
+
get_storage(:ranges)[field] = Stretchy::Types::Range.new(param)
|
105
117
|
else
|
106
|
-
|
107
|
-
store[field] += Array(param)
|
118
|
+
get_storage(:terms)[field] += Array(param)
|
108
119
|
end
|
109
120
|
end
|
110
121
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'stretchy/filters/base'
|
2
|
+
|
1
3
|
module Stretchy
|
2
4
|
module Filters
|
3
5
|
class AndFilter < Base
|
@@ -5,11 +7,7 @@ module Stretchy
|
|
5
7
|
contract filters: {type: Stretchy::Filters::Base, array: true}
|
6
8
|
|
7
9
|
def initialize(*args)
|
8
|
-
|
9
|
-
@filters = args.first
|
10
|
-
else
|
11
|
-
@filters = args
|
12
|
-
end
|
10
|
+
@filters = args.flatten
|
13
11
|
validate!
|
14
12
|
end
|
15
13
|
|
@@ -1,17 +1,18 @@
|
|
1
|
+
require 'stretchy/filters/base'
|
2
|
+
require 'stretchy/types/geo_point'
|
3
|
+
|
1
4
|
module Stretchy
|
2
5
|
module Filters
|
3
6
|
class GeoFilter < Base
|
4
7
|
|
5
8
|
contract distance: {type: :distance, required: true},
|
6
|
-
|
7
|
-
lng: {type: :lng, required: true},
|
9
|
+
geo_point: {type: Stretchy::Types::GeoPoint, required: true},
|
8
10
|
field: {type: :field, required: true}
|
9
11
|
|
10
12
|
def initialize(options = {})
|
11
|
-
@field
|
12
|
-
@distance
|
13
|
-
@
|
14
|
-
@lng = options[:lng]
|
13
|
+
@field = options[:field]
|
14
|
+
@distance = options[:distance]
|
15
|
+
@geo_point = options[:geo_point] || Stretchy::Types::GeoPoint.new(options)
|
15
16
|
validate!
|
16
17
|
end
|
17
18
|
|
@@ -19,10 +20,7 @@ module Stretchy
|
|
19
20
|
{
|
20
21
|
geo_distance: {
|
21
22
|
distance: @distance,
|
22
|
-
@field =>
|
23
|
-
lat: @lat,
|
24
|
-
lon: @lng
|
25
|
-
}
|
23
|
+
@field => @geo_point.to_search
|
26
24
|
}
|
27
25
|
}
|
28
26
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'stretchy/filters/base'
|
2
|
+
|
3
|
+
module Stretchy
|
4
|
+
module Filters
|
5
|
+
class OrFilter < Base
|
6
|
+
|
7
|
+
contract filters: {type: Base, array: true}
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
@filters = args.flatten
|
11
|
+
validate!
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_search
|
15
|
+
{
|
16
|
+
or: @filters.map(&:to_search)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,26 +1,23 @@
|
|
1
|
+
require 'stretchy/filters/base'
|
2
|
+
require 'stretchy/types/range'
|
3
|
+
|
1
4
|
module Stretchy
|
2
5
|
module Filters
|
3
6
|
class RangeFilter < Base
|
4
7
|
|
5
|
-
contract field: {type: :field},
|
6
|
-
|
7
|
-
max: {type: [Numeric, Time]}
|
8
|
+
contract field: {type: :field, required: true},
|
9
|
+
range: {type: Stretchy::Types::Range, required: true}
|
8
10
|
|
9
11
|
def initialize(options = {})
|
10
12
|
@field = options[:field]
|
11
|
-
@
|
12
|
-
@max = options[:max]
|
13
|
+
@range = options[:stretchy_range] || Stretchy::Types::Range.new(options)
|
13
14
|
validate!
|
14
|
-
require_one(min: @min, max: @max)
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_search
|
18
|
-
range = {}
|
19
|
-
range[:gte] = @min if @min
|
20
|
-
range[:lte] = @max if @max
|
21
18
|
{
|
22
19
|
range: {
|
23
|
-
@field => range
|
20
|
+
@field => @range.to_search
|
24
21
|
}
|
25
22
|
}
|
26
23
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'stretchy/utils/contract'
|
2
|
+
|
3
|
+
module Stretchy
|
4
|
+
module Types
|
5
|
+
class Base
|
6
|
+
|
7
|
+
include Stretchy::Utils::Contract
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
raise "Override this in subclass"
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_search
|
14
|
+
raise "Override this in subclass"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'stretchy/types/base'
|
2
|
+
|
3
|
+
module Stretchy
|
4
|
+
module Types
|
5
|
+
class GeoPoint < Base
|
6
|
+
|
7
|
+
attr_reader :lat, :lon
|
8
|
+
|
9
|
+
contract lat: { type: :lat, required: true },
|
10
|
+
lon: { type: :lng, required: true }
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@lat = options[:lat] || options[:latitude]
|
15
|
+
@lon = options[:lng] || options[:lon] ||
|
16
|
+
options[:longitude]
|
17
|
+
|
18
|
+
validate!
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_search
|
22
|
+
{
|
23
|
+
lat: @lat,
|
24
|
+
lon: @lon
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Stretchy
|
2
|
+
module Types
|
3
|
+
class Range < Base
|
4
|
+
|
5
|
+
attr_reader :min, :max, :exclusive_min, :exclusive_max
|
6
|
+
|
7
|
+
contract min: { type: [Numeric, Date, Time] },
|
8
|
+
max: { type: [Numeric, Date, Time] },
|
9
|
+
exclusive_min: { in: [true, false] },
|
10
|
+
exclusive_max: { in: [true, false] }
|
11
|
+
|
12
|
+
def initialize(opts_or_range = {}, options = {})
|
13
|
+
|
14
|
+
case opts_or_range
|
15
|
+
when ::Range
|
16
|
+
@min = opts_or_range.min
|
17
|
+
@max = opts_or_range.max
|
18
|
+
@exclusive_min = !!(options[:exclusive_min] || options[:exclusive])
|
19
|
+
@exclusive_max = !!(options[:exclusive_max] || options[:exclusive])
|
20
|
+
when ::Hash
|
21
|
+
opts = options.merge(opts_or_range)
|
22
|
+
@min = opts[:min]
|
23
|
+
@max = opts[:max]
|
24
|
+
@exclusive_min = !!(opts[:exclusive_min] || opts[:exclusive])
|
25
|
+
@exclusive_max = !!(opts[:exclusive_max] || opts[:exclusive])
|
26
|
+
else
|
27
|
+
raise Stretchy::Errors::ContractError.new("Ranges must be a range or a hash - found #{options.class.name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
require_one min: @min, max: @max
|
31
|
+
validate!
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_search
|
35
|
+
json = {}
|
36
|
+
if @exclusive_min && @min
|
37
|
+
json[:gt] = @min
|
38
|
+
elsif @min
|
39
|
+
json[:gte] = @min
|
40
|
+
end
|
41
|
+
|
42
|
+
if @exclusive_max && @max
|
43
|
+
json[:lt] = @max
|
44
|
+
elsif @max
|
45
|
+
json[:lte] = @max
|
46
|
+
end
|
47
|
+
json
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'json'
|
2
1
|
module Stretchy
|
3
2
|
module Utils
|
4
3
|
module ClientActions
|
@@ -18,6 +17,10 @@ module Stretchy
|
|
18
17
|
client.cat.count(index: index_name).split(' ')[2].to_i
|
19
18
|
end
|
20
19
|
|
20
|
+
def query(*args, &block)
|
21
|
+
Stretchy::Clauses::Base.new(*args, &block)
|
22
|
+
end
|
23
|
+
|
21
24
|
def search(type:, body:, fields: nil)
|
22
25
|
options = { index: index_name, type: type, body: body }
|
23
26
|
options[:fields] = fields if fields.is_a?(Array)
|
@@ -4,6 +4,7 @@ module Stretchy
|
|
4
4
|
|
5
5
|
ASSERTIONS = [:type, :responds_to, :in, :matches, :required]
|
6
6
|
DISTANCE_FORMAT = /^(\d+)(km|mi)$/
|
7
|
+
DECAY_FUNCTIONS = [:gauss, :linear, :exp]
|
7
8
|
|
8
9
|
def self.included(base)
|
9
10
|
base.send(:extend, ClassMethods)
|
@@ -31,7 +32,7 @@ module Stretchy
|
|
31
32
|
|
32
33
|
def require_one(options = {})
|
33
34
|
if options.values.all?{|v| self.class.is_empty?(v) }
|
34
|
-
raise Stretchy::Errors::ContractError.new("One of #{options.keys.join(', ')} must be present")
|
35
|
+
raise Stretchy::Errors::ContractError.new("One of #{options.keys.join(', ')} must be present, found: #{options}")
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
@@ -60,7 +61,7 @@ module Stretchy
|
|
60
61
|
|
61
62
|
def assert_required(name, value, options)
|
62
63
|
msg = "Expected to have param #{name}, but got nil"
|
63
|
-
fail_assertion(msg) if
|
64
|
+
fail_assertion(msg) if is_empty?(value)
|
64
65
|
end
|
65
66
|
|
66
67
|
def assert_type(name, value, options)
|
@@ -83,6 +84,9 @@ module Stretchy
|
|
83
84
|
|
84
85
|
msg = "Expected #{name} to be a string, symbol, or number, but was blank"
|
85
86
|
fail_assertion(msg) if value.is_a?(String) && value.empty?
|
87
|
+
when :decay
|
88
|
+
msg = "Expected #{name} to be one of #{DECAY_FUNCTIONS.join(', ')}, but got #{value}"
|
89
|
+
fail_assertion(msg) unless DECAY_FUNCTIONS.any?{|f| f == value || f.to_s == value }
|
86
90
|
when Array
|
87
91
|
msg = "Expected #{name} to be one of #{type.map{|t| t.name}}, but got #{value.class.name}"
|
88
92
|
fail_assertion(msg) unless type.any?{|t| value.is_a?(t)}
|
data/lib/stretchy/version.rb
CHANGED
data/lib/stretchy.rb
CHANGED
@@ -4,28 +4,10 @@ require 'forwardable'
|
|
4
4
|
require 'excon'
|
5
5
|
require 'elasticsearch'
|
6
6
|
|
7
|
-
require 'stretchy/utils/contract'
|
8
|
-
require 'stretchy/utils/configuration'
|
9
|
-
require 'stretchy/utils/client_actions'
|
10
|
-
require 'stretchy/boosts/base'
|
11
|
-
require 'stretchy/filters/base'
|
12
|
-
require 'stretchy/queries/base'
|
13
|
-
require 'stretchy/clauses/base'
|
14
|
-
require 'stretchy/results/base'
|
15
|
-
|
16
|
-
grep_require = ->(matches){
|
17
|
-
Dir[File.join(File.dirname(__FILE__), 'stretchy', '**', '*.rb')].each do |path|
|
18
|
-
require path if path =~ matches
|
19
|
-
end
|
20
|
-
}
|
21
|
-
|
22
|
-
grep_require.call(/utils/)
|
23
|
-
grep_require.call(/base/)
|
24
|
-
grep_require.call(/(?!utils|base)/)
|
25
|
-
|
26
7
|
module Stretchy
|
8
|
+
end
|
27
9
|
|
28
|
-
|
29
|
-
extend Utils::ClientActions
|
10
|
+
Gem.find_files('stretchy/**/*.rb').reject{|f| f =~ /spec/ }.each {|f| require f }
|
30
11
|
|
31
|
-
|
12
|
+
Stretchy.send(:extend, Stretchy::Utils::Configuration)
|
13
|
+
Stretchy.send(:extend, Stretchy::Utils::ClientActions)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stretchy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- agius
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: elasticsearch
|
@@ -138,8 +138,8 @@ files:
|
|
138
138
|
- bin/setup
|
139
139
|
- lib/stretchy.rb
|
140
140
|
- lib/stretchy/boosts/base.rb
|
141
|
+
- lib/stretchy/boosts/field_decay_boost.rb
|
141
142
|
- lib/stretchy/boosts/filter_boost.rb
|
142
|
-
- lib/stretchy/boosts/geo_boost.rb
|
143
143
|
- lib/stretchy/boosts/random_boost.rb
|
144
144
|
- lib/stretchy/builders/boost_builder.rb
|
145
145
|
- lib/stretchy/builders/match_builder.rb
|
@@ -157,6 +157,7 @@ files:
|
|
157
157
|
- lib/stretchy/filters/exists_filter.rb
|
158
158
|
- lib/stretchy/filters/geo_filter.rb
|
159
159
|
- lib/stretchy/filters/not_filter.rb
|
160
|
+
- lib/stretchy/filters/or_filter.rb
|
160
161
|
- lib/stretchy/filters/query_filter.rb
|
161
162
|
- lib/stretchy/filters/range_filter.rb
|
162
163
|
- lib/stretchy/filters/terms_filter.rb
|
@@ -168,6 +169,9 @@ files:
|
|
168
169
|
- lib/stretchy/queries/match_query.rb
|
169
170
|
- lib/stretchy/results/base.rb
|
170
171
|
- lib/stretchy/results/null_results.rb
|
172
|
+
- lib/stretchy/types/base.rb
|
173
|
+
- lib/stretchy/types/geo_point.rb
|
174
|
+
- lib/stretchy/types/range.rb
|
171
175
|
- lib/stretchy/utils/client_actions.rb
|
172
176
|
- lib/stretchy/utils/configuration.rb
|
173
177
|
- lib/stretchy/utils/contract.rb
|
@@ -1,46 +0,0 @@
|
|
1
|
-
module Stretchy
|
2
|
-
module Boosts
|
3
|
-
class GeoBoost < Base
|
4
|
-
|
5
|
-
DEFAULTS = {
|
6
|
-
field: 'coords',
|
7
|
-
offset: '10km',
|
8
|
-
scale: '50km',
|
9
|
-
decay: 0.75,
|
10
|
-
weight: 1.2
|
11
|
-
}.freeze
|
12
|
-
|
13
|
-
contract offset: {type: :distance},
|
14
|
-
scale: {type: :distance},
|
15
|
-
decay: {type: Numeric},
|
16
|
-
weight: {type: Numeric},
|
17
|
-
lat: {type: :lat},
|
18
|
-
lng: {type: :lng}
|
19
|
-
|
20
|
-
def initialize(options = {})
|
21
|
-
@field = options[:field] || DEFAULTS[:field]
|
22
|
-
@offset = options[:offset] || DEFAULTS[:offset]
|
23
|
-
@scale = options[:scale] || DEFAULTS[:scale]
|
24
|
-
@decay = options[:decay] || DEFAULTS[:decay]
|
25
|
-
@weight = options[:weight] || DEFAULTS[:weight]
|
26
|
-
@lat = options[:lat]
|
27
|
-
@lng = options[:lng]
|
28
|
-
validate!
|
29
|
-
end
|
30
|
-
|
31
|
-
def to_search
|
32
|
-
{
|
33
|
-
gauss: {
|
34
|
-
@field => {
|
35
|
-
origin: { lat: @lat, lon: @lng },
|
36
|
-
offset: @offset,
|
37
|
-
scale: @scale,
|
38
|
-
decay: @decay
|
39
|
-
}
|
40
|
-
},
|
41
|
-
weight: @weight
|
42
|
-
}
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|