elasticated 2.5.5 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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