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.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/Gemfile +4 -0
  4. data/README.md +3 -0
  5. data/Rakefile +6 -0
  6. data/elasticated.gemspec +29 -0
  7. data/lib/elasticated.rb +102 -0
  8. data/lib/elasticated/aggregation.rb +36 -0
  9. data/lib/elasticated/aggregations/cardinality_aggregation.rb +15 -0
  10. data/lib/elasticated/aggregations/count_aggregation.rb +15 -0
  11. data/lib/elasticated/aggregations/count_distinct_aggregation.rb +15 -0
  12. data/lib/elasticated/aggregations/count_filtered_aggregation.rb +29 -0
  13. data/lib/elasticated/aggregations/custom_aggregation.rb +25 -0
  14. data/lib/elasticated/aggregations/date_histogram_aggregation.rb +35 -0
  15. data/lib/elasticated/aggregations/filter_aggregation.rb +33 -0
  16. data/lib/elasticated/aggregations/filter_aggregation_evaluator.rb +22 -0
  17. data/lib/elasticated/aggregations/group_aggregation.rb +29 -0
  18. data/lib/elasticated/aggregations/histogram_aggregation.rb +34 -0
  19. data/lib/elasticated/aggregations/nested_aggregation.rb +30 -0
  20. data/lib/elasticated/aggregations/range_aggregation.rb +35 -0
  21. data/lib/elasticated/aggregations/range_aggregation_evaluator.rb +22 -0
  22. data/lib/elasticated/aggregations/ranges_builder.rb +35 -0
  23. data/lib/elasticated/aggregations/single_value_aggregation.rb +47 -0
  24. data/lib/elasticated/aggregations/subaggregated.rb +27 -0
  25. data/lib/elasticated/aggregations/sum_distinct_aggregation.rb +20 -0
  26. data/lib/elasticated/aggregations/terms_aggregation.rb +63 -0
  27. data/lib/elasticated/aggregations/top_hits_aggregation.rb +25 -0
  28. data/lib/elasticated/block_evaluation.rb +15 -0
  29. data/lib/elasticated/boolean_clause.rb +43 -0
  30. data/lib/elasticated/client.rb +84 -0
  31. data/lib/elasticated/clonable.rb +58 -0
  32. data/lib/elasticated/conditions/custom_condition.rb +19 -0
  33. data/lib/elasticated/conditions/exists_condition.rb +11 -0
  34. data/lib/elasticated/conditions/missing_condition.rb +11 -0
  35. data/lib/elasticated/conditions/nested_condition.rb +19 -0
  36. data/lib/elasticated/conditions/range_condition.rb +27 -0
  37. data/lib/elasticated/conditions/script_condition.rb +22 -0
  38. data/lib/elasticated/conditions/standard_condition.rb +26 -0
  39. data/lib/elasticated/conditions/terms_condition.rb +22 -0
  40. data/lib/elasticated/conditions/wildcard_condition.rb +18 -0
  41. data/lib/elasticated/conditions_builder.rb +75 -0
  42. data/lib/elasticated/configurable.rb +9 -0
  43. data/lib/elasticated/configuration.rb +9 -0
  44. data/lib/elasticated/default_logger.rb +27 -0
  45. data/lib/elasticated/delimiters/date_field_delimiter.rb +33 -0
  46. data/lib/elasticated/delimiters/standard_field_delimiter.rb +33 -0
  47. data/lib/elasticated/delimiters/term_field_delimiter.rb +24 -0
  48. data/lib/elasticated/document.rb +46 -0
  49. data/lib/elasticated/helpers.rb +28 -0
  50. data/lib/elasticated/index_selector.rb +44 -0
  51. data/lib/elasticated/inspectionable.rb +9 -0
  52. data/lib/elasticated/mapping.rb +19 -0
  53. data/lib/elasticated/mapping/builder.rb +36 -0
  54. data/lib/elasticated/mapping/fields_builder.rb +148 -0
  55. data/lib/elasticated/mapping/nested_builder.rb +15 -0
  56. data/lib/elasticated/mapping/object_builder.rb +15 -0
  57. data/lib/elasticated/mapping/partial.rb +11 -0
  58. data/lib/elasticated/mapping/type_builder.rb +14 -0
  59. data/lib/elasticated/partitioned_repository.rb +27 -0
  60. data/lib/elasticated/query.rb +159 -0
  61. data/lib/elasticated/query_aggregations.rb +71 -0
  62. data/lib/elasticated/query_conditions.rb +89 -0
  63. data/lib/elasticated/repositories/monthly_partitioned_repository.rb +96 -0
  64. data/lib/elasticated/repository.rb +139 -0
  65. data/lib/elasticated/results.rb +43 -0
  66. data/lib/version.rb +92 -0
  67. data/spec/aggregation_spec.rb +587 -0
  68. data/spec/date_field_delimiter_spec.rb +67 -0
  69. data/spec/document_spec.rb +44 -0
  70. data/spec/elasticsearch_hit_1.json +14 -0
  71. data/spec/elasticsearch_response_1.json +29 -0
  72. data/spec/elasticsearch_response_2.json +44 -0
  73. data/spec/elasticsearch_top_hits_response.json +20 -0
  74. data/spec/integration_spec.rb +184 -0
  75. data/spec/mapping_spec.rb +219 -0
  76. data/spec/monthly_partitioned_repository_spec.rb +99 -0
  77. data/spec/query_aggregations_spec.rb +44 -0
  78. data/spec/query_conditions_spec.rb +314 -0
  79. data/spec/query_spec.rb +265 -0
  80. data/spec/results_spec.rb +69 -0
  81. data/spec/spec_helper.rb +2 -0
  82. data/spec/term_field_delimiter_spec.rb +39 -0
  83. 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,15 @@
1
+ module Elasticated
2
+ module BlockEvaluation
3
+
4
+ def evaluate(block=nil, obj=nil)
5
+ return unless block
6
+ obj ||= self
7
+ if block.parameters.empty?
8
+ obj.instance_exec &block
9
+ else
10
+ block.call obj
11
+ end
12
+ end
13
+
14
+ end
15
+ 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