elasticated 2.5.5 → 3.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -2
  3. data/Rakefile +52 -1
  4. data/elasticated.gemspec +3 -1
  5. data/lib/elasticated.rb +20 -24
  6. data/lib/elasticated/aggregation.rb +3 -6
  7. data/lib/elasticated/aggregations/date_histogram_aggregation.rb +6 -1
  8. data/lib/elasticated/aggregations/filter_aggregation.rb +8 -12
  9. data/lib/elasticated/aggregations/filter_aggregation_evaluator.rb +1 -1
  10. data/lib/elasticated/aggregations/group_aggregation.rb +14 -11
  11. data/lib/elasticated/aggregations/range_aggregation.rb +10 -11
  12. data/lib/elasticated/aggregations/range_aggregation_evaluator.rb +1 -1
  13. data/lib/elasticated/aggregations/ranges_builder.rb +2 -2
  14. data/lib/elasticated/aggregations/safe_date_histogram_aggregation.rb +7 -2
  15. data/lib/elasticated/aggregations/subaggregated.rb +1 -1
  16. data/lib/elasticated/boolean_clause.rb +4 -3
  17. data/lib/elasticated/bulk_actions/create_action.rb +14 -0
  18. data/lib/elasticated/bulk_actions/delete_action.rb +30 -0
  19. data/lib/elasticated/bulk_actions/index_action.rb +35 -0
  20. data/lib/elasticated/bulk_actions/standard_action.rb +22 -0
  21. data/lib/elasticated/bulk_actions/update_action.rb +44 -0
  22. data/lib/elasticated/bulk_actions/upsert_action.rb +14 -0
  23. data/lib/elasticated/bulk_request.rb +58 -0
  24. data/lib/elasticated/bulk_request/response.rb +32 -0
  25. data/lib/elasticated/bulk_request/response_item.rb +39 -0
  26. data/lib/elasticated/client.rb +27 -3
  27. data/lib/elasticated/conditions/custom_condition.rb +3 -3
  28. data/lib/elasticated/conditions/range_condition.rb +5 -2
  29. data/lib/elasticated/conditions/script_condition.rb +3 -3
  30. data/lib/elasticated/conditions/standard_condition.rb +4 -5
  31. data/lib/elasticated/conditions/term_condition.rb +22 -0
  32. data/lib/elasticated/conditions/terms_condition.rb +2 -2
  33. data/lib/elasticated/conditions_builder.rb +19 -4
  34. data/lib/elasticated/delimiters/date_field_delimiter.rb +21 -12
  35. data/lib/elasticated/delimiters/standard_field_delimiter.rb +18 -2
  36. data/lib/elasticated/delimiters/term_field_delimiter.rb +6 -5
  37. data/lib/elasticated/document.rb +20 -1
  38. data/lib/elasticated/enum.rb +17 -0
  39. data/lib/elasticated/index_selector.rb +26 -25
  40. data/lib/elasticated/mapping.rb +2 -4
  41. data/lib/elasticated/mapping/builder.rb +3 -2
  42. data/lib/elasticated/mapping/fields_builder.rb +13 -9
  43. data/lib/elasticated/mapping/object_builder.rb +38 -4
  44. data/lib/elasticated/mapping/type_builder.rb +3 -5
  45. data/lib/elasticated/mixins/block_evaluation.rb +17 -0
  46. data/lib/elasticated/mixins/clonable.rb +60 -0
  47. data/lib/elasticated/mixins/configurable.rb +22 -0
  48. data/lib/elasticated/mixins/inspectionable.rb +16 -0
  49. data/lib/elasticated/partitioned_repository.rb +24 -18
  50. data/lib/elasticated/query.rb +27 -21
  51. data/lib/elasticated/query_aggregations.rb +5 -7
  52. data/lib/elasticated/query_conditions.rb +6 -3
  53. data/lib/elasticated/quick.rb +7 -0
  54. data/lib/elasticated/repository.rb +184 -40
  55. data/lib/elasticated/repository/intelligent_search.rb +3 -3
  56. data/lib/elasticated/repository/normal_search.rb +2 -2
  57. data/lib/elasticated/repository/resumable_search.rb +5 -5
  58. data/lib/elasticated/repository/scan_scroll_search.rb +4 -4
  59. data/lib/elasticated/repository/scroll_search.rb +3 -3
  60. data/lib/elasticated/repository/search.rb +7 -0
  61. data/lib/elasticated/repository/single_page_search.rb +1 -1
  62. data/lib/elasticated/results.rb +14 -0
  63. data/lib/version.rb +18 -25
  64. data/spec/aggregation_spec.rb +95 -16
  65. data/spec/bulk_request_spec.rb +158 -0
  66. data/spec/date_field_delimiter_spec.rb +50 -6
  67. data/spec/document_spec.rb +1 -5
  68. data/spec/integration_spec.rb +7 -7
  69. data/spec/mapping_spec.rb +128 -8
  70. data/spec/partitioned_repository_spec.rb +218 -0
  71. data/spec/query_conditions_spec.rb +98 -45
  72. data/spec/query_spec.rb +21 -28
  73. data/spec/repository_spec.rb +245 -0
  74. data/spec/results_spec.rb +0 -4
  75. data/spec/sample_responses/elasticsearch_bulk_response_1.json +35 -0
  76. data/spec/sample_responses/elasticsearch_bulk_response_2.json +20 -0
  77. data/spec/sample_responses/elasticsearch_count_1.json +8 -0
  78. data/spec/sample_responses/elasticsearch_count_2.json +8 -0
  79. data/spec/sample_responses/elasticsearch_get_response_1.json +10 -0
  80. data/spec/sample_responses/elasticsearch_get_response_2.json +6 -0
  81. data/spec/{elasticsearch_hit_1.json → sample_responses/elasticsearch_hit_1.json} +0 -0
  82. data/spec/sample_responses/elasticsearch_mget_response_1.json +25 -0
  83. data/spec/{elasticsearch_response_1.json → sample_responses/elasticsearch_response_1.json} +0 -0
  84. data/spec/{elasticsearch_response_2.json → sample_responses/elasticsearch_response_2.json} +0 -0
  85. data/spec/{elasticsearch_top_hits_response.json → sample_responses/elasticsearch_top_hits_response.json} +0 -0
  86. data/spec/spec_helper.rb +47 -0
  87. data/spec/spec_helper/fake_index_selector.rb +27 -0
  88. data/spec/term_field_delimiter_spec.rb +8 -8
  89. metadata +80 -26
  90. data/lib/elasticated/block_evaluation.rb +0 -15
  91. data/lib/elasticated/clonable.rb +0 -58
  92. data/lib/elasticated/configurable.rb +0 -20
  93. data/lib/elasticated/date_delimiter_factory.rb +0 -123
  94. data/lib/elasticated/delimiter_visitor.rb +0 -53
  95. data/lib/elasticated/inspectionable.rb +0 -9
  96. data/lib/elasticated/strategy_params_for_query_service.rb +0 -14
  97. data/lib/elasticated/term_delimiter_factory.rb +0 -73
  98. data/spec/delimiter_factory_spec.rb +0 -399
  99. data/spec/strategy_params_for_query_service_spec.rb +0 -387
@@ -1,7 +1,7 @@
1
1
  module Elasticated
2
2
  class Repository
3
3
  class IntelligentSearch < Search
4
- include Configurable
4
+ include Mixins::Configurable
5
5
 
6
6
  # INTELLIGENT SEARCH CASES
7
7
  # ------------ without sorting
@@ -15,14 +15,14 @@ module Elasticated
15
15
  # without size, with offset => normal_pagination
16
16
  # with size, with offset => single_page or normal_pagination
17
17
 
18
- # if the query is aggregated and the search strategy is use an
18
+ # if the query is aggregated and the search strategy is a
19
19
  # scroll-like alternative, we must do a 2-step search process
20
20
 
21
21
  def best_search_method
22
22
  if query.limited? && !query.heavy_for?(repository)
23
23
  SinglePageSearch.new repository, query, aggregated, opts
24
24
  else
25
- if query.has_offset?
25
+ if query.offset?
26
26
  NormalSearch.new repository, query, aggregated, opts
27
27
  else
28
28
  if query.sorted?
@@ -10,7 +10,7 @@ module Elasticated
10
10
 
11
11
  override! body, size, offset
12
12
  response = client.search body, opts
13
- results = Results.parse response, query
13
+ results = parse_and_prepare_results response, query
14
14
 
15
15
  target_size = query.limited? ? query._size : (results.hits.total - offset)
16
16
  total_pages = (target_size / size.to_f).ceil
@@ -25,7 +25,7 @@ module Elasticated
25
25
  override! body, size, offset
26
26
 
27
27
  response = client.search body, opts
28
- new_results = Results.parse response
28
+ new_results = parse_and_prepare_results response
29
29
  results.append new_results
30
30
  break if new_results.count < size
31
31
 
@@ -2,8 +2,8 @@ module Elasticated
2
2
  class Repository
3
3
  class ResumableSearch < Search
4
4
 
5
- def self.from_scroll_id(client, scroll_id)
6
- search = new client, nil
5
+ def self.from_scroll_id(repository, scroll_id)
6
+ search = new repository, nil
7
7
  search.scroll_id = scroll_id
8
8
  search
9
9
  end
@@ -15,12 +15,12 @@ module Elasticated
15
15
  aggregation_results = if aggregated
16
16
  body = query.build_for_aggregations
17
17
  response = client.search body, opts
18
- Results.parse response, query
18
+ parse_and_prepare_results response, query
19
19
  end
20
20
  # search
21
21
  body = query.build_for_search
22
22
  response = client.search body, opts.merge(scroll: scroll_expiration_time, size: scroll_page_size)
23
- results = Results.parse response
23
+ results = parse_and_prepare_results response
24
24
  results.append aggregation_results if aggregation_results
25
25
  self.scroll_id = results.scroll_id
26
26
  mark_completed! if results.documents.count < scroll_page_size
@@ -31,7 +31,7 @@ module Elasticated
31
31
  raise "No scroll_id present" unless scroll_id
32
32
  raise "No more information to fetch: scroll completed" if completed?
33
33
  response = client.scroll scroll_id, scroll: scroll_expiration_time
34
- results = Results.parse response
34
+ results = parse_and_prepare_results response
35
35
  self.scroll_id = results.scroll_id
36
36
  mark_completed! if results.documents.empty?
37
37
  results
@@ -5,7 +5,7 @@ module Elasticated
5
5
  def fetch_aggregations
6
6
  body = query.build_for_aggregations
7
7
  response = client.search body, opts
8
- Results.parse response, query
8
+ parse_and_prepare_results response, query
9
9
  end
10
10
 
11
11
  def execute
@@ -14,17 +14,17 @@ module Elasticated
14
14
  if aggregated
15
15
  body = query.build_for_aggregations
16
16
  response = client.search body, opts
17
- results = Results.parse response, query
17
+ results = parse_and_prepare_results response, query
18
18
  end
19
19
  # search
20
20
  body = query.build_for_search
21
21
  response = client.search body, opts.merge(search_type: 'scan', scroll: scroll_expiration_time, size: scroll_page_size)
22
- results = Results.parse response
22
+ results = parse_and_prepare_results response
23
23
  results.append fetch_aggregations if aggregated
24
24
  doc_count = 0
25
25
  loop do
26
26
  response = client.scroll results.scroll_id, scroll: scroll_expiration_time
27
- new_results = Results.parse response
27
+ new_results = parse_and_prepare_results response
28
28
  hits = new_results.documents
29
29
  break if hits.empty?
30
30
  if query.limited? && (doc_count + hits.count > query._size)
@@ -5,7 +5,7 @@ module Elasticated
5
5
  def fetch_aggregations
6
6
  body = query.build_for_aggregations
7
7
  response = client.search body, opts
8
- Results.parse response, query
8
+ parse_and_prepare_results response, query
9
9
  end
10
10
 
11
11
  def execute
@@ -14,7 +14,7 @@ module Elasticated
14
14
 
15
15
  override! body
16
16
  response = client.search body, opts.merge(scroll: scroll_expiration_time, size: scroll_page_size)
17
- results = Results.parse response, query
17
+ results = parse_and_prepare_results response, query
18
18
  results.append fetch_aggregations if aggregated
19
19
 
20
20
  target_size = query.limited? ? query._size : results.hits.total
@@ -26,7 +26,7 @@ module Elasticated
26
26
  break if current_page >= total_pages
27
27
 
28
28
  response = client.scroll results.scroll_id, scroll: scroll_expiration_time
29
- new_results = Results.parse response
29
+ new_results = parse_and_prepare_results response
30
30
  hits = new_results.documents
31
31
  doc_count = results.documents.count
32
32
  new_results.documents = hits.first(target_size - doc_count) if query.limited? && (doc_count + hits.count > target_size)
@@ -40,6 +40,13 @@ module Elasticated
40
40
  repository.scroll_page_size
41
41
  end
42
42
 
43
+ # results preparation
44
+
45
+ def parse_and_prepare_results(response, query=nil)
46
+ results = Results.parse response, query
47
+ repository.send :prepare_served_results, results # TODO fixme
48
+ end
49
+
43
50
  end
44
51
  end
45
52
  end
@@ -5,7 +5,7 @@ module Elasticated
5
5
  def execute
6
6
  body = aggregated ? query.build_for_aggregated_search : query.build_for_search
7
7
  response = client.search body, opts
8
- Results.parse response, query
8
+ parse_and_prepare_results response, query
9
9
  end
10
10
 
11
11
  end
@@ -29,6 +29,8 @@ module Elasticated
29
29
  alias_method :from_elasticsearch_response, :parse
30
30
  end
31
31
 
32
+ include Mixins::Inspectionable
33
+
32
34
  attr_accessor :scroll_id
33
35
  attr_accessor :took, :timed_out
34
36
  attr_accessor :shards # methods: total, successful, failed
@@ -58,5 +60,17 @@ module Elasticated
58
60
  documents.count
59
61
  end
60
62
 
63
+ def text_for_inspect
64
+ text = "#{hits.total} hits"
65
+ text = case documents.count
66
+ when 0; "#{text}, no documents"
67
+ when 1; "#{text}, 1 document"
68
+ else; "#{text}, #{documents.count} documents"
69
+ end
70
+ text = aggregations ? "#{text}, with aggregations" : "#{text}, no aggregations"
71
+ text = "#{text}, including scroll_id" if scroll_id
72
+ text
73
+ end
74
+
61
75
  end
62
76
  end
data/lib/version.rb CHANGED
@@ -1,33 +1,26 @@
1
1
  module Elasticated
2
- VERSION = '2.5.5'
2
+ VERSION = '3.0.0'
3
3
  end
4
4
 
5
5
  # Changelog
6
6
 
7
- # 2.5.5
8
- # Se extrae un metodo en IndexSelector para asegurar compatibilidad hacia atras
9
-
10
- # 2.5.4
11
- # Se corrige un bug del date delimiter
12
-
13
- # 2.5.3
14
- # Se corrige un bug en el delimiter visitor
15
-
16
- # 2.5.2
17
- # Se corrige el visitor para que los filtros de subaggregations se representen en conjuntos por aggregation y no por filtro
18
-
19
- # 2.5.1
20
- # Se agregaron test para el merge delimiters en term delimiter factory que ahora considera arrays de delimiters vacios
21
-
22
- # 2.5.0
23
- # Ahora los delimiters son inmutables
24
- # Se puede delimitar por aggregations
25
-
26
- # 2.4.0
27
- # Ahora se puede ordenar un search por un campo, script o hash custom
28
-
29
- # 2.3.0
30
- # Ahora DateFieldDelimiter admite multiples fechas declaradas en un TermsCondition
7
+ # Ahora Repository#execute_aggregated_search puede definir automaticamente si ejecutar las aggregations o no
8
+ # Se agrega el alias 'range' para la condicion 'between'
9
+ # Se agrega el alias 'terms' para la condicion 'equal'
10
+ # Se agregan get, delete, y mget a Repository
11
+ # Ahora el GroupAggregation se puede invocar como 'terms' ademas de 'group'
12
+ # Se elimina la opcion 'include_count' de todas las aggregations que la utilizaban
13
+ # Ya no se permite pasar la opcion 'compact' a aggregations que posean subaggregations
14
+ # Ahora se puede llamar a bloque de aggregations por medio del alias 'aggs'
15
+ # Ahora Repository#update_document actualiza parcialmente el documento
16
+ # Cambian los pre-action y post-action preparation methods
17
+ # Se incluye la extension 'test cluster' y las tareas rake para administrarlo
18
+ # ResumableSearch ahora hace 'restore' de los documentos que devuelve
19
+ # Se agregan mejores vistas de inspeccion para Query, Results y Document
20
+ # Ahora se pueden pedir los documentos sin source
21
+ # Ahora el PartitionedRepository soporta :now
22
+ # Se agrega TermCondition
23
+ # Ahora se puede especificar si el mapping es dinamico
31
24
 
32
25
  # 2.2.0
33
26
  # Se agrega la funcionalidad para especificar un orden aleatorio en las queries
@@ -147,9 +147,9 @@ module Elasticated
147
147
  it "should map a group aggregation response" do
148
148
  agg = GroupAggregation.new :field
149
149
  response = { 'buckets' => [
150
- { 'key' => 'value1', 'doc_count' => 'count1' },
151
- { 'key' => 'value2', 'doc_count' => 'count2' },
152
- { 'key' => 'value3', 'doc_count' => 'count3' }
150
+ { 'key' => 'value1', 'doc_count' => 'count1', 'key_as_string' => 'value1str' },
151
+ { 'key' => 'value2', 'doc_count' => 'count2', 'key_as_string' => 'value2str' },
152
+ { 'key' => 'value3', 'doc_count' => 'count3', 'key_as_string' => 'value3str' }
153
153
  ] }
154
154
  expect(agg.parse(response)).to eq({
155
155
  'value1' => { 'count' => 'count1' },
@@ -158,12 +158,26 @@ module Elasticated
158
158
  })
159
159
  end
160
160
 
161
+ it "should map a group aggregation response including the 'key_as_string' value" do
162
+ agg = GroupAggregation.new :field, key_as_string: true
163
+ response = { 'buckets' => [
164
+ { 'key' => 'value1', 'doc_count' => 'count1', 'key_as_string' => 'value1str' },
165
+ { 'key' => 'value2', 'doc_count' => 'count2', 'key_as_string' => 'value2str' },
166
+ { 'key' => 'value3', 'doc_count' => 'count3', 'key_as_string' => 'value3str' }
167
+ ] }
168
+ expect(agg.parse(response)).to eq({
169
+ 'value1' => { 'count' => 'count1', 'key_as_string' => 'value1str' },
170
+ 'value2' => { 'count' => 'count2', 'key_as_string' => 'value2str' },
171
+ 'value3' => { 'count' => 'count3', 'key_as_string' => 'value3str' }
172
+ })
173
+ end
174
+
161
175
  it "should map a group aggregation response compacted" do
162
176
  agg = GroupAggregation.new :field, compact: true
163
177
  response = { 'buckets' => [
164
- { 'key' => 'value1', 'doc_count' => 'count1' },
165
- { 'key' => 'value2', 'doc_count' => 'count2' },
166
- { 'key' => 'value3', 'doc_count' => 'count3' }
178
+ { 'key' => 'value1', 'doc_count' => 'count1', 'key_as_string' => 'value1str' },
179
+ { 'key' => 'value2', 'doc_count' => 'count2', 'key_as_string' => 'value2str' },
180
+ { 'key' => 'value3', 'doc_count' => 'count3', 'key_as_string' => 'value3str' }
167
181
  ] }
168
182
  expect(agg.parse(response)).to eq({
169
183
  'value1' => 'count1',
@@ -172,6 +186,16 @@ module Elasticated
172
186
  })
173
187
  end
174
188
 
189
+ it "should raise if a group aggregation is compacted but has subaggregations" do
190
+ agg = GroupAggregation.new(:field, compact: true){ max :other_field }
191
+ response = { 'buckets' => [
192
+ { 'key' => 'value1', 'doc_count' => 'count1', 'key_as_string' => 'value1str' },
193
+ { 'key' => 'value2', 'doc_count' => 'count2', 'key_as_string' => 'value2str' },
194
+ { 'key' => 'value3', 'doc_count' => 'count3', 'key_as_string' => 'value3str' }
195
+ ] }
196
+ expect{ agg.parse(response) }.to raise_error RuntimeError
197
+ end
198
+
175
199
  end
176
200
 
177
201
  describe ValueCountAggregation do
@@ -251,12 +275,6 @@ module Elasticated
251
275
  expect(agg.build).to eq expected_result
252
276
  end
253
277
 
254
- it "should build a date histogram aggregation with offset for date" do
255
- agg = DateHistogramAggregation.new :date, offset: '+6h'
256
- expected_result = { date_histogram: { field: :date, interval: 'day', format: 'yyyy-MM-dd', offset: '+6h' } }
257
- expect(agg.build).to eq expected_result
258
- end
259
-
260
278
  it "should build a date histogram aggregation with subaggregations" do
261
279
  agg = DateHistogramAggregation.new :date do |docs|
262
280
  docs.max :numeric_field
@@ -474,6 +492,42 @@ module Elasticated
474
492
  })
475
493
  end
476
494
 
495
+ it "should map a 'range' response with subaggregations" do
496
+ agg = RangeAggregation.new :a_field do |a|
497
+ a.ranges do |r|
498
+ r.greater_equal :max_value, 'high_values'
499
+ r.less_equal :min_value
500
+ r.between :min_value, :max_value
501
+ end
502
+ a.max :other_field
503
+ end
504
+ response = {
505
+ 'buckets' => {
506
+ 'high_values' => {
507
+ 'from' => 'max_value',
508
+ 'doc_count' => 5,
509
+ 'max_other_field' => { 'value' => 15 }
510
+ },
511
+ 'less_equal_min_value' => {
512
+ 'to' => 'min_value',
513
+ 'doc_count' => 3,
514
+ 'max_other_field' => { 'value' => 15 }
515
+ },
516
+ 'between_min_value_and_max_value' => {
517
+ 'from' => 'min_value',
518
+ 'to' => 'max_value',
519
+ 'doc_count' => 9,
520
+ 'max_other_field' => { 'value' => 15 }
521
+ }
522
+ }
523
+ }
524
+ expect(agg.parse(response)).to eq({
525
+ 'high_values' => { 'count' => 5, 'max_other_field' => 15 },
526
+ 'less_equal_min_value' => { 'count' => 3, 'max_other_field' => 15 },
527
+ 'between_min_value_and_max_value' => { 'count' => 9, 'max_other_field' => 15 }
528
+ })
529
+ end
530
+
477
531
  it "should map a 'range' response compacted" do
478
532
  agg = RangeAggregation.new :a_field, compact: true do |a|
479
533
  a.ranges do |r|
@@ -506,6 +560,35 @@ module Elasticated
506
560
  })
507
561
  end
508
562
 
563
+ it "should raise on a subaggregated and compacted 'range'" do
564
+ agg = RangeAggregation.new :a_field, compact: true do |a|
565
+ a.ranges do |r|
566
+ r.greater_equal :max_value, 'high_values'
567
+ r.less_equal :min_value
568
+ r.between :min_value, :max_value
569
+ end
570
+ a.max :other_field
571
+ end
572
+ response = {
573
+ 'buckets' => {
574
+ 'high_values' => {
575
+ 'from' => 'max_value',
576
+ 'doc_count' => 5
577
+ },
578
+ 'less_equal_min_value' => {
579
+ 'to' => 'min_value',
580
+ 'doc_count' => 3
581
+ },
582
+ 'between_min_value_and_max_value' => {
583
+ 'from' => 'min_value',
584
+ 'to' => 'max_value',
585
+ 'doc_count' => 9
586
+ }
587
+ }
588
+ }
589
+ expect{ agg.parse(response) }.to raise_error RuntimeError
590
+ end
591
+
509
592
  end
510
593
 
511
594
  describe SumDistinctAggregation do
@@ -550,10 +633,6 @@ module Elasticated
550
633
 
551
634
  describe TopHitsAggregation do
552
635
 
553
- def open_response(name)
554
- JSON.parse File.read "spec/#{name}.json"
555
- end
556
-
557
636
  it "should build a 'top hits' aggregation" do
558
637
  agg = TopHitsAggregation.new :some_name do |q|
559
638
  q.size 5
@@ -0,0 +1,158 @@
1
+ require_relative 'spec_helper'
2
+
3
+ module Elasticated
4
+ describe BulkRequest do
5
+
6
+ let :repository do
7
+ Repository.new
8
+ end
9
+
10
+ context "in the index-like actions" do
11
+
12
+ let :document do
13
+ Document.create do |doc|
14
+ doc.id = '2'
15
+ doc.type = 'post'
16
+ doc.index = 'insights'
17
+ doc.source = { some_field: 'some_value' }
18
+ end
19
+ end
20
+
21
+ it "should build an index action (with only a doc)" do
22
+ action = BulkActions::IndexAction.new repository, document
23
+ expect(action.build).to eq [
24
+ { index: { _index: "insights", _type: "post", _id: "2" } },
25
+ { some_field: "some_value" }
26
+ ]
27
+ end
28
+
29
+ it "should build an index action (with a doc and other params)" do
30
+ action = BulkActions::IndexAction.new repository, document, id: "5", type: 'faketype', index: 'fakeindex'
31
+ expect(action.build).to eq [
32
+ { index: { _index: "fakeindex", _type: "faketype", _id: "5" } },
33
+ { some_field: "some_value" }
34
+ ]
35
+ end
36
+
37
+ it "should build an create action (with only a doc)" do
38
+ action = BulkActions::CreateAction.new repository, document
39
+ expect(action.build).to eq [
40
+ { create: { _index: "insights", _type: "post", _id: "2" } },
41
+ { some_field: "some_value" }
42
+ ]
43
+ end
44
+
45
+ it "should build an create action (with a doc and other params)" do
46
+ action = BulkActions::CreateAction.new repository, document, id: "5", type: 'faketype', index: 'fakeindex'
47
+ expect(action.build).to eq [
48
+ { create: { _index: "fakeindex", _type: "faketype", _id: "5" } },
49
+ { some_field: "some_value" }
50
+ ]
51
+ end
52
+
53
+ end
54
+
55
+ context "in the update-like actions" do
56
+
57
+ let(:source){ { some_field: 'some_value' } }
58
+
59
+ it "should build an update action" do
60
+ action = BulkActions::UpdateAction.new repository, "2", type: "post", index: "insights", source: source
61
+ expect(action.build).to eq [
62
+ { update: { _index: "insights", _type: "post", _id: "2" } },
63
+ { doc: { some_field: "some_value" } }
64
+ ]
65
+ end
66
+
67
+ it "should build an upsert action" do
68
+ action = BulkActions::UpsertAction.new repository, "2", type: "post", index: "insights", source: source
69
+ expect(action.build).to eq [
70
+ { update: { _index: "insights", _type: "post", _id: "2" } },
71
+ { doc: { some_field: "some_value" }, doc_as_upsert: true }
72
+ ]
73
+ end
74
+
75
+ end
76
+
77
+ context "in the delete-like actions" do
78
+
79
+ it "should build a delete action" do
80
+ action = BulkActions::DeleteAction.new repository, '2', index: 'insights', type: 'post'
81
+ expect(action.build).to eq [
82
+ { delete: { _index: "insights", _type: "post", _id: "2" } }
83
+ ]
84
+ end
85
+
86
+ end
87
+
88
+ context "the bulk request" do
89
+
90
+ let :source do
91
+ { some_field: 'some_value' }
92
+ end
93
+
94
+ let :document do
95
+ Document.create do |doc|
96
+ doc.id = '2'
97
+ doc.type = 'post'
98
+ doc.index = 'insights'
99
+ doc.source = source
100
+ end
101
+ end
102
+
103
+ it "should build a serie of actions correctly" do
104
+ bulk_request = BulkRequest.build_over repository do |r|
105
+ r.delete_document "2", type: 'post', index: 'insights'
106
+ r.create_document document
107
+ r.update_document "2", type: 'post', index: 'insights', source: source
108
+ r.index_document document
109
+ end
110
+ expect(bulk_request.to_text).to eq \
111
+ '{"delete":{"_index":"insights","_type":"post","_id":"2"}}' + "\n" +
112
+ '{"create":{"_index":"insights","_type":"post","_id":"2"}}' + "\n" +
113
+ '{"some_field":"some_value"}' + "\n" +
114
+ '{"update":{"_index":"insights","_type":"post","_id":"2"}}' + "\n" +
115
+ '{"doc":{"some_field":"some_value"}}' + "\n" +
116
+ '{"index":{"_index":"insights","_type":"post","_id":"2"}}' + "\n" +
117
+ '{"some_field":"some_value"}'
118
+ end
119
+
120
+ it "should parse a simple response" do
121
+ elasticsearch_response = open_response 'elasticsearch_bulk_response_1'
122
+ bulk_request = BulkRequest.build_over repository # actions actually doesnt matter
123
+ response = bulk_request.parse elasticsearch_response
124
+ expect(response.errors?).to be false
125
+ expect(response.items.count).to eql 4
126
+ item1 = response.items[0]
127
+ expect(item1.action).to eq 'delete'
128
+ expect(item1.index).to eq 'website'
129
+ expect(item1.type).to eq 'blog'
130
+ expect(item1.id).to eq '123'
131
+ expect(item1.version).to eql 2
132
+ expect(item1.status).to eq 200
133
+ expect(item1.found).to be true
134
+ expect(item1.error?).to be false
135
+ expect(item1.completed?).to be true
136
+ end
137
+
138
+ it "should parse a simple response with errors" do
139
+ elasticsearch_response = open_response 'elasticsearch_bulk_response_2'
140
+ bulk_request = BulkRequest.build_over repository # actions actually doesnt matter
141
+ response = bulk_request.parse elasticsearch_response
142
+ expect(response.errors?).to be true
143
+ expect(response.items.count).to eql 2
144
+ item1 = response.items[0]
145
+ expect(item1.action).to eq 'create'
146
+ expect(item1.index).to eq 'website'
147
+ expect(item1.type).to eq 'blog'
148
+ expect(item1.id).to eq '123'
149
+ expect(item1.status).to eq 409
150
+ expect(item1.error).to eq 'DocumentAlreadyExistsException[[website][4] [blog][123]: document already exists]'
151
+ expect(item1.error?).to be true
152
+ expect(item1.completed?).to be false
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+ end