artirix_data_models 0.5.0 → 0.6.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6a90c9b7956cc31c38374094fd416d8d4e9dd09
4
- data.tar.gz: 3cf3edcbf15f24b14b7beb57943b9f93bb12dbaa
3
+ metadata.gz: 4c953eedf5e26123d9a2136591abf12b0a6be6ff
4
+ data.tar.gz: a3966ad6e6704d526176554e4b9a5d332dde1264
5
5
  SHA512:
6
- metadata.gz: 568c1f293797134592df3bd5bca0ba3daacf0ede0aff7770004b5a3aae4821f9ae8eb2df8a668607bff2bfcb4f31bc6ea1a8c334b213084c19763ddebe578714
7
- data.tar.gz: 5e27b33a022ccb060b174fb4466fb62bea449cc8c6ff8a5b3b405482b20f0f7c14eba73eeeddd4f62b31d447b0f2999d0afa8cf2485f095eda3ff3d425dea8bc
6
+ metadata.gz: 2c79dbd9b140a1e584bad0d110d38a6a6ae384761d41ea296d78a58ebc81c44cea3691cbc5ed5ef00cdd9e5fb92d565549946ffeaf356748b57fac362f155677
7
+ data.tar.gz: afc8d927f2082134cb81a9f118f73b799882a019fccc20760886c2d34a2fc0cebfae479794523e735eccddf9b5110edecd1d6b04bd2c61fac00dcc0ebe53913c
data/.gitignore CHANGED
@@ -12,4 +12,5 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
- .idea
15
+ .idea
16
+ /manual_sandbox.rb
data/README.md CHANGED
@@ -224,7 +224,30 @@ end
224
224
 
225
225
  ## Changes
226
226
 
227
- ### v.0.5.0
227
+ ### 0.6.2
228
+
229
+ *Fixed Breaking Change*: removal of `Aggregation.from_json` static method. Now back but delegating to default factory method is `aggregation_factory.aggregation_from_json` in the Aggregation Factory *instance*.
230
+
231
+ - EsCollection's aggregations can now be build based on raw ElasticSearch responses, including nested aggregations. It ignores any aggregation that does not have "buckets", so that nested aggs for `global` or `filtered` are skipped and only the ones with real data are used. (TODO: write docs. In the mean time, have a look at the specs).
232
+ - added `aggregation` method to `Aggregation::Value` class, and also the aggs to the `data_hash` if they are present.
233
+
234
+ ### 0.5.0
228
235
 
229
236
  - opening gem as is to the public.
230
- - still a lot of TODOs in the documentation
237
+ - still a lot of TODOs in the documentation
238
+
239
+
240
+ ## Yanked versions
241
+
242
+
243
+ ### ~0.6.1~
244
+
245
+ Yanked because of breaking change introduction: removal of `Aggregation.from_json` method
246
+
247
+ - added `aggregation` method to `Aggregation::Value` class, and also the aggs to the `data_hash` if they are present.
248
+
249
+ ### ~v0.6.0~
250
+
251
+ Yanked because of breaking change introduction: removal of `Aggregation.from_json` method
252
+
253
+ - EsCollection's aggregations can now be build based on raw ElasticSearch responses, including nested aggregations. It ignores any aggregation that does not have "buckets", so that nested aggs for `global` or `filtered` are skipped and only the ones with real data are used. (TODO: write docs. In the mean time, have a look at the specs).
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'faraday'
25
25
  spec.add_dependency 'keyword_init', '~> 1.3'
26
26
  spec.add_dependency 'naught'
27
+ spec.add_dependency 'hashie'
27
28
 
28
29
  spec.add_development_dependency 'kaminari', '~> 0.16'
29
30
  spec.add_development_dependency 'will_paginate', '~> 3.0'
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'artirix_data_models'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require 'pry'
11
+ Pry.start
12
+
13
+ # require 'irb'
14
+ # IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -6,11 +6,7 @@ module ArtirixDataModels
6
6
  delegate :each, :empty?, to: :buckets
7
7
 
8
8
  def self.from_json(definition, value_class = Value)
9
- buckets = definition[:buckets].map do |bucket|
10
- value_class.new definition[:name].to_sym, bucket[:name], bucket[:count]
11
- end
12
-
13
- new definition[:name].to_sym, buckets
9
+ DAORegistry.aggregations_factory.aggregation_from_json(definition, value_class: value_class, aggregation_class: self)
14
10
  end
15
11
 
16
12
  def pretty_name
@@ -32,7 +28,14 @@ module ArtirixDataModels
32
28
  }
33
29
  end
34
30
 
35
- class Value < Struct.new(:aggregation_name, :name, :count)
31
+ class Value < Struct.new(:aggregation_name, :name, :count, :aggregations)
32
+
33
+ def aggregations
34
+ Array(super)
35
+ end
36
+
37
+ alias_method :nested_aggregations, :aggregations
38
+ alias_method :nested_aggregations=, :aggregations=
36
39
 
37
40
  def pretty_name
38
41
  tranlsation_key = "aggregations.#{aggregation_name.to_s.gsub('.', '_')}.buckets.#{name.to_s.gsub('.', '_')}"
@@ -47,7 +50,20 @@ module ArtirixDataModels
47
50
  count == 0
48
51
  end
49
52
 
53
+ def aggregation(name)
54
+ n = name.to_sym
55
+ aggregations.detect { |x| x.name == n }
56
+ end
57
+
50
58
  def data_hash
59
+ basic_data_hash.tap do |h|
60
+ if aggregations.present?
61
+ h[:aggregations] = aggregations.map(&:data_hash)
62
+ end
63
+ end
64
+ end
65
+
66
+ def basic_data_hash
51
67
  {
52
68
  name: name,
53
69
  count: count
@@ -0,0 +1,47 @@
1
+ module ArtirixDataModels
2
+
3
+ class AggregationBuilder
4
+ attr_reader :aggregations_factory, :definition, :value_class, :aggregation_class
5
+
6
+ def initialize(aggregations_factory:, definition:, aggregation_class: Aggregation, value_class: Aggregation::Value)
7
+ @aggregations_factory = aggregations_factory
8
+ @definition = definition
9
+ @aggregation_class = aggregation_class
10
+ @value_class = value_class
11
+ end
12
+
13
+ def build
14
+ aggregation_class.new agg_name, buckets
15
+ end
16
+
17
+ alias_method :call, :build
18
+
19
+ private
20
+ def buckets
21
+ definition[:buckets].map do |bucket|
22
+ build_bucket(bucket)
23
+ end
24
+ end
25
+
26
+ def build_bucket(bucket)
27
+ name = bucket[:name]
28
+ count = bucket[:count]
29
+ nested_aggs = nested_aggs_from(bucket)
30
+
31
+ value_class.new agg_name, name, count, nested_aggs
32
+ end
33
+
34
+ def nested_aggs_from(bucket)
35
+ raw_nested_aggs = bucket.fetch(:aggregations) { [] }
36
+
37
+ raw_nested_aggs.map do |nested_agg|
38
+ aggregations_factory.build_from_json nested_agg, value_class
39
+ end
40
+ end
41
+
42
+ def agg_name
43
+ definition[:name].to_sym
44
+ end
45
+
46
+ end
47
+ end
@@ -1,28 +1,18 @@
1
1
  module ArtirixDataModels
2
2
  class AggregationsFactory
3
- DEFAULT_FACTORY = ->(aggregation) { Aggregation.from_json aggregation }
4
3
  DEFAULT_COLLECTION_CLASS_NAME = ''.freeze
5
4
 
6
- # singleton instance
7
5
  def initialize
8
6
  @_loaders = Hash.new { |h, k| h[k] = {} }
9
7
  setup_config
10
8
  end
11
9
 
10
+ # SETUP AND CONFIG MANAGEMENT
11
+
12
12
  def setup_config
13
13
  # To be Extended
14
14
  end
15
15
 
16
- def build_from_json(aggregation, collection_class = nil)
17
- get_loader(aggregation[:name], collection_class).call aggregation
18
- end
19
-
20
- def get_loader(aggregation_name, collection_class)
21
- @_loaders[collection_class.to_s][aggregation_name.to_s] ||
22
- @_loaders[DEFAULT_COLLECTION_CLASS_NAME][aggregation_name.to_s] ||
23
- DEFAULT_FACTORY
24
- end
25
-
26
16
  def set_loader(aggregation_name, collection_class = nil, loader = nil, &block)
27
17
  if block
28
18
  @_loaders[collection_class.to_s][aggregation_name.to_s] = block
@@ -33,13 +23,43 @@ module ArtirixDataModels
33
23
  end
34
24
  end
35
25
 
36
- # static methods
37
- def self.set_loader(aggregation_name, collection_class, loader = nil, &block)
38
- instance.set_loader aggregation_name, collection_class, loader, &block
26
+ def default_loader
27
+ proc { |aggregation| Aggregation.from_json aggregation }
39
28
  end
40
29
 
41
- def self.build_from_json(aggregation_name, collection_class)
42
- instance.build_from_json aggregation_name, collection_class
30
+ def get_loader(aggregation_name, collection_class)
31
+ @_loaders[collection_class.to_s][aggregation_name.to_s] ||
32
+ @_loaders[DEFAULT_COLLECTION_CLASS_NAME][aggregation_name.to_s] ||
33
+ default_loader
43
34
  end
35
+
36
+ # AGGREGATION BUILDING
37
+
38
+ def build_from_json(aggregation, collection_class = nil)
39
+ get_loader(aggregation[:name], collection_class).call aggregation
40
+ end
41
+
42
+ def build_all_from_raw_data(raw, collection_class = nil)
43
+ normalised = normalise_aggregations_data(raw)
44
+ normalised.map { |definition| build_from_json definition, collection_class }
45
+ end
46
+
47
+ def aggregation_from_json(definition, value_class: Aggregation::Value, aggregation_class: Aggregation)
48
+ builder_params = {
49
+ aggregations_factory: self,
50
+ definition: definition,
51
+ aggregation_class: aggregation_class,
52
+ value_class: value_class,
53
+ }
54
+
55
+ AggregationBuilder.new(builder_params).build
56
+ end
57
+
58
+ private
59
+
60
+ def normalise_aggregations_data(raw_aggs)
61
+ RawAggregationDataNormaliser.new(self, raw_aggs).normalise
62
+ end
63
+
44
64
  end
45
65
  end
@@ -27,6 +27,10 @@ class ArtirixDataModels::DAORegistry
27
27
  set_loader(:model_fields) { ArtirixDataModels::ModelFieldsDAO.new gateway: get(:gateway) }
28
28
  end
29
29
 
30
+ def aggregations_factory
31
+ get :aggregations_factory
32
+ end
33
+
30
34
  def method_missing(method, *args, &block)
31
35
  if exist?(method)
32
36
  get(method)
@@ -106,7 +106,7 @@ module ArtirixDataModels
106
106
  end
107
107
 
108
108
  def aggregations
109
- @aggregations ||= response[:aggregations].to_a.map { |aggregation| aggregations_factory.build_from_json aggregation, model_class }
109
+ @aggregations ||= build_aggregations
110
110
  end
111
111
 
112
112
  def aggregation(name)
@@ -152,7 +152,15 @@ module ArtirixDataModels
152
152
  ].join(CACHE_KEY_SECTION_SEPARATOR)
153
153
  end
154
154
 
155
+ def raw_aggregations_data
156
+ response[:aggregations]
157
+ end
158
+
155
159
  private
160
+ def build_aggregations
161
+ aggregations_factory.build_all_from_raw_data(raw_aggregations_data)
162
+ end
163
+
156
164
  def load_results
157
165
  hits[:hits].map do |document|
158
166
  deserialize_document(document)
@@ -0,0 +1,68 @@
1
+ module ArtirixDataModels
2
+ class RawAggregationDataNormaliser
3
+
4
+ FIND_BUCKETS = ->(_k, v, _o) { v.respond_to?(:key?) && v.key?(:buckets) }
5
+
6
+ attr_reader :raw_aggs, :aggregations_factory
7
+
8
+ def initialize(aggregations_factory, raw_aggs)
9
+ @aggregations_factory = aggregations_factory
10
+ @raw_aggs = raw_aggs
11
+ end
12
+
13
+ def normalise
14
+ return [] unless raw_aggs.present?
15
+ return raw_aggs if Array === raw_aggs
16
+
17
+ normalise_hash
18
+ end
19
+
20
+ alias_method :call, :normalise
21
+
22
+ private
23
+
24
+ def normalise_hash
25
+ with_buckets_list = deep_locate raw_aggs, FIND_BUCKETS
26
+
27
+ with_buckets_list.reduce([]) do |list, with_buckets|
28
+ with_buckets.each do |name, value|
29
+ add_normalised_element_to_list(list, name, value)
30
+ end
31
+
32
+ list
33
+ end
34
+ end
35
+
36
+ def add_normalised_element_to_list(list, k, v)
37
+ return unless Hash === v && v.key?(:buckets)
38
+
39
+ buckets = v[:buckets].map do |raw_bucket|
40
+ normalise_bucket(raw_bucket)
41
+ end
42
+
43
+ list << { name: k, buckets: buckets }
44
+ end
45
+
46
+ def normalise_bucket(raw_bucket)
47
+ basic_bucket(raw_bucket).tap do |bucket|
48
+ nested_aggs = nested_aggs_from(raw_bucket)
49
+ bucket[:aggregations] = nested_aggs if nested_aggs.present?
50
+ end
51
+ end
52
+
53
+ def basic_bucket(raw_bucket)
54
+ name = raw_bucket[:key] || raw_bucket[:name]
55
+ count = raw_bucket[:doc_count] || raw_bucket[:count]
56
+ { name: name, count: count }
57
+ end
58
+
59
+ # aux
60
+ def deep_locate(object, callable)
61
+ Hashie::Extensions::DeepLocate.deep_locate(callable, object).deep_dup
62
+ end
63
+
64
+ def nested_aggs_from(raw_bucket)
65
+ RawAggregationDataNormaliser.new(aggregations_factory, raw_bucket).normalise
66
+ end
67
+ end
68
+ end
@@ -1,3 +1,4 @@
1
+ # :nocov:
1
2
  def fake_mode_for(model_name)
2
3
  before(:all) do
3
4
  SimpleConfig.for(:site) do
@@ -22,3 +23,4 @@ def fake_mode_for(model_name)
22
23
  end
23
24
  end
24
25
 
26
+ # :nocov:
@@ -1,3 +1,4 @@
1
+ # :nocov:
1
2
  def given_gateway_config(connection_url = nil)
2
3
  connection_url ||= 'http://example.com/other'
3
4
  before(:all) do
@@ -21,3 +22,5 @@ def mock_gateway_get_not_found_response(path:, body: nil, gateway: nil)
21
22
 
22
23
  allow(gateway).to receive(:perform_get).with(path, body).and_raise ArtirixDataModels::DataGateway::NotFound
23
24
  end
25
+
26
+ # :nocov:
@@ -1,3 +1,5 @@
1
+ # :nocov:
2
+
1
3
  # based on https://github.com/geoffharcourt/active_model_lint-rspec/blob/master/lib/active_model_lint-rspec/an_active_model.rb
2
4
  shared_examples_for 'a ReadOnly ActiveModel like ArtirixDataModels' do
3
5
  require 'active_model/lint'
@@ -18,3 +20,4 @@ shared_examples_for 'a ReadOnly ActiveModel like ArtirixDataModels' do
18
20
  end
19
21
  end
20
22
 
23
+ # :nocov:
@@ -1,4 +1,4 @@
1
-
1
+ # :nocov:
2
2
  shared_examples_for 'an ArtirixDataModel DAO' do
3
3
  # example of needed definitions
4
4
  #
@@ -95,3 +95,4 @@ shared_examples_for 'an ArtirixDataModel DAO' do
95
95
  Then { result == partial_mode_fields_list }
96
96
  end
97
97
  end
98
+ # :nocov:
@@ -1,4 +1,4 @@
1
-
1
+ # :nocov:
2
2
  shared_examples_for 'an ArtirixDataModel Model' do
3
3
 
4
4
  # Example of Config:
@@ -222,3 +222,4 @@ shared_examples_for 'an ArtirixDataModel Model' do
222
222
  end
223
223
  end
224
224
 
225
+ # :nocov:
@@ -1,3 +1,4 @@
1
+ # :nocov:
1
2
  shared_examples_for 'has attributes' do
2
3
  describe 'responds to the given attribute getters and setters' do
3
4
  When(:subject) { described_class.new }
@@ -29,4 +30,6 @@ shared_examples_for 'has attributes' do
29
30
  attributes.reject { |at| subject.respond_to? "#{at}?" }.empty?
30
31
  end
31
32
  end
32
- end
33
+ end
34
+
35
+ # :nocov:
@@ -1,3 +1,3 @@
1
1
  module ArtirixDataModels
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.2'
3
3
  end
@@ -1,4 +1,4 @@
1
- require "artirix_data_models/version"
1
+ require 'artirix_data_models/version'
2
2
 
3
3
  # dependencies
4
4
  require 'active_support/all'
@@ -7,6 +7,7 @@ require 'oj'
7
7
  require 'faraday'
8
8
  require 'keyword_init'
9
9
  require 'naught'
10
+ require 'hashie'
10
11
 
11
12
  # note DO NOT require kaminari or will_paginate, it'll be done when invoking `ArtirixDataModels::EsCollection.work_with_will_paginate`
12
13
 
@@ -14,6 +15,8 @@ require 'naught'
14
15
  # loading features
15
16
  require 'artirix_data_models/es_collection'
16
17
  require 'artirix_data_models/aggregations_factory'
18
+ require 'artirix_data_models/raw_aggregation_data_normaliser'
19
+ require 'artirix_data_models/aggregation_builder'
17
20
  require 'artirix_data_models/aggregation'
18
21
  require 'artirix_data_models/model'
19
22
  require 'artirix_data_models/gateways/data_gateway'
@@ -4,7 +4,7 @@ RSpec.describe ArtirixDataModels::EsCollection, type: :model do
4
4
 
5
5
  describe "with an ES response" do
6
6
 
7
- Given(:model_class){
7
+ Given(:model_class) {
8
8
  Class.new do
9
9
  attr_reader :data
10
10
 
@@ -13,9 +13,9 @@ RSpec.describe ArtirixDataModels::EsCollection, type: :model do
13
13
  end
14
14
  end
15
15
  }
16
- Given(:es_response){ ArtirixDataModels::EsCollection::EMPTY_RESPONSE }
16
+ Given(:es_response) { ArtirixDataModels::EsCollection::EMPTY_RESPONSE }
17
17
 
18
- When(:es_collection){ ArtirixDataModels::EsCollection.new(model_class, response: es_response) }
18
+ When(:es_collection) { ArtirixDataModels::EsCollection.new(model_class, response: es_response) }
19
19
 
20
20
  Then { es_collection.total == 0 }
21
21
  Then { es_collection.results == [] }
@@ -23,7 +23,7 @@ RSpec.describe ArtirixDataModels::EsCollection, type: :model do
23
23
 
24
24
  describe "coerced from an array" do
25
25
 
26
- Given(:model_class){
26
+ Given(:model_class) {
27
27
  Class.new do
28
28
  attr_reader :number
29
29
 
@@ -33,13 +33,142 @@ RSpec.describe ArtirixDataModels::EsCollection, type: :model do
33
33
  end
34
34
  }
35
35
 
36
- Given(:results_array){ [model_class.new(1), model_class.new(2), model_class.new(3)] }
36
+ Given(:results_array) { [model_class.new(1), model_class.new(2), model_class.new(3)] }
37
37
 
38
- When(:es_collection){ ArtirixDataModels::EsCollection.from_array(results_array) }
38
+ When(:es_collection) { ArtirixDataModels::EsCollection.from_array(results_array) }
39
39
 
40
40
  Then { es_collection.total == 3 }
41
41
  Then { es_collection.results == results_array }
42
42
  Then { es_collection.aggregations == [] }
43
43
  end
44
44
 
45
+ describe 'aggregations' do
46
+
47
+ Given(:model_class) do
48
+ Class.new do
49
+ attr_reader :data
50
+
51
+ def initialize(data)
52
+ @data = { given: data }
53
+ end
54
+ end
55
+ end
56
+
57
+ Given(:es_response) { Oj.load(File.read(fixture_file), symbol_keys: true) }
58
+
59
+ context 'with Data Layer conversion into array' do
60
+ Given(:fixture_file) { fixture_pathname('articles_search_dl.json') }
61
+
62
+ When(:es_collection) { ArtirixDataModels::EsCollection.new(model_class, response: es_response) }
63
+
64
+ Then { es_collection.total == 11477 }
65
+ Then { es_collection.results == [] }
66
+
67
+ Then { es_collection.aggregations.size == 2 }
68
+
69
+ Then { es_collection.aggregations.first.name == :primary_category_slug }
70
+ Then { es_collection.aggregations.first.buckets.size == 10 }
71
+ Then { es_collection.aggregations.first.buckets.first.name == 'yacht-market-intelligence' }
72
+ Then { es_collection.aggregations.first.buckets.first.count == 7794 }
73
+ Then { es_collection.aggregations.first.buckets.last.name == 'brokerage' }
74
+ Then { es_collection.aggregations.first.buckets.last.count == 7 }
75
+
76
+ Then { es_collection.aggregations.last.name == :location_slug }
77
+ Then { es_collection.aggregations.last.buckets.size == 10 }
78
+ Then { es_collection.aggregations.last.buckets.first.name == 'americas' }
79
+ Then { es_collection.aggregations.last.buckets.first.count == 3 }
80
+ Then { es_collection.aggregations.last.buckets.last.name == 'italy' }
81
+ Then { es_collection.aggregations.last.buckets.last.count == 1 }
82
+ end
83
+
84
+ context 'with Raw ES with single aggregation' do
85
+ Given(:fixture_file) { fixture_pathname('articles_search_raw_es.json') }
86
+
87
+ When(:es_collection) { ArtirixDataModels::EsCollection.new(model_class, response: es_response) }
88
+
89
+ Then { es_collection.total == 14974 }
90
+ Then { es_collection.results == [] }
91
+
92
+ Then { es_collection.aggregations.size == 1 }
93
+
94
+ Then { es_collection.aggregations.first.name == :disease_slug }
95
+ Then { es_collection.aggregations.first.buckets.size == 6 }
96
+ Then { es_collection.aggregations.first.buckets.first.name == 'sickle-cell-anemia' }
97
+ Then { es_collection.aggregations.first.buckets.first.count == 5754 }
98
+ Then { es_collection.aggregations.first.buckets.last.name == 'fabry-disease' }
99
+ Then { es_collection.aggregations.first.buckets.last.count == 742 }
100
+ end
101
+
102
+ context 'with Raw ES with single nested aggregation' do
103
+ Given(:fixture_file) { fixture_pathname('articles_search_nested_single_raw_es.json') }
104
+
105
+ When(:es_collection) { ArtirixDataModels::EsCollection.new(model_class, response: es_response) }
106
+
107
+ Then { es_collection.total == 11492 }
108
+ Then { es_collection.results == [] }
109
+
110
+ Then { es_collection.aggregations.size == 2 }
111
+
112
+ Then { es_collection.aggregations.first.name == :category_slug }
113
+ Then { es_collection.aggregations.first.buckets.size == 24 }
114
+ Then { es_collection.aggregations.first.buckets.first.name == 'brokerage-sales-news' }
115
+ Then { es_collection.aggregations.first.buckets.first.count == 7792 }
116
+ Then { es_collection.aggregations.first.buckets.last.name == 'why-charter-a-superyacht' }
117
+ Then { es_collection.aggregations.first.buckets.last.count == 1 }
118
+
119
+ Then { es_collection.aggregations.last.name == :location_slug }
120
+ Then { es_collection.aggregations.last.buckets.size == 19 }
121
+ Then { es_collection.aggregations.last.buckets.first.name == 'americas' }
122
+ Then { es_collection.aggregations.last.buckets.first.count == 4 }
123
+ Then { es_collection.aggregations.last.buckets.last.name == 'tahiti' }
124
+ Then { es_collection.aggregations.last.buckets.last.count == 1 }
125
+ end
126
+
127
+ context 'with Raw ES with nested aggregations' do
128
+ Given(:fixture_file) { fixture_pathname('articles_search_nested_raw_es.json') }
129
+
130
+ Given(:model_class) {
131
+ Class.new do
132
+ attr_reader :data
133
+
134
+ def initialize(data)
135
+ @data = { given: data }
136
+ end
137
+ end
138
+ }
139
+ Given(:es_response) { Oj.load(File.read(fixture_file), symbol_keys: true) }
140
+
141
+ When(:es_collection) { ArtirixDataModels::EsCollection.new(model_class, response: es_response) }
142
+
143
+ Then { es_collection.total == 4512 }
144
+ Then { es_collection.results == [] }
145
+
146
+ Then { es_collection.aggregations.size == 1 }
147
+
148
+ Then { es_collection.aggregations.first.name == :level1_taxonomy }
149
+ Then { es_collection.aggregations.first.buckets.size == 4 }
150
+
151
+ Then { es_collection.aggregations.first.buckets.first.name == 'Treatment' }
152
+ Then { es_collection.aggregations.first.buckets.first.count == 2404 }
153
+ Then { es_collection.aggregations.first.buckets.first.aggregations.size == 1 }
154
+ Then { es_collection.aggregations.first.buckets.first.aggregations.first.name == :level2_taxonomy }
155
+ Then { es_collection.aggregations.first.buckets.first.aggregations.first.buckets.size == 7 }
156
+ Then { es_collection.aggregations.first.buckets.first.aggregations.first.buckets.first.name == 'Drug Treatments' }
157
+ Then { es_collection.aggregations.first.buckets.first.aggregations.first.buckets.first.count == 977 }
158
+ Then { es_collection.aggregations.first.buckets.first.aggregations.first.buckets.last.name == 'Complementary and Alternative Therapies' }
159
+ Then { es_collection.aggregations.first.buckets.first.aggregations.first.buckets.last.count == 14 }
160
+
161
+
162
+ Then { es_collection.aggregations.first.buckets.last.name == 'Living' }
163
+ Then { es_collection.aggregations.first.buckets.last.count == 365 }
164
+ Then { es_collection.aggregations.first.buckets.last.aggregations.size == 1 }
165
+ Then { es_collection.aggregations.first.buckets.last.aggregations.first.name == :level2_taxonomy }
166
+ Then { es_collection.aggregations.first.buckets.last.aggregations.first.buckets.size == 8 }
167
+ Then { es_collection.aggregations.first.buckets.last.aggregations.first.buckets.first.name == 'Emotional Impact' }
168
+ Then { es_collection.aggregations.first.buckets.last.aggregations.first.buckets.first.count == 104 }
169
+ Then { es_collection.aggregations.first.buckets.last.aggregations.first.buckets.last.name == 'Cognitive Impact' }
170
+ Then { es_collection.aggregations.first.buckets.last.aggregations.first.buckets.last.count == 3 }
171
+ end
172
+ end
173
+
45
174
  end
@@ -0,0 +1,99 @@
1
+ {
2
+ "hits": {
3
+ "total": 11477,
4
+ "max_score": 0.0,
5
+ "hits": []
6
+ },
7
+ "aggregations": [
8
+ {
9
+ "name": "primary_category_slug",
10
+ "buckets": [
11
+ {
12
+ "name": "yacht-market-intelligence",
13
+ "count": 7794
14
+ },
15
+ {
16
+ "name": "brokerage-sales-news",
17
+ "count": 7792
18
+ },
19
+ {
20
+ "name": "yachts",
21
+ "count": 2238
22
+ },
23
+ {
24
+ "name": "news",
25
+ "count": 2236
26
+ },
27
+ {
28
+ "name": "charter",
29
+ "count": 1322
30
+ },
31
+ {
32
+ "name": "luxury-yacht-charter-news",
33
+ "count": 1292
34
+ },
35
+ {
36
+ "name": "luxury-yacht-events",
37
+ "count": 110
38
+ },
39
+ {
40
+ "name": "luxury-yacht-events-news",
41
+ "count": 110
42
+ },
43
+ {
44
+ "name": "luxury-yacht-charter-advice",
45
+ "count": 15
46
+ },
47
+ {
48
+ "name": "brokerage",
49
+ "count": 7
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "name": "location_slug",
55
+ "buckets": [
56
+ {
57
+ "name": "americas",
58
+ "count": 3
59
+ },
60
+ {
61
+ "name": "central-america",
62
+ "count": 3
63
+ },
64
+ {
65
+ "name": "europe",
66
+ "count": 3
67
+ },
68
+ {
69
+ "name": "caribbean",
70
+ "count": 2
71
+ },
72
+ {
73
+ "name": "costa-rica",
74
+ "count": 2
75
+ },
76
+ {
77
+ "name": "mediterranean",
78
+ "count": 2
79
+ },
80
+ {
81
+ "name": "anegada",
82
+ "count": 1
83
+ },
84
+ {
85
+ "name": "france",
86
+ "count": 1
87
+ },
88
+ {
89
+ "name": "french-polynesia",
90
+ "count": 1
91
+ },
92
+ {
93
+ "name": "italy",
94
+ "count": 1
95
+ }
96
+ ]
97
+ }
98
+ ]
99
+ }
@@ -0,0 +1,149 @@
1
+ {
2
+ "took": 16,
3
+ "timed_out": false,
4
+ "_shards": {
5
+ "total": 5,
6
+ "successful": 5,
7
+ "failed": 0
8
+ },
9
+ "hits": {
10
+ "total": 4512,
11
+ "max_score": 0,
12
+ "hits": []
13
+ },
14
+ "aggregations": {
15
+ "taxonomy": {
16
+ "doc_count": 4674,
17
+ "level1_taxonomy": {
18
+ "doc_count_error_upper_bound": 0,
19
+ "sum_other_doc_count": 0,
20
+ "buckets": [
21
+ {
22
+ "key": "Treatment",
23
+ "doc_count": 2404,
24
+ "level2_taxonomy": {
25
+ "doc_count_error_upper_bound": 0,
26
+ "sum_other_doc_count": 0,
27
+ "buckets": [
28
+ {
29
+ "key": "Drug Treatments",
30
+ "doc_count": 977
31
+ },
32
+ {
33
+ "key": "Drug Development",
34
+ "doc_count": 731
35
+ },
36
+ {
37
+ "key": "Other Treatments",
38
+ "doc_count": 358
39
+ },
40
+ {
41
+ "key": "Surgical Treatments",
42
+ "doc_count": 264
43
+ },
44
+ {
45
+ "key": "Assistive Devices",
46
+ "doc_count": 43
47
+ },
48
+ {
49
+ "key": "Palliative Care",
50
+ "doc_count": 17
51
+ },
52
+ {
53
+ "key": "Complementary and Alternative Therapies",
54
+ "doc_count": 14
55
+ }
56
+ ]
57
+ }
58
+ },
59
+ {
60
+ "key": "Understand",
61
+ "doc_count": 1356,
62
+ "level2_taxonomy": {
63
+ "doc_count_error_upper_bound": 0,
64
+ "sum_other_doc_count": 0,
65
+ "buckets": [
66
+ {
67
+ "key": "Symptoms",
68
+ "doc_count": 1066
69
+ },
70
+ {
71
+ "key": "Outlook",
72
+ "doc_count": 143
73
+ },
74
+ {
75
+ "key": "Causes",
76
+ "doc_count": 123
77
+ },
78
+ {
79
+ "key": "Ethics",
80
+ "doc_count": 24
81
+ }
82
+ ]
83
+ }
84
+ },
85
+ {
86
+ "key": "Diagnosis",
87
+ "doc_count": 549,
88
+ "level2_taxonomy": {
89
+ "doc_count_error_upper_bound": 0,
90
+ "sum_other_doc_count": 0,
91
+ "buckets": [
92
+ {
93
+ "key": "Diagnostic Techniques",
94
+ "doc_count": 525
95
+ },
96
+ {
97
+ "key": "Diagnostic Challenges",
98
+ "doc_count": 24
99
+ }
100
+ ]
101
+ }
102
+ },
103
+ {
104
+ "key": "Living",
105
+ "doc_count": 365,
106
+ "level2_taxonomy": {
107
+ "doc_count_error_upper_bound": 0,
108
+ "sum_other_doc_count": 0,
109
+ "buckets": [
110
+ {
111
+ "key": "Emotional Impact",
112
+ "doc_count": 104
113
+ },
114
+ {
115
+ "key": "Genetic Counseling",
116
+ "doc_count": 63
117
+ },
118
+ {
119
+ "key": "Diet",
120
+ "doc_count": 59
121
+ },
122
+ {
123
+ "key": "Fertility/Sex",
124
+ "doc_count": 56
125
+ },
126
+ {
127
+ "key": "Exercise",
128
+ "doc_count": 48
129
+ },
130
+ {
131
+ "key": "Physical Impact",
132
+ "doc_count": 26
133
+ },
134
+ {
135
+ "key": "Sleep",
136
+ "doc_count": 6
137
+ },
138
+ {
139
+ "key": "Cognitive Impact",
140
+ "doc_count": 3
141
+ }
142
+ ]
143
+ }
144
+ }
145
+ ]
146
+ }
147
+ }
148
+ }
149
+ }
@@ -0,0 +1,212 @@
1
+ {
2
+ "took": 2,
3
+ "timed_out": false,
4
+ "_shards": {
5
+ "total": 5,
6
+ "successful": 5,
7
+ "failed": 0
8
+ },
9
+ "hits": {
10
+ "total": 11492,
11
+ "max_score": 0,
12
+ "hits": []
13
+ },
14
+ "aggregations": {
15
+ "global_primary_category_slug": {
16
+ "doc_count": 11492,
17
+ "global_filtered_primary_category_slug": {
18
+ "doc_count": 11492,
19
+ "category_slug": {
20
+ "doc_count_error_upper_bound": 0,
21
+ "sum_other_doc_count": 0,
22
+ "buckets": [
23
+ {
24
+ "key": "brokerage-sales-news",
25
+ "doc_count": 7792
26
+ },
27
+ {
28
+ "key": "news",
29
+ "doc_count": 2236
30
+ },
31
+ {
32
+ "key": "luxury-yacht-charter-news",
33
+ "doc_count": 1292
34
+ },
35
+ {
36
+ "key": "luxury-yacht-events-news",
37
+ "doc_count": 110
38
+ },
39
+ {
40
+ "key": "charter",
41
+ "doc_count": 14
42
+ },
43
+ {
44
+ "key": "brokerage",
45
+ "doc_count": 7
46
+ },
47
+ {
48
+ "key": "luxury-yacht-charter-advice",
49
+ "doc_count": 5
50
+ },
51
+ {
52
+ "key": "cruising-advice",
53
+ "doc_count": 4
54
+ },
55
+ {
56
+ "key": "luxury-yacht-life",
57
+ "doc_count": 4
58
+ },
59
+ {
60
+ "key": "safety-advice",
61
+ "doc_count": 4
62
+ },
63
+ {
64
+ "key": "design01-news",
65
+ "doc_count": 3
66
+ },
67
+ {
68
+ "key": "plan-and-book-advice",
69
+ "doc_count": 3
70
+ },
71
+ {
72
+ "key": "charter-news",
73
+ "doc_count": 2
74
+ },
75
+ {
76
+ "key": "destinations",
77
+ "doc_count": 2
78
+ },
79
+ {
80
+ "key": "superyacht-sales-data",
81
+ "doc_count": 2
82
+ },
83
+ {
84
+ "key": "testcat123456789-edit",
85
+ "doc_count": 2
86
+ },
87
+ {
88
+ "key": "yachts",
89
+ "doc_count": 2
90
+ },
91
+ {
92
+ "key": "yachts-luxury-yacht-broker-directory",
93
+ "doc_count": 2
94
+ },
95
+ {
96
+ "key": "annual-reports",
97
+ "doc_count": 1
98
+ },
99
+ {
100
+ "key": "boat-shows",
101
+ "doc_count": 1
102
+ },
103
+ {
104
+ "key": "company-news",
105
+ "doc_count": 1
106
+ },
107
+ {
108
+ "key": "new-to-the-fleet",
109
+ "doc_count": 1
110
+ },
111
+ {
112
+ "key": "superyacht1234",
113
+ "doc_count": 1
114
+ },
115
+ {
116
+ "key": "why-charter-a-superyacht",
117
+ "doc_count": 1
118
+ }
119
+ ]
120
+ }
121
+ }
122
+ },
123
+ "global_location_slug": {
124
+ "doc_count": 11492,
125
+ "global_filtered_location_slug": {
126
+ "doc_count": 11492,
127
+ "location_slug": {
128
+ "doc_count_error_upper_bound": 0,
129
+ "sum_other_doc_count": 0,
130
+ "buckets": [
131
+ {
132
+ "key": "americas",
133
+ "doc_count": 4
134
+ },
135
+ {
136
+ "key": "central-america",
137
+ "doc_count": 4
138
+ },
139
+ {
140
+ "key": "europe",
141
+ "doc_count": 3
142
+ },
143
+ {
144
+ "key": "caribbean",
145
+ "doc_count": 2
146
+ },
147
+ {
148
+ "key": "costa-rica",
149
+ "doc_count": 2
150
+ },
151
+ {
152
+ "key": "mediterranean",
153
+ "doc_count": 2
154
+ },
155
+ {
156
+ "key": "anegada",
157
+ "doc_count": 1
158
+ },
159
+ {
160
+ "key": "france",
161
+ "doc_count": 1
162
+ },
163
+ {
164
+ "key": "french-polynesia",
165
+ "doc_count": 1
166
+ },
167
+ {
168
+ "key": "italy",
169
+ "doc_count": 1
170
+ },
171
+ {
172
+ "key": "leeward-islands",
173
+ "doc_count": 1
174
+ },
175
+ {
176
+ "key": "north-america",
177
+ "doc_count": 1
178
+ },
179
+ {
180
+ "key": "pacific",
181
+ "doc_count": 1
182
+ },
183
+ {
184
+ "key": "panama",
185
+ "doc_count": 1
186
+ },
187
+ {
188
+ "key": "rest-of-the-world",
189
+ "doc_count": 1
190
+ },
191
+ {
192
+ "key": "san-blas-islands",
193
+ "doc_count": 1
194
+ },
195
+ {
196
+ "key": "spain",
197
+ "doc_count": 1
198
+ },
199
+ {
200
+ "key": "st-barths",
201
+ "doc_count": 1
202
+ },
203
+ {
204
+ "key": "tahiti",
205
+ "doc_count": 1
206
+ }
207
+ ]
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "took": 5,
3
+ "timed_out": false,
4
+ "_shards": {
5
+ "total": 5,
6
+ "successful": 5,
7
+ "failed": 0
8
+ },
9
+ "hits": {
10
+ "total": 14974,
11
+ "max_score": 0,
12
+ "hits": []
13
+ },
14
+ "aggregations": {
15
+ "disease_slug": {
16
+ "doc_count_error_upper_bound": 0,
17
+ "sum_other_doc_count": 0,
18
+ "buckets": [
19
+ {
20
+ "key": "sickle-cell-anemia",
21
+ "doc_count": 5754
22
+ },
23
+ {
24
+ "key": "cystic-fibrosis",
25
+ "doc_count": 4512
26
+ },
27
+ {
28
+ "key": "behcet-syndrome",
29
+ "doc_count": 1613
30
+ },
31
+ {
32
+ "key": "myasthenia-gravis",
33
+ "doc_count": 1402
34
+ },
35
+ {
36
+ "key": "cushing-syndrome",
37
+ "doc_count": 951
38
+ },
39
+ {
40
+ "key": "fabry-disease",
41
+ "doc_count": 742
42
+ }
43
+ ]
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,5 @@
1
+ require 'pathname'
2
+
3
+ def fixture_pathname(filename)
4
+ Pathname(__FILE__).join("../../fixtures/#{filename}")
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: artirix_data_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eduardo Turiño
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-07 00:00:00.000000000 Z
11
+ date: 2015-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: hashie
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: kaminari
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -195,7 +209,9 @@ dependencies:
195
209
  description: used in Boat International UI and Admin apps
196
210
  email:
197
211
  - eturino@artirix.com
198
- executables: []
212
+ executables:
213
+ - console
214
+ - setup
199
215
  extensions: []
200
216
  extra_rdoc_files: []
201
217
  files:
@@ -207,8 +223,11 @@ files:
207
223
  - README.md
208
224
  - Rakefile
209
225
  - artirix_data_models.gemspec
226
+ - bin/console
227
+ - bin/setup
210
228
  - lib/artirix_data_models.rb
211
229
  - lib/artirix_data_models/aggregation.rb
230
+ - lib/artirix_data_models/aggregation_builder.rb
212
231
  - lib/artirix_data_models/aggregations_factory.rb
213
232
  - lib/artirix_data_models/cache_service.rb
214
233
  - lib/artirix_data_models/cached_action_adaptor.rb
@@ -224,6 +243,7 @@ files:
224
243
  - lib/artirix_data_models/gateway_response_adaptors/model_adaptor.rb
225
244
  - lib/artirix_data_models/gateways/data_gateway.rb
226
245
  - lib/artirix_data_models/model.rb
246
+ - lib/artirix_data_models/raw_aggregation_data_normaliser.rb
227
247
  - lib/artirix_data_models/spec_support.rb
228
248
  - lib/artirix_data_models/spec_support/fake_mode.rb
229
249
  - lib/artirix_data_models/spec_support/gateway_mock.rb
@@ -238,11 +258,16 @@ files:
238
258
  - spec/artirix_data_models/gateways/data_gateway_spec.rb
239
259
  - spec/artirix_data_models/gateways/gateway_response_adaptors/model_adaptor_spec.rb
240
260
  - spec/artirix_data_models/model_fields_dao_spec.rb
261
+ - spec/fixtures/articles_search_dl.json
262
+ - spec/fixtures/articles_search_nested_raw_es.json
263
+ - spec/fixtures/articles_search_nested_single_raw_es.json
264
+ - spec/fixtures/articles_search_raw_es.json
241
265
  - spec/spec_helper.rb
242
266
  - spec/support/.keep
243
267
  - spec/support/a_finder_enabled_ui_model_dao.rb
244
268
  - spec/support/a_search_enabled_ui_model_dao.rb
245
269
  - spec/support/artirix_data_models.rb
270
+ - spec/support/fixtures.rb
246
271
  homepage: ''
247
272
  licenses:
248
273
  - MIT
@@ -273,9 +298,14 @@ test_files:
273
298
  - spec/artirix_data_models/gateways/data_gateway_spec.rb
274
299
  - spec/artirix_data_models/gateways/gateway_response_adaptors/model_adaptor_spec.rb
275
300
  - spec/artirix_data_models/model_fields_dao_spec.rb
301
+ - spec/fixtures/articles_search_dl.json
302
+ - spec/fixtures/articles_search_nested_raw_es.json
303
+ - spec/fixtures/articles_search_nested_single_raw_es.json
304
+ - spec/fixtures/articles_search_raw_es.json
276
305
  - spec/spec_helper.rb
277
306
  - spec/support/.keep
278
307
  - spec/support/a_finder_enabled_ui_model_dao.rb
279
308
  - spec/support/a_search_enabled_ui_model_dao.rb
280
309
  - spec/support/artirix_data_models.rb
310
+ - spec/support/fixtures.rb
281
311
  has_rdoc: