stretchy 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/README.md +76 -46
- data/lib/stretchy/clauses/base.rb +152 -0
- data/lib/stretchy/clauses/boost_clause.rb +139 -0
- data/lib/stretchy/clauses/boost_match_clause.rb +59 -0
- data/lib/stretchy/clauses/boost_where_clause.rb +68 -0
- data/lib/stretchy/clauses/match_clause.rb +100 -0
- data/lib/stretchy/clauses/where_clause.rb +148 -0
- data/lib/stretchy/results/base.rb +20 -1
- data/lib/stretchy/utils/client_actions.rb +24 -4
- data/lib/stretchy/utils/colorize.rb +71 -0
- data/lib/stretchy/utils/configuration.rb +15 -2
- data/lib/stretchy/utils/logger.rb +59 -0
- data/lib/stretchy/utils.rb +4 -2
- data/lib/stretchy/version.rb +1 -1
- data/lib/stretchy.rb +2 -0
- data/stretchy.gemspec +1 -0
- metadata +19 -2
@@ -2,10 +2,31 @@ require 'stretchy/clauses/boost_clause'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
+
#
|
6
|
+
# Boost documents that match a free-text query. Most
|
7
|
+
# options will be passed into {#initialize}, but you
|
8
|
+
# can also chain `.not` onto it. Calling `.where` or
|
9
|
+
# `.match` from here will apply filters (*not boosts*)
|
10
|
+
# and return to the base state
|
11
|
+
#
|
12
|
+
# @author [atevans]
|
13
|
+
#
|
5
14
|
class BoostMatchClause < BoostClause
|
6
15
|
|
7
16
|
delegate [:range, :geo] => :where
|
8
17
|
|
18
|
+
#
|
19
|
+
# Adds a match query to the boost functions.
|
20
|
+
#
|
21
|
+
# @overload initialize(base, opts_or_string)
|
22
|
+
# @param base [Base] Base query to copy data from
|
23
|
+
# @param opts_or_string [String] String to do a free-text match across the document
|
24
|
+
#
|
25
|
+
# @overload initialize(base, opts_or_string)
|
26
|
+
# @param base [Base] Base query to copy data from
|
27
|
+
# @param options = {} [Hash] Fields and values to match via full-text search
|
28
|
+
#
|
29
|
+
# @return [BoostMatchClause] Boost clause in match context, with queries applied
|
9
30
|
def initialize(base, opts_or_string = {}, options = {})
|
10
31
|
super(base)
|
11
32
|
if opts_or_string.is_a?(Hash)
|
@@ -17,14 +38,52 @@ module Stretchy
|
|
17
38
|
end
|
18
39
|
end
|
19
40
|
|
41
|
+
#
|
42
|
+
# Switches to inverse context, and applies filters as inverse
|
43
|
+
# options (ie, documents that *do not* match the query will
|
44
|
+
# be boosted)
|
45
|
+
#
|
46
|
+
# @overload not(opts_or_string)
|
47
|
+
# @param [String] String that must not match anywhere in the document
|
48
|
+
#
|
49
|
+
# @overload not(opts_or_string)
|
50
|
+
# @param opts_or_string [Hash] Fields and values that should not match in the document
|
51
|
+
#
|
52
|
+
# @return [BoostMatchClause] Query with inverse matching boost function applied
|
20
53
|
def not(opts_or_string = {}, options = {})
|
21
54
|
self.class.new(self, opts_or_string, options.merge(inverse: !inverse?))
|
22
55
|
end
|
23
56
|
|
57
|
+
#
|
58
|
+
# Returns to the base context; filters passed here
|
59
|
+
# will be used to filter documents.
|
60
|
+
#
|
61
|
+
# @example Returning to base context
|
62
|
+
# query.boost.match('string').where(other_field: 64)
|
63
|
+
#
|
64
|
+
# @example Staying in boost context
|
65
|
+
# query.boost.match('string').boost.where(other_field: 99)
|
66
|
+
#
|
67
|
+
# @see {WhereClause#initialize}
|
68
|
+
#
|
69
|
+
# @return [WhereClause] Query with where clause applied
|
24
70
|
def where(*args)
|
25
71
|
WhereClause.new(self, *args)
|
26
72
|
end
|
27
73
|
|
74
|
+
#
|
75
|
+
# Returns to the base context. Queries passed here
|
76
|
+
# will be used to filter documents.
|
77
|
+
#
|
78
|
+
# @example Returning to base context
|
79
|
+
# query.boost.match(message: 'curse word').match('username')
|
80
|
+
#
|
81
|
+
# @example Staying in boost context
|
82
|
+
# query.boost.match(message: 'happy word').boost.match('love')
|
83
|
+
#
|
84
|
+
# @see {MatchClause#initialize}
|
85
|
+
#
|
86
|
+
# @return [MatchClause] Base context with match queries applied
|
28
87
|
def match(*args)
|
29
88
|
MatchClause.new(self, *args)
|
30
89
|
end
|
@@ -2,27 +2,95 @@ require 'stretchy/clauses/boost_clause'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
+
#
|
6
|
+
# Boosts documents that match certain filters. Most filters will
|
7
|
+
# be passed into {#initialize}, but you can also use `.range` and
|
8
|
+
# `.geo` .
|
9
|
+
#
|
10
|
+
# @author [atevans]
|
11
|
+
#
|
5
12
|
class BoostWhereClause < BoostClause
|
6
13
|
|
14
|
+
#
|
15
|
+
# Generates a boost that matches a set of filters.
|
16
|
+
#
|
17
|
+
# @param base [Base] Query to copy data from.
|
18
|
+
# @param options = {} [Hash] Fields and values to filter on.
|
19
|
+
#
|
20
|
+
# @see {WhereClause#initialize}
|
21
|
+
#
|
22
|
+
# @return [BoostWhereClause] Query with filter boosts applied
|
7
23
|
def initialize(base, options = {})
|
8
24
|
super(base, options)
|
9
25
|
where_function(:init, options)
|
10
26
|
self
|
11
27
|
end
|
12
28
|
|
29
|
+
#
|
30
|
+
# Returns to the base context; filters passed here
|
31
|
+
# will be used to filter documents.
|
32
|
+
#
|
33
|
+
# @example Returning to base context
|
34
|
+
# query.boost.where(number_field: 33).where(other_field: 64)
|
35
|
+
#
|
36
|
+
# @example Staying in boost context
|
37
|
+
# query.boost.where(number_field: 33).boost.where(other_field: 99)
|
38
|
+
#
|
39
|
+
# @see {WhereClause#initialize}
|
40
|
+
#
|
41
|
+
# @return [WhereClause] Query with where clause applied
|
13
42
|
def where(*args)
|
14
43
|
WhereClause.new(self, *args)
|
15
44
|
end
|
16
45
|
|
46
|
+
#
|
47
|
+
# Returns to the base context. Queries passed here
|
48
|
+
# will be used to filter documents.
|
49
|
+
#
|
50
|
+
# @example Returning to base context
|
51
|
+
# query.boost.where(number_field: 89).match('username')
|
52
|
+
#
|
53
|
+
# @example Staying in boost context
|
54
|
+
# query.boost.where(number_field: 89).boost.match('love')
|
55
|
+
#
|
56
|
+
# @see {MatchClause#initialize}
|
57
|
+
#
|
58
|
+
# @return [MatchClause] Base context with match queries applied
|
17
59
|
def match(*args)
|
18
60
|
MatchClause.new(self, *args)
|
19
61
|
end
|
20
62
|
|
63
|
+
#
|
64
|
+
# Applies a range filter with a min or max
|
65
|
+
# as a boost.
|
66
|
+
#
|
67
|
+
# @see {WhereClause#range}
|
68
|
+
#
|
69
|
+
# @see {Filters::RangeFilter}
|
70
|
+
#
|
71
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/guide/master/_ranges.html Elastic Guides - Ranges
|
72
|
+
#
|
73
|
+
# @return [Base] Query in base context with range boost applied
|
21
74
|
def range(*args)
|
22
75
|
where_function(:range, *args)
|
23
76
|
Base.new(self)
|
24
77
|
end
|
25
78
|
|
79
|
+
#
|
80
|
+
# Boosts a document if it matches a geo filter.
|
81
|
+
# This is different than {BoostClause#near} -
|
82
|
+
# while `.near` applies a decay function that boosts
|
83
|
+
# based on how close a field is to a geo point,
|
84
|
+
# `.geo` applies a filter that either boosts or doesn't
|
85
|
+
# boost the document.
|
86
|
+
#
|
87
|
+
# @see {WhereFunction#geo}
|
88
|
+
#
|
89
|
+
# @see {Filters::GeoFilter}
|
90
|
+
#
|
91
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-filter.html Elastic Docs - Geo Distance Filter
|
92
|
+
#
|
93
|
+
# @return [Base] Query in base context with geo filter boost applied
|
26
94
|
def geo(*args)
|
27
95
|
where_function(:geo, *args)
|
28
96
|
Base.new(self)
|
@@ -2,12 +2,49 @@ require 'stretchy/clauses/base'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
+
#
|
6
|
+
# A Match clause inherits the same state as any clause.
|
7
|
+
# There aren't any more specific methods to chain, as
|
8
|
+
# this clause only handles basic full-text searches.
|
9
|
+
#
|
10
|
+
# @author [atevans]
|
11
|
+
#
|
5
12
|
class MatchClause < Base
|
6
13
|
|
14
|
+
#
|
15
|
+
# Creates a temporary MatchClause outside the main
|
16
|
+
# query scope by using a new {Base}. Primarily
|
17
|
+
# used in {BoostClause} for boosting on full-text
|
18
|
+
# matches.
|
19
|
+
#
|
20
|
+
# @param options = {} [Hash] Options to pass to the full-text match
|
21
|
+
#
|
22
|
+
# @return [MatchClause] Temporary clause outside current state
|
7
23
|
def self.tmp(options = {})
|
8
24
|
self.new(Base.new, options)
|
9
25
|
end
|
10
26
|
|
27
|
+
#
|
28
|
+
# Creates a new state with a match query applied.
|
29
|
+
#
|
30
|
+
# @overload initialize(base, opts_or_string)
|
31
|
+
# @param [Base] Base clause to copy data from
|
32
|
+
# @param [String] Performs a full-text query for this string on all fields in the document.
|
33
|
+
#
|
34
|
+
# @overload initialize(base, opts_or_string)
|
35
|
+
# @param [Base] Base clause to copy data from
|
36
|
+
# @param [Hash] A hash of fields and values to perform full-text matches with
|
37
|
+
#
|
38
|
+
# @example A basic full-text match
|
39
|
+
# query.match("anywhere in document")
|
40
|
+
#
|
41
|
+
# @example A full-text search on specific fields
|
42
|
+
# query.match(
|
43
|
+
# my_field: "match in my_field",
|
44
|
+
# other_field: "match in other_field"
|
45
|
+
# )
|
46
|
+
#
|
47
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html Elastic Docs - Match Query
|
11
48
|
def initialize(base, opts_or_str = {}, options = {})
|
12
49
|
super(base)
|
13
50
|
if opts_or_str.is_a?(Hash)
|
@@ -21,14 +58,73 @@ module Stretchy
|
|
21
58
|
end
|
22
59
|
end
|
23
60
|
|
61
|
+
#
|
62
|
+
# Switches to inverted context. Matches applied here work the same way as
|
63
|
+
# {#initialize}, but returned documents must **not** match these filters.
|
64
|
+
#
|
65
|
+
# @overload not(opts_or_str)
|
66
|
+
# @param [String] A string that must not be matched anywhere in the document
|
67
|
+
# @overload not(opts_or_str)
|
68
|
+
# @param [Hash] A hash of fields and strings that must not be matched in those fields
|
69
|
+
#
|
70
|
+
# @return [MatchClause] inverted query state with match filters applied
|
71
|
+
#
|
72
|
+
# @example Inverted full-text
|
73
|
+
# query.match.not("hello")
|
74
|
+
#
|
75
|
+
# @example Inverted full-text matching for specific fields
|
76
|
+
# query.match.not(
|
77
|
+
# my_field: "not_match_1",
|
78
|
+
# other_field: "not_match_2"
|
79
|
+
# )
|
24
80
|
def not(opts_or_str = {}, options = {})
|
25
81
|
self.class.new(self, opts_or_str, options.merge(inverse: true, should: should?))
|
26
82
|
end
|
27
83
|
|
84
|
+
#
|
85
|
+
# Switches to `should` context. Applies full-text matches
|
86
|
+
# that are not required, but boost the relevance score for
|
87
|
+
# matching documents.
|
88
|
+
#
|
89
|
+
# Can be chained with {#not}
|
90
|
+
#
|
91
|
+
# @overload not(opts_or_str)
|
92
|
+
# @param [String] A string that should be matched anywhere in the document
|
93
|
+
# @overload not(opts_or_str)
|
94
|
+
# @param [Hash] A hash of fields and strings that should be matched in those fields
|
95
|
+
#
|
96
|
+
# @param opts_or_str = {} [type] [description]
|
97
|
+
# @param options = {} [type] [description]
|
98
|
+
#
|
99
|
+
# @return [MatchClause] query state with should filters added
|
100
|
+
#
|
101
|
+
# @example Should match with full-text
|
102
|
+
# query.match.should("anywhere")
|
103
|
+
#
|
104
|
+
# @example Should match specific fields
|
105
|
+
# query.match.should(
|
106
|
+
# field_one: "one",
|
107
|
+
# field_two: "two"
|
108
|
+
# )
|
109
|
+
#
|
110
|
+
# @example Should not match
|
111
|
+
# query.match.should.not(
|
112
|
+
# field_one: "one",
|
113
|
+
# field_two: "two"
|
114
|
+
# )
|
115
|
+
#
|
116
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html Elastic Docs - Bool Query
|
28
117
|
def should(opts_or_str = {}, options = {})
|
29
118
|
self.class.new(self, opts_or_str, options.merge(should: true))
|
30
119
|
end
|
31
120
|
|
121
|
+
#
|
122
|
+
# Converts this match context to a set of boosts
|
123
|
+
# to use in a {Stretchy::Queries::FunctionScoreQuery}
|
124
|
+
#
|
125
|
+
# @param weight = nil [Numeric] Weight of generated boost
|
126
|
+
#
|
127
|
+
# @return [Stretchy::Boosts::FilterBoost] boost containing these match parameters
|
32
128
|
def to_boost(weight = nil)
|
33
129
|
weight ||= Stretchy::Boosts::FilterBoost::DEFAULT_WEIGHT
|
34
130
|
Stretchy::Boosts::FilterBoost.new(
|
@@ -39,6 +135,10 @@ module Stretchy
|
|
39
135
|
)
|
40
136
|
end
|
41
137
|
|
138
|
+
#
|
139
|
+
# Accessor for `@should`
|
140
|
+
#
|
141
|
+
# @return [true, false] `@should`
|
42
142
|
def should?
|
43
143
|
!!@should
|
44
144
|
end
|
@@ -2,12 +2,62 @@ require 'stretchy/clauses/base'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
+
#
|
6
|
+
# A Where clause inherits the same state as any clause,
|
7
|
+
# but has a few possible states to transition to. You
|
8
|
+
# can call {#range} and {#geo} to add their respective
|
9
|
+
# filters, or you can transition to the inverted state
|
10
|
+
# via {#not}, or the `should` state via {#should}
|
11
|
+
#
|
12
|
+
# ### STATES:
|
13
|
+
# * **inverted:** any filters added in this state will be
|
14
|
+
# inverted, ie the document must **NOT** match said
|
15
|
+
# filters.
|
16
|
+
# * **should:** any filters added in this state will be
|
17
|
+
# applied to a `should` block. Documents which do
|
18
|
+
# not match these filters will be returned, but
|
19
|
+
# documents which do match will have a higher
|
20
|
+
# relevance score.
|
21
|
+
#
|
22
|
+
# @author [atevans]
|
23
|
+
#
|
5
24
|
class WhereClause < Base
|
6
25
|
|
26
|
+
#
|
27
|
+
# Creates a temporary context by initializing a new Base object.
|
28
|
+
# Used primarily in {BoostWhereClause}
|
29
|
+
#
|
30
|
+
# @param options = {} [Hash] Options to filter on
|
31
|
+
#
|
32
|
+
# @return [WhereClause] A clause outside the main query context
|
7
33
|
def self.tmp(options = {})
|
8
34
|
self.new(Base.new, options)
|
9
35
|
end
|
10
36
|
|
37
|
+
#
|
38
|
+
# Options passed to the initializer will be interpreted as filters
|
39
|
+
# to be added to the query. This is similar to ActiveRecord's `where`
|
40
|
+
# method.
|
41
|
+
#
|
42
|
+
# @param base [Base] Used to intialize the new state from the previous clause
|
43
|
+
# @param options = {} [Hash] filters to be applied to the new state
|
44
|
+
# @option options [true, false] :inverted (nil) Whether the new state is inverted
|
45
|
+
# @option options [true, false] :should (nil) Whether the new state is should
|
46
|
+
#
|
47
|
+
# @example Apply ActiveRecord-like filters
|
48
|
+
# query.where(
|
49
|
+
# string_field: "string",
|
50
|
+
# must_not_exist: nil,
|
51
|
+
# in_range: 27..33,
|
52
|
+
# included_in: [47, 23, 86]
|
53
|
+
# )
|
54
|
+
#
|
55
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-filter.html Elastic Docs - Terms Filter
|
56
|
+
#
|
57
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-filter.html Elastic Docs - Exists Filter
|
58
|
+
#
|
59
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-filter.html Elastic Docs - Range Filter
|
60
|
+
#
|
11
61
|
def initialize(base, options = {})
|
12
62
|
super(base)
|
13
63
|
@inverse = options.delete(:inverse)
|
@@ -15,15 +65,62 @@ module Stretchy
|
|
15
65
|
add_params(options)
|
16
66
|
end
|
17
67
|
|
68
|
+
#
|
69
|
+
# Accessor for `@should`
|
70
|
+
#
|
71
|
+
# @return [true, false] `@should`
|
18
72
|
def should?
|
19
73
|
!!@should
|
20
74
|
end
|
21
75
|
|
76
|
+
#
|
77
|
+
# Add a range filter to the current context. While
|
78
|
+
# you can pass a `Range` object to {#where}, this
|
79
|
+
# allows you to specify an open-ended range, such
|
80
|
+
# as only specifying the minimum or maximum value.
|
81
|
+
#
|
82
|
+
# @param field [String, Symbol] The field to filter with this range
|
83
|
+
# @param options = {} [Hash] Options for the range
|
84
|
+
# @option options [Numeric] :min (nil) Minimum. Ranges are _inclusive_ by default
|
85
|
+
# @option options [Numeric] :max (nil) Maximum. Ranges are _inclusive_ by default
|
86
|
+
# @option options [true, false] :exclusive (nil) Overrides default and makes the range exclusive -
|
87
|
+
# equivalent to passing both `:exclusive_min` and `:exclusive_max`
|
88
|
+
# @option options [true, false] :exclusive_min (nil) Overrides default and makes the minimum exclusive
|
89
|
+
# @option options [true, false] :exclusive_max (nil) Overrides default and makes the maximum exclusive
|
90
|
+
#
|
91
|
+
# @return [self] query state with range filter applied
|
92
|
+
#
|
93
|
+
# @example Adding a range filter
|
94
|
+
# query.where.range(:my_range_field,
|
95
|
+
# min: 33,
|
96
|
+
# exclusive: true
|
97
|
+
# )
|
22
98
|
def range(field, options = {})
|
23
99
|
get_storage(:ranges)[field] = Stretchy::Types::Range.new(options)
|
24
100
|
self
|
25
101
|
end
|
26
102
|
|
103
|
+
#
|
104
|
+
# Adds a geo distance filter to the current context.
|
105
|
+
# Documents must have a `geo_point` field that is within
|
106
|
+
# the specified distance of the passed parameters.
|
107
|
+
#
|
108
|
+
# @param field [String, Symbol] The field this filter will be applied to.
|
109
|
+
# @param options = {} [Hash] Options for the geo distance filter
|
110
|
+
# @option options [String] :distance (nil) The maximum distance from the specified origin.
|
111
|
+
# Use an Elastic distance format such as `'21mi'` or `'37km'`
|
112
|
+
# @option options [Float] :lat (nil) The latitude of the origin point. Can also be specified as `:latitude`
|
113
|
+
# @option options [Float] :lng (nil) The longitude of the origin point.
|
114
|
+
# Can also be specified as `:lon` or `:longitude`
|
115
|
+
#
|
116
|
+
# @return [self] query state with geo distance filter applied
|
117
|
+
#
|
118
|
+
# @example Searching by distance from a point
|
119
|
+
# query.where.geo(:coords,
|
120
|
+
# distance: '27km',
|
121
|
+
# lat: 33.3,
|
122
|
+
# lng: 29.2
|
123
|
+
# )
|
27
124
|
def geo(field, options = {})
|
28
125
|
get_storage(:geos)[field] = {
|
29
126
|
distance: options[:distance],
|
@@ -32,14 +129,65 @@ module Stretchy
|
|
32
129
|
self
|
33
130
|
end
|
34
131
|
|
132
|
+
#
|
133
|
+
# Switches current state to inverted. Options passed
|
134
|
+
# here are equivalent to those passed to {#initialize},
|
135
|
+
# except documents *must not* match these filters.
|
136
|
+
#
|
137
|
+
# Can be chained with {#should} to produce inverted should queries
|
138
|
+
#
|
139
|
+
# @param options = {} [Hash] Options to filter on
|
140
|
+
#
|
141
|
+
# @return [WhereClause] inverted query state with not filters applied.
|
142
|
+
#
|
143
|
+
# @example Inverting filters
|
144
|
+
# query.where.not(
|
145
|
+
# must_exist: nil,
|
146
|
+
# not_matching: "this string",
|
147
|
+
# not_in: [45, 67, 99],
|
148
|
+
# not_in_range: 89..23
|
149
|
+
# )
|
150
|
+
#
|
151
|
+
# @example Inverted should filters
|
152
|
+
# query.should.not(
|
153
|
+
# match_field: [:these, "options"]
|
154
|
+
# )
|
35
155
|
def not(options = {})
|
36
156
|
self.class.new(self, options.merge(inverse: true, should: should?))
|
37
157
|
end
|
38
158
|
|
159
|
+
#
|
160
|
+
# Switches the current state to `should`. Options passed
|
161
|
+
# here are equivalent to those passed to {#initialize},
|
162
|
+
# except documents which do not match are still returned
|
163
|
+
# with a lower score than documents which do match.
|
164
|
+
#
|
165
|
+
# Can be chained with {#not} to produce inverted should queries
|
166
|
+
#
|
167
|
+
# @param options = {} [Hash] Options to filter on
|
168
|
+
#
|
169
|
+
# @return [WhereClause] should query state with should filters applied
|
170
|
+
#
|
171
|
+
# @example Specifying should options
|
172
|
+
# query.should(
|
173
|
+
# field: [99, 27]
|
174
|
+
# )
|
175
|
+
#
|
176
|
+
# @example Inverted should options
|
177
|
+
# query.should.not(
|
178
|
+
# exists_field: nil
|
179
|
+
# )
|
39
180
|
def should(options = {})
|
40
181
|
self.class.new(self, options.merge(should: true))
|
41
182
|
end
|
42
183
|
|
184
|
+
#
|
185
|
+
# Converts the current context into a boost to
|
186
|
+
# be passed into a {FunctionScoreQuery}.
|
187
|
+
#
|
188
|
+
# @param weight = nil [Numeric] A weight for the {FunctionScoreQuery}
|
189
|
+
#
|
190
|
+
# @return [Boosts::FilterBoost] A boost including all the current filters
|
43
191
|
def to_boost(weight = nil)
|
44
192
|
weight ||= Stretchy::Boosts::FilterBoost::DEFAULT_WEIGHT
|
45
193
|
|
@@ -26,7 +26,14 @@ module Stretchy
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def response
|
29
|
-
|
29
|
+
params = {
|
30
|
+
type: type,
|
31
|
+
body: request,
|
32
|
+
from: offset,
|
33
|
+
size: limit
|
34
|
+
}
|
35
|
+
params[:explain] = true if clause.get_explain
|
36
|
+
@response ||= Stretchy.search(params)
|
30
37
|
end
|
31
38
|
|
32
39
|
def ids
|
@@ -41,6 +48,18 @@ module Stretchy
|
|
41
48
|
end
|
42
49
|
alias :results :hits
|
43
50
|
|
51
|
+
def scores
|
52
|
+
@scores ||= Hash[response['hits']['hits'].map do |hit|
|
53
|
+
[hit['_id'], hit['_score']]
|
54
|
+
end]
|
55
|
+
end
|
56
|
+
|
57
|
+
def explanations
|
58
|
+
@scores ||= Hash[response['hits']['hits'].map do |hit|
|
59
|
+
[hit['_id'], hit['_explanation']]
|
60
|
+
end]
|
61
|
+
end
|
62
|
+
|
44
63
|
def took
|
45
64
|
@took ||= response['took']
|
46
65
|
end
|
@@ -8,12 +8,14 @@ module Stretchy
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
# used for ensuring a
|
11
|
+
# used for ensuring a consistent index in specs
|
12
12
|
def refresh
|
13
|
+
Stretchy.log("Refreshing index: #{index_name}")
|
13
14
|
client.indices.refresh index: index_name
|
14
15
|
end
|
15
16
|
|
16
17
|
def count
|
18
|
+
Stretchy.log("Counting all documents in index: #{index_name}")
|
17
19
|
client.cat.count(index: index_name).split(' ')[2].to_i
|
18
20
|
end
|
19
21
|
|
@@ -30,7 +32,10 @@ module Stretchy
|
|
30
32
|
params[field] = options[field] if options[field]
|
31
33
|
end
|
32
34
|
|
33
|
-
|
35
|
+
Stretchy.log("Querying Elastic:", params)
|
36
|
+
response = client.search(params)
|
37
|
+
Stretchy.log("Received response:", response)
|
38
|
+
response
|
34
39
|
end
|
35
40
|
|
36
41
|
def index(options = {})
|
@@ -38,7 +43,16 @@ module Stretchy
|
|
38
43
|
type = options[:type]
|
39
44
|
body = options[:body]
|
40
45
|
id = options[:id] || options['id'] || body['id'] || body['_id'] || body[:id] || body[:_id]
|
41
|
-
|
46
|
+
params = {
|
47
|
+
index: index,
|
48
|
+
type: type,
|
49
|
+
id: id,
|
50
|
+
body: body
|
51
|
+
}
|
52
|
+
Stretchy.log("Indexing document:", params)
|
53
|
+
response = client.index(params)
|
54
|
+
Stretchy.log("Received response:", response)
|
55
|
+
response
|
42
56
|
end
|
43
57
|
|
44
58
|
def bulk(options = {})
|
@@ -51,23 +65,29 @@ module Stretchy
|
|
51
65
|
document
|
52
66
|
]
|
53
67
|
end
|
54
|
-
|
68
|
+
Stretchy.log("Bulk indexing documents:", {body: requests})
|
69
|
+
response = client.bulk body: requests
|
70
|
+
Stretchy.log("Received response:", response)
|
55
71
|
end
|
56
72
|
|
57
73
|
def exists(_index_name = index_name)
|
74
|
+
Stretchy.log("Checking index existence for: #{_index_name}")
|
58
75
|
client.indices.exists(index: _index_name)
|
59
76
|
end
|
60
77
|
alias :exists? :exists
|
61
78
|
|
62
79
|
def delete(_index_name = index_name)
|
80
|
+
Stretchy.log("Deleting index: #{_index_name}")
|
63
81
|
client.indices.delete(index: _index_name) if exists?(_index_name)
|
64
82
|
end
|
65
83
|
|
66
84
|
def create(_index_name = index_name)
|
85
|
+
Stretchy.log("Creating index: #{_index_name}")
|
67
86
|
client.indices.create(index: _index_name) unless exists?(_index_name)
|
68
87
|
end
|
69
88
|
|
70
89
|
def mapping(_index_name, _type, _body)
|
90
|
+
Stretchy.log("Putting mapping:", {index_name: _index_name, type: _type, body: _body})
|
71
91
|
client.indices.put_mapping(index: _index_name, type: _type, body: _body)
|
72
92
|
end
|
73
93
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Stretchy
|
2
|
+
module Utils
|
3
|
+
class Colorize
|
4
|
+
COLORS = {
|
5
|
+
"default" => "38",
|
6
|
+
"black" => "30",
|
7
|
+
"red" => "31",
|
8
|
+
"green" => "32",
|
9
|
+
"brown" => "33",
|
10
|
+
"blue" => "34",
|
11
|
+
"purple" => "35",
|
12
|
+
"cyan" => "36",
|
13
|
+
"gray" => "37",
|
14
|
+
"dark gray" => "1;30",
|
15
|
+
"light red" => "1;31",
|
16
|
+
"light green" => "1;32",
|
17
|
+
"yellow" => "1;33",
|
18
|
+
"light blue" => "1;34",
|
19
|
+
"light purple" => "1;35",
|
20
|
+
"light cyan" => "1;36",
|
21
|
+
"white" => "1;37"
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
BG_COLORS = {
|
25
|
+
"default" => "0",
|
26
|
+
"black" => "40",
|
27
|
+
"red" => "41",
|
28
|
+
"green" => "42",
|
29
|
+
"brown" => "43",
|
30
|
+
"blue" => "44",
|
31
|
+
"purple" => "45",
|
32
|
+
"cyan" => "46",
|
33
|
+
"gray" => "47",
|
34
|
+
"dark gray" => "100",
|
35
|
+
"light red" => "101",
|
36
|
+
"light green" => "102",
|
37
|
+
"yellow" => "103",
|
38
|
+
"light blue" => "104",
|
39
|
+
"light purple" => "105",
|
40
|
+
"light cyan" => "106",
|
41
|
+
"white" => "107"
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
def colorize(string, color = "default", bg = "default")
|
45
|
+
color_code = COLORS[color]
|
46
|
+
bg_code = BG_COLORS[bg]
|
47
|
+
return "\033[#{bg_code};#{color_code}m#{string}\033[0m"
|
48
|
+
end
|
49
|
+
|
50
|
+
module ClassMethods
|
51
|
+
def colors
|
52
|
+
Colorize::COLORS
|
53
|
+
end
|
54
|
+
|
55
|
+
def bgs
|
56
|
+
Colorize::BG_COLORS
|
57
|
+
end
|
58
|
+
|
59
|
+
Colorize::COLORS.keys.each do |color|
|
60
|
+
define_method color do |string|
|
61
|
+
color_code = colors[color]
|
62
|
+
bg_code = bgs["default"]
|
63
|
+
string.split("\n").map{|s| "\033[#{bg_code};#{color_code}m#{s}\033[0m" }.join("\n")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
extend ClassMethods
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|