stretchy 0.1.2 → 0.2.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 +2 -0
- data/lib/stretchy/boosts/filter_boost.rb +4 -4
- data/lib/stretchy/boosts/random_boost.rb +2 -2
- data/lib/stretchy/builders/boost_builder.rb +36 -0
- data/lib/stretchy/builders/match_builder.rb +44 -0
- data/lib/stretchy/builders/where_builder.rb +126 -0
- data/lib/stretchy/clauses/base.rb +95 -0
- data/lib/stretchy/clauses/boost_clause.rb +50 -0
- data/lib/stretchy/clauses/boost_match_clause.rb +31 -0
- data/lib/stretchy/clauses/boost_where_clause.rb +44 -0
- data/lib/stretchy/clauses/match_clause.rb +57 -0
- data/lib/stretchy/clauses/where_clause.rb +113 -0
- data/lib/stretchy/filters/and_filter.rb +6 -2
- data/lib/stretchy/filters/bool_filter.rb +8 -7
- data/lib/stretchy/filters/exists_filter.rb +3 -0
- data/lib/stretchy/filters/geo_filter.rb +9 -9
- data/lib/stretchy/filters/range_filter.rb +8 -6
- data/lib/stretchy/filters/terms_filter.rb +17 -9
- data/lib/stretchy/queries/bool_query.rb +21 -0
- data/lib/stretchy/queries/filtered_query.rb +4 -3
- data/lib/stretchy/queries/function_score_query.rb +1 -1
- data/lib/stretchy/queries/match_query.rb +12 -5
- data/lib/stretchy/results/base.rb +45 -0
- data/lib/stretchy/results/null_results.rb +22 -0
- data/lib/stretchy/utils/contract.rb +16 -1
- data/lib/stretchy/version.rb +1 -1
- data/lib/stretchy.rb +12 -3
- data/stretchy.gemspec +6 -4
- metadata +42 -5
- data/lib/stretchy/null_query.rb +0 -30
- data/lib/stretchy/query.rb +0 -241
- data/lib/stretchy/request_body.rb +0 -67
@@ -2,10 +2,13 @@ module Stretchy
|
|
2
2
|
module Filters
|
3
3
|
class ExistsFilter < Base
|
4
4
|
|
5
|
+
contract field: {type: :field, required: true}
|
6
|
+
|
5
7
|
# CAUTION: this will match empty strings
|
6
8
|
# see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-filter.html
|
7
9
|
def initialize(field)
|
8
10
|
@field = field
|
11
|
+
validate!
|
9
12
|
end
|
10
13
|
|
11
14
|
def to_search
|
@@ -2,16 +2,16 @@ module Stretchy
|
|
2
2
|
module Filters
|
3
3
|
class GeoFilter < Base
|
4
4
|
|
5
|
-
contract distance: {type: :distance},
|
6
|
-
lat: {type: :lat},
|
7
|
-
lng: {type: :lng},
|
8
|
-
field: {type: :field}
|
5
|
+
contract distance: {type: :distance, required: true},
|
6
|
+
lat: {type: :lat, required: true},
|
7
|
+
lng: {type: :lng, required: true},
|
8
|
+
field: {type: :field, required: true}
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@field = field
|
12
|
-
@distance = distance
|
13
|
-
@lat = lat
|
14
|
-
@lng = lng
|
10
|
+
def initialize(options = {})
|
11
|
+
@field = options[:field]
|
12
|
+
@distance = options[:distance]
|
13
|
+
@lat = options[:lat]
|
14
|
+
@lng = options[:lng]
|
15
15
|
validate!
|
16
16
|
end
|
17
17
|
|
@@ -3,13 +3,15 @@ module Stretchy
|
|
3
3
|
class RangeFilter < Base
|
4
4
|
|
5
5
|
contract field: {type: :field},
|
6
|
-
min: {type: Numeric},
|
7
|
-
max: {type: Numeric}
|
6
|
+
min: {type: [Numeric, Time]},
|
7
|
+
max: {type: [Numeric, Time]}
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@field = field
|
11
|
-
@min = min
|
12
|
-
@max = max
|
9
|
+
def initialize(options = {})
|
10
|
+
@field = options[:field]
|
11
|
+
@min = options[:min]
|
12
|
+
@max = options[:max]
|
13
|
+
validate!
|
14
|
+
require_one(min: @min, max: @max)
|
13
15
|
end
|
14
16
|
|
15
17
|
def to_search
|
@@ -2,20 +2,28 @@ module Stretchy
|
|
2
2
|
module Filters
|
3
3
|
class TermsFilter < Base
|
4
4
|
|
5
|
-
contract field: {type: :field},
|
6
|
-
values: {type: Array}
|
5
|
+
contract field: {type: :field, required: true},
|
6
|
+
values: {type: Array, required: true}
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def initialize(field_or_opts = {}, terms = [])
|
9
|
+
if field_or_opts.is_a?(Hash)
|
10
|
+
@terms = field_or_opts
|
11
|
+
else
|
12
|
+
@terms = { field_or_opts => Array(terms) }
|
13
|
+
end
|
14
|
+
validate_terms!
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate_terms!
|
18
|
+
exc = Stretchy::Errors::ContractError.new("Terms cannot be blank")
|
19
|
+
raise exc if @terms.none? || @terms.any? do |field, terms|
|
20
|
+
terms.none?
|
21
|
+
end
|
12
22
|
end
|
13
23
|
|
14
24
|
def to_search
|
15
25
|
{
|
16
|
-
terms:
|
17
|
-
@field => @values
|
18
|
-
}
|
26
|
+
terms: @terms
|
19
27
|
}
|
20
28
|
end
|
21
29
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Stretchy
|
2
|
+
module Queries
|
3
|
+
class BoolQuery < Base
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@must = Array(options[:must])
|
7
|
+
@must_not = Array(options[:must_not])
|
8
|
+
@should = Array(options[:should])
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_search
|
12
|
+
json = {}
|
13
|
+
json[:must] = @must.map(&:to_search) if @must.any?
|
14
|
+
json[:must_not] = @must_not.map(&:to_search) if @must_not.any?
|
15
|
+
json[:should] = @should.map(&:to_search) if @should.any?
|
16
|
+
{ bool: json }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -5,10 +5,11 @@ module Stretchy
|
|
5
5
|
contract query: {type: Base},
|
6
6
|
filter: {type: Stretchy::Filters::Base}
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@query = query
|
10
|
-
@filter = filter
|
8
|
+
def initialize(options = {})
|
9
|
+
@query = options[:query]
|
10
|
+
@filter = options[:filter]
|
11
11
|
validate!
|
12
|
+
require_one(query: @query, filter: @filter)
|
12
13
|
end
|
13
14
|
|
14
15
|
def to_search
|
@@ -5,7 +5,7 @@ module Stretchy
|
|
5
5
|
SCORE_MODES = %w(multiply sum avg first max min)
|
6
6
|
BOOST_MODES = %w(multiply replace sum avg max min)
|
7
7
|
|
8
|
-
contract functions: {
|
8
|
+
contract functions: {type: Stretchy::Boosts::Base, array: true},
|
9
9
|
query: {type: Base},
|
10
10
|
filter: {type: Stretchy::Filters::Base},
|
11
11
|
score_mode: {type: String, in: SCORE_MODES},
|
@@ -8,10 +8,17 @@ module Stretchy
|
|
8
8
|
operator: {type: String, in: OPERATORS},
|
9
9
|
string: {type: String}
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def initialize(options = {})
|
12
|
+
case options
|
13
|
+
when String
|
14
|
+
@field = '_all'
|
15
|
+
@string = options
|
16
|
+
@operator = 'and'
|
17
|
+
when Hash
|
18
|
+
@field = options[:field] || '_all'
|
19
|
+
@string = options[:string]
|
20
|
+
@operator = options[:operator] || 'and'
|
21
|
+
end
|
15
22
|
validate!
|
16
23
|
end
|
17
24
|
|
@@ -19,7 +26,7 @@ module Stretchy
|
|
19
26
|
{
|
20
27
|
match: {
|
21
28
|
@field => {
|
22
|
-
query:
|
29
|
+
query: @string,
|
23
30
|
operator: @operator
|
24
31
|
}
|
25
32
|
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Stretchy
|
2
|
+
module Results
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def initialize(request_json, options = {})
|
6
|
+
@request = request_json
|
7
|
+
@index = options[:index]
|
8
|
+
@type = options[:type]
|
9
|
+
end
|
10
|
+
|
11
|
+
def response
|
12
|
+
@response ||= Stretchy.search(type: @type, body: request_json)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ids
|
16
|
+
@ids ||= response['hits']['hits'].map{|h| h['_id'] =~ /\d+(\.\d+)?/ ? h['_id'].to_i : h['_id'] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def hits
|
20
|
+
@hits ||= response['hits']['hits'].map do |hit|
|
21
|
+
merge_fields = hit.reject{|field, _| field == '_source' }
|
22
|
+
hit['_source'].merge(merge_fields)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias :results :hits
|
26
|
+
|
27
|
+
def took
|
28
|
+
@took ||= response['took']
|
29
|
+
end
|
30
|
+
|
31
|
+
def shards
|
32
|
+
@shards ||= response['_shards']
|
33
|
+
end
|
34
|
+
|
35
|
+
def total
|
36
|
+
@total ||= response['hits']['total']
|
37
|
+
end
|
38
|
+
|
39
|
+
def max_score
|
40
|
+
@max_score ||= response['hits']['max_score']
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Stretchy
|
2
|
+
module Results
|
3
|
+
class NullResults < Base
|
4
|
+
# use this class when you don't want to actually run
|
5
|
+
# a search, or catch an exception or something
|
6
|
+
|
7
|
+
def response
|
8
|
+
@response ||= {
|
9
|
+
'took' => 0,
|
10
|
+
'timed_out' => false,
|
11
|
+
'_shards' => {},
|
12
|
+
'hits' => {
|
13
|
+
'total' => 0,
|
14
|
+
'max_score' => 0,
|
15
|
+
'hits' => []
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -29,10 +29,22 @@ module Stretchy
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def require_one(options = {})
|
33
|
+
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
|
+
end
|
36
|
+
end
|
37
|
+
|
32
38
|
module ClassMethods
|
33
39
|
|
34
40
|
attr_reader :contracts
|
35
41
|
|
42
|
+
def is_empty?(value)
|
43
|
+
value.nil? ||
|
44
|
+
(value.respond_to?(:any?) && !value.any?) ||
|
45
|
+
(value.respond_to?(:length) && value.length == 0)
|
46
|
+
end
|
47
|
+
|
36
48
|
def contract(var, options = {})
|
37
49
|
@contracts ||= {}
|
38
50
|
if var.is_a?(Hash)
|
@@ -56,7 +68,7 @@ module Stretchy
|
|
56
68
|
case type
|
57
69
|
when :distance
|
58
70
|
msg = "Expected #{name} to be a distance measure, but #{value} is not a valid distance"
|
59
|
-
fail_assertion(msg) unless value.match(DISTANCE_FORMAT)
|
71
|
+
fail_assertion(msg) unless String(value).match(DISTANCE_FORMAT)
|
60
72
|
when :lat
|
61
73
|
msg = "Expected #{name} to be between 90 and -90, but was #{value}"
|
62
74
|
value = Float(value) rescue nil
|
@@ -71,6 +83,9 @@ module Stretchy
|
|
71
83
|
|
72
84
|
msg = "Expected #{name} to be a string, symbol, or number, but was blank"
|
73
85
|
fail_assertion(msg) if value.is_a?(String) && value.empty?
|
86
|
+
when Array
|
87
|
+
msg = "Expected #{name} to be one of #{type.map{|t| t.name}}, but got #{value.class.name}"
|
88
|
+
fail_assertion(msg) unless type.any?{|t| value.is_a?(t)}
|
74
89
|
else
|
75
90
|
msg = "Expected #{name} to be of type #{type}, but found #{value.class.name}"
|
76
91
|
fail_assertion(msg) unless value.is_a?(type)
|
data/lib/stretchy/version.rb
CHANGED
data/lib/stretchy.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'logger'
|
3
|
+
require 'forwardable'
|
3
4
|
require 'excon'
|
4
5
|
require 'elasticsearch'
|
5
6
|
|
@@ -9,10 +10,18 @@ require 'stretchy/utils/client_actions'
|
|
9
10
|
require 'stretchy/boosts/base'
|
10
11
|
require 'stretchy/filters/base'
|
11
12
|
require 'stretchy/queries/base'
|
13
|
+
require 'stretchy/clauses/base'
|
14
|
+
require 'stretchy/results/base'
|
12
15
|
|
13
|
-
|
14
|
-
|
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)/)
|
16
25
|
|
17
26
|
module Stretchy
|
18
27
|
|
data/stretchy.gemspec
CHANGED
@@ -22,8 +22,10 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency "elasticsearch", "~> 1.0"
|
23
23
|
spec.add_dependency "excon", "~> 0.45"
|
24
24
|
|
25
|
-
spec.add_development_dependency "bundler",
|
26
|
-
spec.add_development_dependency "rake",
|
27
|
-
spec.add_development_dependency "rspec",
|
28
|
-
spec.add_development_dependency "fuubar",
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.8"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.2"
|
28
|
+
spec.add_development_dependency "fuubar", "~> 2.0"
|
29
|
+
spec.add_development_dependency "pry", "~> 0.10"
|
30
|
+
spec.add_development_dependency "awesome_print", "~> 1.6"
|
29
31
|
end
|
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.2.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-
|
11
|
+
date: 2015-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: elasticsearch
|
@@ -94,6 +94,34 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '2.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.10'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.10'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: awesome_print
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.6'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.6'
|
97
125
|
description: Build queries for Elasticsearch with a chainable interface like ActiveRecord's.
|
98
126
|
email:
|
99
127
|
- andrew@atevans.com
|
@@ -113,6 +141,15 @@ files:
|
|
113
141
|
- lib/stretchy/boosts/filter_boost.rb
|
114
142
|
- lib/stretchy/boosts/geo_boost.rb
|
115
143
|
- lib/stretchy/boosts/random_boost.rb
|
144
|
+
- lib/stretchy/builders/boost_builder.rb
|
145
|
+
- lib/stretchy/builders/match_builder.rb
|
146
|
+
- lib/stretchy/builders/where_builder.rb
|
147
|
+
- lib/stretchy/clauses/base.rb
|
148
|
+
- lib/stretchy/clauses/boost_clause.rb
|
149
|
+
- lib/stretchy/clauses/boost_match_clause.rb
|
150
|
+
- lib/stretchy/clauses/boost_where_clause.rb
|
151
|
+
- lib/stretchy/clauses/match_clause.rb
|
152
|
+
- lib/stretchy/clauses/where_clause.rb
|
116
153
|
- lib/stretchy/errors/contract_error.rb
|
117
154
|
- lib/stretchy/filters/and_filter.rb
|
118
155
|
- lib/stretchy/filters/base.rb
|
@@ -123,14 +160,14 @@ files:
|
|
123
160
|
- lib/stretchy/filters/query_filter.rb
|
124
161
|
- lib/stretchy/filters/range_filter.rb
|
125
162
|
- lib/stretchy/filters/terms_filter.rb
|
126
|
-
- lib/stretchy/null_query.rb
|
127
163
|
- lib/stretchy/queries/base.rb
|
164
|
+
- lib/stretchy/queries/bool_query.rb
|
128
165
|
- lib/stretchy/queries/filtered_query.rb
|
129
166
|
- lib/stretchy/queries/function_score_query.rb
|
130
167
|
- lib/stretchy/queries/match_all_query.rb
|
131
168
|
- lib/stretchy/queries/match_query.rb
|
132
|
-
- lib/stretchy/
|
133
|
-
- lib/stretchy/
|
169
|
+
- lib/stretchy/results/base.rb
|
170
|
+
- lib/stretchy/results/null_results.rb
|
134
171
|
- lib/stretchy/utils/client_actions.rb
|
135
172
|
- lib/stretchy/utils/configuration.rb
|
136
173
|
- lib/stretchy/utils/contract.rb
|
data/lib/stretchy/null_query.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
module Stretchy
|
2
|
-
class NullQuery
|
3
|
-
def initialize(options = {})
|
4
|
-
end
|
5
|
-
|
6
|
-
def response
|
7
|
-
{}
|
8
|
-
end
|
9
|
-
|
10
|
-
def id_response
|
11
|
-
{}
|
12
|
-
end
|
13
|
-
|
14
|
-
def results
|
15
|
-
[]
|
16
|
-
end
|
17
|
-
|
18
|
-
def ids
|
19
|
-
[]
|
20
|
-
end
|
21
|
-
|
22
|
-
def shards
|
23
|
-
[]
|
24
|
-
end
|
25
|
-
|
26
|
-
def total
|
27
|
-
0
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|