quandl-elasticsearch 2.1.0.rc5

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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rubocop.yml +34 -0
  4. data/COMMANDS.md +29 -0
  5. data/Gemfile +10 -0
  6. data/Gemfile.lock +155 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +50 -0
  9. data/Rakefile +1 -0
  10. data/config/elasticsearch.yml +32 -0
  11. data/elasticsearch/elasticsearch.yml +386 -0
  12. data/elasticsearch/logging.yml +56 -0
  13. data/elasticsearch/stopwords/english.txt +38 -0
  14. data/elasticsearch/synonyms/synonyms_english.txt +318 -0
  15. data/fixtures/vcr_cassettes/search_spec_database_1.yml +38 -0
  16. data/fixtures/vcr_cassettes/search_spec_database_2.yml +38 -0
  17. data/fixtures/vcr_cassettes/search_spec_dataset_1.yml +48 -0
  18. data/fixtures/vcr_cassettes/search_spec_dataset_2.yml +41 -0
  19. data/fixtures/vcr_cassettes/setup.yml +139 -0
  20. data/lib/quandl/elasticsearch.rb +61 -0
  21. data/lib/quandl/elasticsearch/base.rb +20 -0
  22. data/lib/quandl/elasticsearch/database.rb +22 -0
  23. data/lib/quandl/elasticsearch/dataset.rb +51 -0
  24. data/lib/quandl/elasticsearch/indice.rb +96 -0
  25. data/lib/quandl/elasticsearch/query.rb +282 -0
  26. data/lib/quandl/elasticsearch/search.rb +150 -0
  27. data/lib/quandl/elasticsearch/tag.rb +21 -0
  28. data/lib/quandl/elasticsearch/template.rb +189 -0
  29. data/lib/quandl/elasticsearch/utility.rb +6 -0
  30. data/lib/quandl/elasticsearch/version.rb +6 -0
  31. data/quandl +77 -0
  32. data/quandl-elasticsearch.gemspec +34 -0
  33. data/solano.yml +20 -0
  34. data/spec/lib/quandl/elasticsearch/database_spec.rb +98 -0
  35. data/spec/lib/quandl/elasticsearch/dataset_spec.rb +124 -0
  36. data/spec/lib/quandl/elasticsearch/indice_spec.rb +10 -0
  37. data/spec/lib/quandl/elasticsearch/query_spec.rb +239 -0
  38. data/spec/lib/quandl/elasticsearch/search_spec.rb +83 -0
  39. data/spec/lib/quandl/elasticsearch/template_spec.rb +182 -0
  40. data/spec/lib/quandl/elasticsearch/utility_spec.rb +10 -0
  41. data/spec/lib/quandl/elasticsearch_spec.rb +99 -0
  42. data/spec/spec_helper.rb +27 -0
  43. data/templates/database_mapping.json +11 -0
  44. data/templates/dataset_mapping.json +9 -0
  45. data/templates/quandl_delimiter.json +0 -0
  46. data/templates/search_term_mapping.json +13 -0
  47. data/tests/Database-Ratings.csv +405 -0
  48. data/tests/Database-Tags.csv +341 -0
  49. data/tests/compare.csv +1431 -0
  50. data/tests/compare.rb +33 -0
  51. data/tests/console.rb +4 -0
  52. data/tests/generated_db_tags.csv +341 -0
  53. data/tests/search.rb +14 -0
  54. data/tests/search_db_mapping.txt +402 -0
  55. data/tests/status.rb +2 -0
  56. data/tests/test_search.csv +87 -0
  57. data/tests/test_search.rb +113 -0
  58. data/tests/testing-list.txt +183 -0
  59. data/tests/top500searches.csv +477 -0
  60. metadata +300 -0
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'quandl/elasticsearch/indice'
3
+
4
+ describe Quandl::Elasticsearch::Indice do
5
+ describe '.initialize' do
6
+ it 'can initialize' do
7
+ _indice = Quandl::Elasticsearch::Indice.new
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,239 @@
1
+ require 'spec_helper'
2
+
3
+ describe Quandl::Elasticsearch::Query do
4
+ let(:dataset_search_filter) do
5
+ [
6
+ { term: { db_hidden: false } },
7
+ { term: { db_exclusive: false } }
8
+ ]
9
+ end
10
+ describe '.initialize' do
11
+ it 'can initialize' do
12
+ query = Quandl::Elasticsearch::Query.new
13
+ expect(query.class).to be(Quandl::Elasticsearch::Query)
14
+ end
15
+ end
16
+
17
+ describe '.query_body' do
18
+ let(:expected) do
19
+ {
20
+ explain: false,
21
+ fields: %w(_parent code),
22
+ query: {
23
+ bool: {
24
+ must: {
25
+ bool: {
26
+ should: [
27
+ {
28
+ multi_match: {
29
+ fields: ['code', 'name^1.1'],
30
+ query: 'stocks', type: 'phrase', operator: 'and',
31
+ zero_terms_query: 'all', slop: 10
32
+ }
33
+ },
34
+ {
35
+ has_child: {
36
+ type: 'dataset', score_mode: 'max',
37
+ query: {
38
+ bool: {
39
+ should: [
40
+ { multi_match: { fields: ['name^1.1'], query: 'stocks', type: 'phrase', slop: 10 } },
41
+ { multi_match: { fields: ['code', 'name^1.1'], query: 'stocks', type: 'best_fields' } },
42
+ { prefix: { code: 'stocks' } }
43
+ ]
44
+ }
45
+ }
46
+ }
47
+ },
48
+ { prefix: { code: 'stocks' } }
49
+ ]
50
+ }
51
+ },
52
+ filter: { bool: { must: [] } }
53
+ }
54
+ },
55
+ from: 1,
56
+ size: 10
57
+ }
58
+ end
59
+ it 'returns the correct JSON string' do
60
+ expect(Quandl::Elasticsearch::Query.query_body('stocks', 1, 10)).to eq(expected)
61
+ end
62
+ end
63
+
64
+ describe '.multi_dataset_query_body' do
65
+ let(:expected_query) do
66
+ {
67
+ timeout: '10s',
68
+ fields: %w(_parent _id code),
69
+ query: {
70
+ bool: {
71
+ must: {
72
+ bool: {
73
+ should: [{ multi_match: { fields: ['code', 'name^1.1'], query: 'stocks', type: 'best_fields' } },
74
+ { prefix: { code: 'stocks' } }]
75
+ }
76
+ },
77
+ filter: {
78
+ bool: {
79
+ must: [{ term: { _parent: 1 } }, { term: { is_private: false } }]
80
+ }
81
+ }
82
+ }
83
+ },
84
+ sort: ['_score', { to_date: { order: 'desc' } }],
85
+ highlight: { fields: { name: {} } },
86
+ size: 10
87
+ }
88
+ end
89
+
90
+ it 'returns the correct JSON string' do
91
+ expect(Quandl::Elasticsearch::Query.multi_dataset_query_body(1, 'stocks', 10)).to eq(index: 'quandl_index', type: 'dataset', search: expected_query)
92
+ end
93
+ end
94
+
95
+ describe '.dataset_query_body' do
96
+ context 'searching using a query' do
97
+ let(:options) { { query: 'stocks' } }
98
+ let(:expected_query) do
99
+ {
100
+ fields: %w(_parent code),
101
+ query: {
102
+ bool: {
103
+ must: {
104
+ bool: {
105
+ should: [
106
+ { multi_match: { fields: ['name^1.1'], query: 'stocks', type: 'phrase', slop: 10 } },
107
+ { multi_match: { fields: ['code', 'name^1.1'], query: 'stocks', type: 'best_fields' } },
108
+ { prefix: { code: 'stocks' } }
109
+ ]
110
+ }
111
+ },
112
+ filter: {
113
+ bool: {
114
+ must: [{ term: { is_private: false } }] + dataset_search_filter
115
+ }
116
+ }
117
+ }
118
+ },
119
+ highlight: { fields: { name: {} } }, from: 1, size: 10
120
+ }
121
+ end
122
+
123
+ it 'returns the correct JSON string' do
124
+ expect(Quandl::Elasticsearch::Query.dataset_query_body(1, 10, options)).to eq(expected_query)
125
+ end
126
+ end
127
+
128
+ context 'searching all datasets' do
129
+ let(:options) { {} }
130
+ let(:expected_query) do
131
+ {
132
+ fields: %w(_parent code),
133
+ query: {
134
+ bool: {
135
+ must: { match_all: {} },
136
+ filter: {
137
+ bool: {
138
+ must: [{ term: { is_private: false } }] + dataset_search_filter
139
+ }
140
+ }
141
+ }
142
+ },
143
+ sort: [{ to_date: { order: 'desc' } }],
144
+ from: 1,
145
+ size: 10
146
+ }
147
+ end
148
+
149
+ it 'returns the correct JSON string' do
150
+ expect(Quandl::Elasticsearch::Query.dataset_query_body(1, 10, options)).to eq(expected_query)
151
+ end
152
+ end
153
+
154
+ context 'searching within a database' do
155
+ let(:options) { { query: 'stocks', database_id: 1 } }
156
+ let(:expected_query) do
157
+ {
158
+ fields: %w(_parent code),
159
+ query: {
160
+ bool: {
161
+ must: {
162
+ bool: {
163
+ should: [
164
+ { multi_match: { fields: ['name^1.1'], query: 'stocks', type: 'phrase', slop: 10 } },
165
+ { multi_match: { fields: ['code', 'name^1.1'], query: 'stocks', type: 'best_fields' } },
166
+ { prefix: { code: 'stocks' } }
167
+ ]
168
+ }
169
+ },
170
+ filter: {
171
+ bool: { must: [{ term: { is_private: false } }, { term: { _parent: 1 } }] }
172
+ }
173
+ }
174
+ },
175
+ highlight: { fields: { name: {} } },
176
+ from: 1,
177
+ size: 10
178
+ }
179
+ end
180
+
181
+ it 'returns the correct JSON string' do
182
+ expect(Quandl::Elasticsearch::Query.dataset_query_body(1, 10, options)).to eq(expected_query)
183
+ end
184
+ end
185
+ end
186
+
187
+ context 'filter option' do
188
+ let(:filter) { 'sample' }
189
+ let(:options) { { query: 'stocks', database_id: 1, filter: filter } }
190
+
191
+ context 'filter option is set to sample' do
192
+ it 'filters on sample=true datasets' do
193
+ query_body = Quandl::Elasticsearch::Query.dataset_query_body(1, 10, options)
194
+ expect(query_body[:query][:bool][:filter][:bool][:must]).to include(term: { sample: true })
195
+ end
196
+ end
197
+
198
+ context 'filter option is not set to sample' do
199
+ let(:filter) { 'all' }
200
+
201
+ it 'does not filter on sample=true datasets' do
202
+ query_body = Quandl::Elasticsearch::Query.dataset_query_body(1, 10, options)
203
+ expect(query_body[:query][:bool][:filter][:bool][:must]).to contain_exactly({ term: { is_private: false } }, term: { _parent: 1 })
204
+ end
205
+ end
206
+ end
207
+
208
+ describe '.add_timeout!' do
209
+ let(:body) { {} }
210
+
211
+ context 'timeout configured' do
212
+ context 'search key body' do
213
+ it 'adds timeout to body' do
214
+ Quandl::Elasticsearch::Query.add_timeout!(body)
215
+ expect(body).to eq(timeout: '10s')
216
+ end
217
+ end
218
+
219
+ context 'root body' do
220
+ let(:body) { { search: {} } }
221
+
222
+ it 'adds timeout to search body' do
223
+ Quandl::Elasticsearch::Query.add_timeout!(body)
224
+ expect(body).to eq(search: { timeout: '10s' })
225
+ end
226
+ end
227
+ end
228
+
229
+ context 'timeout is not configured' do
230
+ before(:each) do
231
+ Quandl::Elasticsearch.configuration.api_timeout = nil
232
+ end
233
+ it 'does not add timeout to body' do
234
+ Quandl::Elasticsearch::Query.add_timeout!(body)
235
+ expect(body).to eq({})
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+ require 'vcr'
3
+
4
+ describe Quandl::Elasticsearch::Search do
5
+ describe '.initialize' do
6
+ it 'can initialize' do
7
+ search = Quandl::Elasticsearch::Search.new
8
+ expect(search.class).to be(Quandl::Elasticsearch::Search)
9
+ end
10
+ end
11
+
12
+ describe '.page_size' do
13
+ it 'can return default page size' do
14
+ expect(subject.page_size).to be(10)
15
+ end
16
+
17
+ it 'can change a page size via init and return it' do
18
+ search = Quandl::Elasticsearch::Search.new(5)
19
+ expect(search.page_size).to be(5)
20
+ end
21
+ end
22
+
23
+ describe '.max_datasets' do
24
+ it 'can return max_datasets' do
25
+ expect(subject.max_datasets).to be(3)
26
+ end
27
+ end
28
+
29
+ describe '.database' do
30
+ it 'can process raw data from ES into appropriate format' do
31
+ VCR.use_cassette('search_spec_database_1') do
32
+ response = subject.database('stocks', '', 'free', 0, false)
33
+ expect(response).to eq([{ '_id' => '33', 'code' => 'NSE' }, { '_id' => '460', 'code' => 'LUXSE' }, { '_id' => '442', 'code' => 'ZAGREBSE' }, { '_id' => '462', 'code' => 'BUCHARESTSE' }, { '_id' => '5628', 'code' => 'EURONEXT' }, { '_id' => '450', 'code' => 'PHILSE' }, { '_id' => '663', 'code' => 'SI' }, { '_id' => '1383', 'code' => 'SIX' }, { '_id' => '464', 'code' => 'NIKKEI' }, { '_id' => '8401', 'code' => 'CRYPTOCHART' }, { 'total' => 130, 'took' => 752 }])
34
+ end
35
+ VCR.use_cassette('search_spec_database_2') do
36
+ response = subject.database('stocks', '', 'premium', 0, false)
37
+ expect(response).to eq([{ '_id' => '12910', 'code' => 'EOD' }, { '_id' => '13091', 'code' => 'ZFA' }, { '_id' => '13122', 'code' => 'ZFB' }, { '_id' => '13065', 'code' => 'ZSEE' }, { '_id' => '12266', 'code' => 'ZEE' }, { '_id' => '13000', 'code' => 'ZET' }, { '_id' => '12985', 'code' => 'ZAR' }, { '_id' => '12908', 'code' => 'SF1' }, { '_id' => '12973', 'code' => 'ZDIV' }, { '_id' => '12991', 'code' => 'ZSS' }, { 'total' => 18, 'took' => 769 }])
38
+ end
39
+ end
40
+
41
+ it 'adds timeout to query body' do
42
+ VCR.use_cassette('search_spec_database_1') do
43
+ expect(Quandl::Elasticsearch::Query).to receive(:add_timeout!).and_call_original
44
+ subject.database('stocks', '', 'premium', 0, false)
45
+ end
46
+ end
47
+
48
+ context 'timeout' do
49
+ it 'raises timeout error' do
50
+ expect_any_instance_of(::Elasticsearch::Transport::Client).to receive(:search).and_return('timed_out' => true)
51
+ expect { subject.database('stocks', '', 'premium', 0, false) }.to raise_error(Quandl::Elasticsearch::Search::TimeoutError)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '.dataset' do
57
+ it 'can process raw data returned from ES into appropriate format' do
58
+ VCR.use_cassette('search_spec_dataset_1') do
59
+ response = subject.dataset('stocks', 'daily', 0, 10, database_id: 104_71)
60
+ expect(response).to eq([{ '_id' => '13147174', 'highlight' => { 'name' => ['OMX Iceland All-<em>Share</em> GI (OMXIGI)'] } }, { '_id' => '13147176', 'highlight' => { 'name' => ['OMX Iceland All-<em>Share</em> PI (OMXIPI)'] } }, { '_id' => '13147219', 'highlight' => { 'name' => ['OMX Stockholm All-<em>Share</em> Cap_GI (OMXSCAPGI)'] } }, { '_id' => '13147220', 'highlight' => { 'name' => ['OMX Stockholm All-<em>Share</em> Cap_PI (OMXSCAPPI)'] } }, { '_id' => '13147404', 'highlight' => { 'name' => ['OMXI-FO All <em>Share</em> PI (OMXIFOPI)'] } }, { '_id' => '13147405', 'highlight' => { 'name' => ['OMXI-FO All <em>Share</em> GI (OMXIFOGI)'] } }, { '_id' => '13127717', 'highlight' => { 'name' => ['NASDAQ Select Canadian Preferred <em>Share</em> Index TR (NQCAPFDT)'] } }, { '_id' => '13127718', 'highlight' => { 'name' => ['NASDAQ Select Canadian Preferred <em>Share</em> Index (NQCAPFD)'] } }, { '_id' => '13961773', 'highlight' => { 'name' => ['NASDAQ Bullet<em>Shares</em> Corporate Debt 0-3 Ladder (BSCP03)'] } }, { '_id' => '13961774', 'highlight' => { 'name' => ['NASDAQ Bullet<em>Shares</em> High Yield Debt 0-3 Ladder TR (BSHY03T)'] } }, { 'total' => 30 }])
61
+ end
62
+
63
+ VCR.use_cassette('search_spec_dataset_2') do
64
+ response = subject.dataset('mexico', nil, 0, 3, database_id: 104_71)
65
+ expect(response).to eq([{ '_id' => '13124809', 'highlight' => { 'name' => ['NASDAQ <em>Mexico</em> Basic Matls AUD Index (NQMX1000AUD)'] } }, { '_id' => '13124810', 'highlight' => { 'name' => ['NASDAQ <em>Mexico</em> AUD Index (NQMXAUD)'] } }, { '_id' => '13124813', 'highlight' => { 'name' => ['NASDAQ <em>Mexico</em> AUD NTR Index (NQMXAUDN)'] } }, { 'total' => 315 }])
66
+ end
67
+ end
68
+
69
+ it 'adds timout to query body' do
70
+ VCR.use_cassette('search_spec_dataset_1') do
71
+ expect(Quandl::Elasticsearch::Query).to receive(:add_timeout!).and_call_original
72
+ subject.dataset('stocks', 'daily', 0, 10, database_id: 104_71)
73
+ end
74
+ end
75
+
76
+ context 'timeout' do
77
+ it 'raises timeout error' do
78
+ expect_any_instance_of(::Elasticsearch::Transport::Client).to receive(:search).and_return('timed_out' => true)
79
+ expect { subject.dataset('stocks', 'daily', 0, 10, database_id: 104_71) }.to raise_error(Quandl::Elasticsearch::Search::TimeoutError)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+ require 'quandl/elasticsearch/indice'
3
+ require 'active_support/all'
4
+
5
+ describe Quandl::Elasticsearch::Template do
6
+ describe '.initialize' do
7
+ it 'can initialize' do
8
+ template = Quandl::Elasticsearch::Template.new
9
+ expect(template.class).to be(Quandl::Elasticsearch::Template)
10
+ end
11
+ end
12
+
13
+ describe '.main_index' do
14
+ it 'can return the correct string' do
15
+ expect(Quandl::Elasticsearch::Template.main_index.deep_stringify_keys).to match(
16
+ {
17
+ settings: {
18
+ index: {
19
+ number_of_shards: 1, number_of_replicas: 0
20
+ },
21
+ similarity: {
22
+ quandl_bm25: {
23
+ type: 'BM25', k1: 0, b: 0.75
24
+ }
25
+ },
26
+ analysis: {
27
+ analyzer: {
28
+ keyword_lowercase: {
29
+ type: 'english', tokenizer: 'keyword', filter: 'lowercase', stopwords: '_none_'
30
+ },
31
+ quandl_index: {
32
+ type: 'custom', tokenizer: 'whitespace', filter: %w(quandl_delimiter lowercase quandl_synonym_filter quandl_stop_words quandl_stemmer trim), char_filter: ['quandl_char_filter']
33
+ },
34
+ quandl_search: {
35
+ type: 'custom', tokenizer: 'whitespace', filter: %w(quandl_delimiter lowercase quandl_stop_words quandl_stemmer trim), char_filter: ['quandl_char_filter']
36
+ }
37
+ },
38
+ filter: {
39
+ quandl_synonym_filter: {
40
+ type: 'synonym',
41
+ synonyms: be_an(Array)
42
+ },
43
+ quandl_stemmer: {
44
+ type: 'stemmer',
45
+ name: 'english'
46
+ },
47
+ quandl_stop_words: {
48
+ type: 'stop',
49
+ ignore_case: true,
50
+ stopwords: be_an(Array)
51
+ },
52
+ quandl_delimiter: {
53
+ type: 'word_delimiter',
54
+ split_on_case_change: true,
55
+ catenate_numbers: true, preserve_original: true, generate_word_parts: true, split_on_numbers: true, stem_english_prossessive: true, type_table: ['& => ALPHA', '_ => ALPHA', '$ => DIGIT', '% => DIGIT'], protected_word: ['u.s.', 'u.s.a.']
56
+ }
57
+ },
58
+ char_filter: {
59
+ quandl_char_filter: {
60
+ type: 'mapping', mappings: ['\\u0060=>\\u0020', '\\u002F=>\\u0020', '\\u003A=>\\u0020', '\\u002D=>\\u0020', '\\u0028=>\\u0020', '\\u0029=>\\u0020', '\\u002C=>\\u0020', '\\u003B=>\\u0020']
61
+ }
62
+ }
63
+ }
64
+ },
65
+ mappings: {
66
+ database: {
67
+ properties: {
68
+ code: {
69
+ type: 'string', store: true, norms: {
70
+ enabled: false
71
+ },
72
+ analyzer: 'keyword_lowercase'
73
+ },
74
+ name: {
75
+ type: 'string', store: true, norms: {
76
+ enabled: true
77
+ },
78
+ analyzer: 'quandl_index', search_analyzer: 'quandl_search'
79
+ },
80
+ description: {
81
+ type: 'string', store: true, norms: {
82
+ enabled: true
83
+ },
84
+ analyzer: 'quandl_index'
85
+ },
86
+ documentation: {
87
+ type: 'string', store: true, norms: {
88
+ enabled: true
89
+ },
90
+ analyzer: 'quandl_index'
91
+ },
92
+ premium: {
93
+ type: 'boolean', store: true, norms: {
94
+ enabled: false
95
+ }
96
+ },
97
+ hidden: {
98
+ type: 'boolean', store: true, norms: {
99
+ enabled: false
100
+ }
101
+ },
102
+ exclusive: {
103
+ type: 'boolean', store: true, norms: {
104
+ enabled: false
105
+ }
106
+ },
107
+ type: {
108
+ type: 'string', store: true, norms: {
109
+ enabled: false
110
+ },
111
+ index: 'not_analyzed'
112
+ },
113
+ rating: {
114
+ type: 'integer', store: true, norms: {
115
+ enabled: false
116
+ }, index: 'not_analyzed'
117
+ },
118
+ tags: {
119
+ type: 'string', store: true, norms: {
120
+ enabled: false
121
+ },
122
+ index: 'not_analyzed'
123
+ }
124
+ }
125
+ },
126
+ dataset: {
127
+ _parent: {
128
+ type: 'database'
129
+ },
130
+ properties: {
131
+ code: {
132
+ type: 'string', store: true, norms: {
133
+ enabled: false
134
+ },
135
+ analyzer: 'keyword_lowercase'
136
+ },
137
+ name: {
138
+ type: 'string', store: true, norms: {
139
+ enabled: true
140
+ },
141
+ analyzer: 'quandl_index', search_analyzer: 'quandl_search'
142
+ },
143
+ is_private: {
144
+ type: 'boolean', norms: {
145
+ enabled: false
146
+ },
147
+ store: false
148
+ },
149
+ type: {
150
+ type: 'string', store: true, norms: {
151
+ enabled: false
152
+ },
153
+ index: 'not_analyzed'
154
+ },
155
+ frequency: {
156
+ type: 'string', store: false, norms: {
157
+ enabled: false
158
+ },
159
+ index: 'not_analyzed'
160
+ },
161
+ to_date: {
162
+ type: 'date', format: 'date', store: false, norms: {
163
+ enabled: false
164
+ }
165
+ },
166
+ db_hidden: {
167
+ type: 'boolean', norms: { enabled: false }, store: false
168
+ },
169
+ db_exclusive: {
170
+ type: 'boolean', norms: { enabled: false }, store: false
171
+ },
172
+ sample: {
173
+ type: 'boolean', norms: { enabled: false }, store: false
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }.deep_stringify_keys
179
+ )
180
+ end
181
+ end
182
+ end