elasticated 1.0.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 +7 -0
- data/.gitignore +35 -0
- data/Gemfile +4 -0
- data/README.md +3 -0
- data/Rakefile +6 -0
- data/elasticated.gemspec +29 -0
- data/lib/elasticated.rb +102 -0
- data/lib/elasticated/aggregation.rb +36 -0
- data/lib/elasticated/aggregations/cardinality_aggregation.rb +15 -0
- data/lib/elasticated/aggregations/count_aggregation.rb +15 -0
- data/lib/elasticated/aggregations/count_distinct_aggregation.rb +15 -0
- data/lib/elasticated/aggregations/count_filtered_aggregation.rb +29 -0
- data/lib/elasticated/aggregations/custom_aggregation.rb +25 -0
- data/lib/elasticated/aggregations/date_histogram_aggregation.rb +35 -0
- data/lib/elasticated/aggregations/filter_aggregation.rb +33 -0
- data/lib/elasticated/aggregations/filter_aggregation_evaluator.rb +22 -0
- data/lib/elasticated/aggregations/group_aggregation.rb +29 -0
- data/lib/elasticated/aggregations/histogram_aggregation.rb +34 -0
- data/lib/elasticated/aggregations/nested_aggregation.rb +30 -0
- data/lib/elasticated/aggregations/range_aggregation.rb +35 -0
- data/lib/elasticated/aggregations/range_aggregation_evaluator.rb +22 -0
- data/lib/elasticated/aggregations/ranges_builder.rb +35 -0
- data/lib/elasticated/aggregations/single_value_aggregation.rb +47 -0
- data/lib/elasticated/aggregations/subaggregated.rb +27 -0
- data/lib/elasticated/aggregations/sum_distinct_aggregation.rb +20 -0
- data/lib/elasticated/aggregations/terms_aggregation.rb +63 -0
- data/lib/elasticated/aggregations/top_hits_aggregation.rb +25 -0
- data/lib/elasticated/block_evaluation.rb +15 -0
- data/lib/elasticated/boolean_clause.rb +43 -0
- data/lib/elasticated/client.rb +84 -0
- data/lib/elasticated/clonable.rb +58 -0
- data/lib/elasticated/conditions/custom_condition.rb +19 -0
- data/lib/elasticated/conditions/exists_condition.rb +11 -0
- data/lib/elasticated/conditions/missing_condition.rb +11 -0
- data/lib/elasticated/conditions/nested_condition.rb +19 -0
- data/lib/elasticated/conditions/range_condition.rb +27 -0
- data/lib/elasticated/conditions/script_condition.rb +22 -0
- data/lib/elasticated/conditions/standard_condition.rb +26 -0
- data/lib/elasticated/conditions/terms_condition.rb +22 -0
- data/lib/elasticated/conditions/wildcard_condition.rb +18 -0
- data/lib/elasticated/conditions_builder.rb +75 -0
- data/lib/elasticated/configurable.rb +9 -0
- data/lib/elasticated/configuration.rb +9 -0
- data/lib/elasticated/default_logger.rb +27 -0
- data/lib/elasticated/delimiters/date_field_delimiter.rb +33 -0
- data/lib/elasticated/delimiters/standard_field_delimiter.rb +33 -0
- data/lib/elasticated/delimiters/term_field_delimiter.rb +24 -0
- data/lib/elasticated/document.rb +46 -0
- data/lib/elasticated/helpers.rb +28 -0
- data/lib/elasticated/index_selector.rb +44 -0
- data/lib/elasticated/inspectionable.rb +9 -0
- data/lib/elasticated/mapping.rb +19 -0
- data/lib/elasticated/mapping/builder.rb +36 -0
- data/lib/elasticated/mapping/fields_builder.rb +148 -0
- data/lib/elasticated/mapping/nested_builder.rb +15 -0
- data/lib/elasticated/mapping/object_builder.rb +15 -0
- data/lib/elasticated/mapping/partial.rb +11 -0
- data/lib/elasticated/mapping/type_builder.rb +14 -0
- data/lib/elasticated/partitioned_repository.rb +27 -0
- data/lib/elasticated/query.rb +159 -0
- data/lib/elasticated/query_aggregations.rb +71 -0
- data/lib/elasticated/query_conditions.rb +89 -0
- data/lib/elasticated/repositories/monthly_partitioned_repository.rb +96 -0
- data/lib/elasticated/repository.rb +139 -0
- data/lib/elasticated/results.rb +43 -0
- data/lib/version.rb +92 -0
- data/spec/aggregation_spec.rb +587 -0
- data/spec/date_field_delimiter_spec.rb +67 -0
- data/spec/document_spec.rb +44 -0
- data/spec/elasticsearch_hit_1.json +14 -0
- data/spec/elasticsearch_response_1.json +29 -0
- data/spec/elasticsearch_response_2.json +44 -0
- data/spec/elasticsearch_top_hits_response.json +20 -0
- data/spec/integration_spec.rb +184 -0
- data/spec/mapping_spec.rb +219 -0
- data/spec/monthly_partitioned_repository_spec.rb +99 -0
- data/spec/query_aggregations_spec.rb +44 -0
- data/spec/query_conditions_spec.rb +314 -0
- data/spec/query_spec.rb +265 -0
- data/spec/results_spec.rb +69 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/term_field_delimiter_spec.rb +39 -0
- metadata +225 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class HistogramAggregation < Aggregation
|
3
|
+
include Subaggregated
|
4
|
+
|
5
|
+
attr_accessor :interval
|
6
|
+
|
7
|
+
def initialize(field, interval, opts={}, &block)
|
8
|
+
self.interval = interval
|
9
|
+
super
|
10
|
+
initialize_subaggregations &block
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_name
|
14
|
+
"group_by_#{field}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
terms = { field: field, interval: interval }
|
19
|
+
terms.merge! extra_params
|
20
|
+
aggregation_struct = { histogram: terms }
|
21
|
+
aggregation_struct.merge! build_subaggregations
|
22
|
+
aggregation_struct
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(response)
|
26
|
+
response['buckets'].inject({}) do |hash, bucket|
|
27
|
+
bucket_hash = { 'count' => bucket['doc_count'] }
|
28
|
+
bucket_hash.merge! parse_subaggregations(bucket)
|
29
|
+
hash.merge bucket['key'] => bucket_hash
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class NestedAggregation < Aggregation
|
3
|
+
include Subaggregated
|
4
|
+
|
5
|
+
attr_accessor :path
|
6
|
+
|
7
|
+
def initialize(path, *args, &block)
|
8
|
+
self.path = path
|
9
|
+
super
|
10
|
+
initialize_subaggregations &block
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_name
|
14
|
+
path
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
body = { nested: { path: path } }
|
19
|
+
body.merge! build_subaggregations
|
20
|
+
body
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse(response)
|
24
|
+
ret = { 'count' => response['doc_count'] }
|
25
|
+
ret.merge! parse_subaggregations(response)
|
26
|
+
ret
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class RangeAggregation < Aggregation
|
3
|
+
include Subaggregated
|
4
|
+
|
5
|
+
attr_accessor :_conditions
|
6
|
+
|
7
|
+
def initialize(field, *args, &block)
|
8
|
+
super
|
9
|
+
initialize_subaggregations RangeAggregationEvaluator.new, &block
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_name
|
13
|
+
"range_over_#{field}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def build
|
17
|
+
# _subaggregations is a RangeAggregationEvaluator, so...
|
18
|
+
ranges = _subaggregations.build_ranges
|
19
|
+
body_struct = { field: field, keyed: true, ranges: ranges }
|
20
|
+
body_struct.merge! extra_params
|
21
|
+
body = { range: body_struct }
|
22
|
+
body.merge! build_subaggregations
|
23
|
+
body
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse(response)
|
27
|
+
response['buckets'].inject({}) do |hash, (key_name, values)|
|
28
|
+
range_body = { 'count' => values['doc_count'] }
|
29
|
+
range_body.merge! parse_subaggregations(values)
|
30
|
+
hash.merge key_name => range_body
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class RangeAggregationEvaluator < QueryAggregations
|
3
|
+
|
4
|
+
include BlockEvaluation
|
5
|
+
|
6
|
+
attr_accessor :_ranges_builder
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
self._ranges_builder = RangesBuilder.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def ranges(&block)
|
14
|
+
_ranges_builder.evaluate block
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_ranges
|
18
|
+
_ranges_builder.build
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class RangesBuilder
|
3
|
+
|
4
|
+
include Clonable
|
5
|
+
include BlockEvaluation
|
6
|
+
|
7
|
+
attr_accessor :_ranges
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self._ranges = Array.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def less_equal(value, range_name=nil)
|
14
|
+
range_name ||= "less_equal_#{value}"
|
15
|
+
_ranges << { key: range_name, to: value }
|
16
|
+
end
|
17
|
+
alias_method :le, :less_equal
|
18
|
+
|
19
|
+
def greater_equal(value, range_name=nil)
|
20
|
+
range_name ||= "greater_equal_#{value}"
|
21
|
+
_ranges << { key: range_name, from: value }
|
22
|
+
end
|
23
|
+
alias_method :ge, :greater_equal
|
24
|
+
|
25
|
+
def between(min_value, max_value, range_name=nil)
|
26
|
+
range_name ||= "between_#{min_value}_and_#{max_value}"
|
27
|
+
_ranges << { key: range_name, from: min_value, to: max_value }
|
28
|
+
end
|
29
|
+
|
30
|
+
def build
|
31
|
+
_ranges
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class SingleValueAggregation < Aggregation
|
3
|
+
|
4
|
+
# abstract class
|
5
|
+
# child must implement operation()
|
6
|
+
|
7
|
+
def default_name
|
8
|
+
"#{operation}_#{field}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def build
|
12
|
+
operation_info = { field: field }
|
13
|
+
operation_info.merge! extra_params
|
14
|
+
{ operation => operation_info }
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse(response)
|
18
|
+
response['value'] || 0
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class MaxAggregation < SingleValueAggregation
|
24
|
+
def operation
|
25
|
+
:max
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class MinAggregation < SingleValueAggregation
|
30
|
+
def operation
|
31
|
+
:min
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class SumAggregation < SingleValueAggregation
|
36
|
+
def operation
|
37
|
+
:sum
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class AvgAggregation < SingleValueAggregation
|
42
|
+
def operation
|
43
|
+
:avg
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Elasticated
|
2
|
+
module Subaggregated
|
3
|
+
|
4
|
+
attr_accessor :_subaggregations
|
5
|
+
|
6
|
+
def initialize_subaggregations(query_aggregations=nil, &block)
|
7
|
+
self._subaggregations = query_aggregations || QueryAggregations.new
|
8
|
+
_subaggregations.evaluate block if block
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def build_subaggregations
|
14
|
+
hash = _subaggregations.build
|
15
|
+
if hash.empty?
|
16
|
+
{}
|
17
|
+
else
|
18
|
+
{ aggs: hash }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_subaggregations(response)
|
23
|
+
_subaggregations.parse response
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class SumDistinctAggregation < GroupAggregation
|
3
|
+
# warning! this "thing" extends from a multi-value aggregation but is a single-value!
|
4
|
+
|
5
|
+
attr_accessor :_operation
|
6
|
+
|
7
|
+
def initialize(group_field, operation_type, operation_field, *args)
|
8
|
+
super group_field, *args
|
9
|
+
self._operation = _subaggregations.send operation_type, operation_field
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(response)
|
13
|
+
response['buckets'].inject(0) do |total, element|
|
14
|
+
operation_name = _operation.name.to_s # TODO
|
15
|
+
total += _subaggregations.parse(element)[operation_name] || 0
|
16
|
+
end || 0
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class TermsAggregation < Aggregation
|
3
|
+
|
4
|
+
# abstract class
|
5
|
+
# child must implement 'default_name'
|
6
|
+
# child must implement 'parse'
|
7
|
+
# child should implement 'build'
|
8
|
+
|
9
|
+
# Como usar el parametro order:
|
10
|
+
# Se puede ordenar por un unico criterio haciendo por ejemplo:
|
11
|
+
# group :user, order: :born_date, order_method: :desc
|
12
|
+
# Se puede ordenar por un unico criterio haciendo tambien:
|
13
|
+
# group :user, order: { born_date: :desc }
|
14
|
+
# Se puede ordenar por un unico criterio especificando el metodo en el mismo string:
|
15
|
+
# group :user, order: 'born_date:desc'
|
16
|
+
# Se puede ordenar por multiples criterios haciendo:
|
17
|
+
# group :user, order: { born_date: :desc, first_name: :asc }
|
18
|
+
# Se puede ordenar por multiples criterios especificando el metodo en el mismo string:
|
19
|
+
# group :user, order: ['born_date:desc', 'first_name:asc']
|
20
|
+
|
21
|
+
attr_accessor :size, :order_field, :order_method
|
22
|
+
|
23
|
+
def initialize(field, options={})
|
24
|
+
self.order_field = options.delete(:order) || options.delete(:order_by)
|
25
|
+
self.order_method = options.delete :order_method
|
26
|
+
self.size = options.delete(:size) || options.delete(:limit) || options.delete(:top)
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def build
|
31
|
+
terms = { field: field, size: (size || 0) }
|
32
|
+
if order_field.is_a? Hash
|
33
|
+
if order_field.size == 1
|
34
|
+
terms.merge! order: order_field
|
35
|
+
else
|
36
|
+
terms.merge! order: order_field.map{ |field, method| { field => method } }
|
37
|
+
end
|
38
|
+
elsif order_field.is_a? Array
|
39
|
+
terms.merge! order: order_field.map{ |e| order_criteria_from e }
|
40
|
+
else
|
41
|
+
if order_method
|
42
|
+
terms.merge! order: { order_field => order_method || default_order_method }
|
43
|
+
else
|
44
|
+
terms.merge! order: order_criteria_from(order_field)
|
45
|
+
end
|
46
|
+
end if order_field
|
47
|
+
terms.merge! extra_params
|
48
|
+
{ terms: terms }
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def order_criteria_from(str_or_symbol)
|
54
|
+
field, method = str_or_symbol.to_s.split ':'
|
55
|
+
method ? { field => method } : { str_or_symbol => default_order_method }
|
56
|
+
end
|
57
|
+
|
58
|
+
def default_order_method
|
59
|
+
:desc
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class TopHitsAggregation < Aggregation
|
3
|
+
|
4
|
+
attr_accessor :query
|
5
|
+
|
6
|
+
def initialize(name, *args, &block)
|
7
|
+
super
|
8
|
+
self.query = Query.new
|
9
|
+
query.evaluate block
|
10
|
+
end
|
11
|
+
|
12
|
+
def build
|
13
|
+
{ top_hits: query.build_for_top_hits }
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(response)
|
17
|
+
# total = response['hits']['total']
|
18
|
+
# max_score = response['hits']['max_score']
|
19
|
+
# hits = response['hits']['hits'].map{ |hit| Document.from_elasticsearch_hit hit }
|
20
|
+
# HitsInfo.new total, max_score, hits
|
21
|
+
response['hits']['hits'].map{ |hit| Document.from_elasticsearch_hit hit }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class BooleanClause
|
3
|
+
|
4
|
+
include Clonable
|
5
|
+
|
6
|
+
attr_accessor :conditions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.conditions = Array.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# delimiters
|
13
|
+
|
14
|
+
def fill_delimiter(field_delimiter)
|
15
|
+
conditions.each{ |condition| condition.fill_delimiter field_delimiter }
|
16
|
+
end
|
17
|
+
|
18
|
+
# conditions
|
19
|
+
|
20
|
+
def add(condition)
|
21
|
+
conditions << condition
|
22
|
+
end
|
23
|
+
|
24
|
+
include ConditionsBuilder
|
25
|
+
|
26
|
+
def count
|
27
|
+
conditions.count
|
28
|
+
end
|
29
|
+
|
30
|
+
def empty?
|
31
|
+
count == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def build
|
35
|
+
conditions.map &:build
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_first
|
39
|
+
conditions.first.build
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Elasticated
|
2
|
+
class Client
|
3
|
+
include Configurable
|
4
|
+
|
5
|
+
attr_accessor :transport
|
6
|
+
|
7
|
+
def initialize(opts={})
|
8
|
+
self.transport = ::Elasticsearch::Client.new opts
|
9
|
+
end
|
10
|
+
|
11
|
+
def index_document(document_source, opts={})
|
12
|
+
transport.index opts.merge body: document_source
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_document(document_source, opts={})
|
16
|
+
transport.update opts.merge body: { doc: document_source }
|
17
|
+
end
|
18
|
+
|
19
|
+
def index_exists?(index_name)
|
20
|
+
transport.indices.exists index: index_name
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_index(index_name, opts={})
|
24
|
+
args = { index: index_name }
|
25
|
+
shards = opts[:shards]
|
26
|
+
mapping = opts[:mapping]
|
27
|
+
body = Hash.new
|
28
|
+
body.merge!(settings: { number_of_shards: shards }) if shards
|
29
|
+
body.merge!(mappings: mapping) if mapping
|
30
|
+
args.merge! body: body unless body.empty?
|
31
|
+
log.info "Creating index #{index_name}"
|
32
|
+
transport.indices.create args
|
33
|
+
log.info "Index #{index_name} created"
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_alias(index_name, new_alias)
|
37
|
+
log.info "Putting alias #{new_alias} to index #{index_name}"
|
38
|
+
transport.indices.put_alias index: index_name, name: new_alias
|
39
|
+
log.info "Alias #{new_alias} for index #{index_name} created"
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove_aliases(index_name)
|
43
|
+
aliases = transport.indices.get_aliases[index_name]['aliases'].map(&:first) rescue Array.new
|
44
|
+
aliases.each do |alias_name|
|
45
|
+
transport.indices.delete_alias index: index_name, name: alias_name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove_alias(index_name, index_alias)
|
50
|
+
log.info "Alias #{index_alias} removed from index #{index_name}"
|
51
|
+
transport.indices.delete_alias index: index_name, name: index_alias
|
52
|
+
end
|
53
|
+
|
54
|
+
def refresh_index(index_name)
|
55
|
+
log.debug "Refreshing #{index_name}"
|
56
|
+
transport.indices.refresh index: index_name
|
57
|
+
end
|
58
|
+
|
59
|
+
def refresh
|
60
|
+
transport.indices.refresh
|
61
|
+
end
|
62
|
+
|
63
|
+
def count(body, opts={})
|
64
|
+
log.debug "Elasticsearch count #{body.to_json}"
|
65
|
+
transport.count opts.merge body: body
|
66
|
+
end
|
67
|
+
|
68
|
+
def search(body, opts={})
|
69
|
+
log.debug "Elasticsearch query #{body.to_json}"
|
70
|
+
transport.search opts.merge body: body
|
71
|
+
end
|
72
|
+
|
73
|
+
def scroll(scroll_id, opts={})
|
74
|
+
log.debug "Elasticsearch scroll #{scroll_id}"
|
75
|
+
transport.scroll opts.merge scroll_id: scroll_id
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(body, opts={})
|
79
|
+
log.debug "Elasticsearch delete #{body.to_json}"
|
80
|
+
transport.delete_by_query opts.merge body: body
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|