es-query-builder 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 01a7f2c3df52977455ef8cc73bb06caffae1b5d8
4
- data.tar.gz: f9b6ddc51b5b9f64fceb830d8a9a12b5e557d362
3
+ metadata.gz: 8b7061462ef887c110a7362d082ecab6170d8800
4
+ data.tar.gz: 273dc7a2f14404b05e8bf8b573fb1f4af62650be
5
5
  SHA512:
6
- metadata.gz: 76e05d352511a51d07b53363273dc10da73ed421d09b8c92acf3bf7274a151f1756fd5ea68037a84efebc39f872c41590304be8df8aae1444bbed0a06aadf1b0
7
- data.tar.gz: 4d19002288f950ad91db2bc5a819cd9fff51f74349a4aafa0bd9536af50cea9540c10defc4ed478e1ffb440a48517c143dad5403360b877766ba6e184af8b0c7
6
+ metadata.gz: 19b39d4e940467281be485a915fa4333f808d903c0dac03d69206570a603f0b592b02e0950357b6cefb90f4229a62bc63ab884adae1394961e615aad981350a5
7
+ data.tar.gz: 36d828651c879de1cdfb76b59bbdc4de8772372c72845acd7fcfb8929a44ecbb3d1c6f4b824553d038cc75f26c432f96bda09bb2e589f2d3594a65eaf115f994
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.0.2 - January 3, 2015
4
+
5
+ - [[#1](https://github.com/increments/es-query-builder/pull/1)] Support nested query and has_child query by [@f-kubotar](https://github.com/f-kubotar).
6
+
7
+ ## 0.0.1 - November 15, 2014
8
+
9
+ - The initial release.
@@ -47,11 +47,14 @@ class EsQueryBuilder
47
47
  #
48
48
  # Returns nothing.
49
49
  def initialize(query_fields: [], filter_fields: [],
50
- all_query_fields: '_all', hierarchy_fields: [])
50
+ all_query_fields: '_all', hierarchy_fields: [],
51
+ nested_fields: {}, child_fields: {})
51
52
  @query_fields = query_fields
52
53
  @filter_fields = filter_fields
53
54
  @all_query_fields = all_query_fields
54
55
  @hierarchy_fields = hierarchy_fields
56
+ @nested_fields = nested_fields
57
+ @child_fields = child_fields
55
58
  end
56
59
 
57
60
  # Public: Convert the given query string into a query object.
@@ -88,6 +91,10 @@ class EsQueryBuilder
88
91
  #
89
92
  # Returns a Parser.
90
93
  def parser
91
- @parser ||= Parser.new(@all_query_fields, @hierarchy_fields)
94
+ @parser ||= Parser.new(
95
+ all_query_fields: @all_query_fields,
96
+ hierarchy_fields: @hierarchy_fields,
97
+ nested_fields: @nested_fields,
98
+ child_fields: @child_fields)
92
99
  end
93
100
  end
@@ -18,9 +18,12 @@ class EsQueryBuilder
18
18
  # character as a hierarchy (default: []).
19
19
  #
20
20
  # Returns nothing.
21
- def initialize(all_query_fields = '_all', hierarchy_fields = [])
21
+ def initialize(all_query_fields: '_all', hierarchy_fields: [],
22
+ nested_fields: {}, child_fields: {})
22
23
  @all_query_fields = all_query_fields
23
24
  @hierarchy_fields = hierarchy_fields
25
+ @nested_fields = nested_fields
26
+ @child_fields = child_fields
24
27
  end
25
28
 
26
29
  # Public: Parse the given tokens and build a query hash.
@@ -155,25 +158,70 @@ class EsQueryBuilder
155
158
  def create_bool_queries(query_tokens)
156
159
  must, must_not = [], []
157
160
  query_tokens.each do |token|
158
- # When the field is not given or invalid one, search by all fields.
159
- field = token.field || @all_query_fields
160
161
  queries = token.minus? ? must_not : must
161
- if field.is_a?(String)
162
- queries << {
163
- match: {
164
- field => token.term
165
- }
162
+
163
+ queries <<
164
+ # When the field is not given or invalid one, search by all fields.
165
+ if token.field.nil?
166
+ should = []
167
+ should << create_match_query(@all_query_fields, token.term)
168
+ @nested_fields.each do |nested_path, nested_field|
169
+ should << create_nested_match_query(nested_path, nested_field, token.term)
170
+ end
171
+ @child_fields.each do |child_type, child_field|
172
+ should << create_has_child_match_query(child_type, child_field, token.term)
173
+ end
174
+ connect_queries(should)
175
+
176
+ # When the specify nested field
177
+ elsif nested_field = @nested_fields[token.field_namespace]
178
+ create_nested_match_query(token.field_namespace, nested_field, token.term)
179
+
180
+ # When the specify child field
181
+ elsif child_field = @child_fields[token.field_namespace]
182
+ create_has_child_match_query(token.field_namespace, child_field, token.term)
183
+
184
+ # When the specify standard field
185
+ else
186
+ create_match_query(token.field, token.term)
187
+ end
188
+ end
189
+ [must, must_not]
190
+ end
191
+
192
+ def create_match_query(field, term)
193
+ if field.is_a?(String)
194
+ {
195
+ match: {
196
+ field => term
166
197
  }
167
- else
168
- queries << {
169
- multi_match: {
170
- fields: field,
171
- query: token.term
172
- }
198
+ }
199
+ else
200
+ {
201
+ multi_match: {
202
+ fields: field,
203
+ query: term
173
204
  }
174
- end
205
+ }
175
206
  end
176
- [must, must_not]
207
+ end
208
+
209
+ def create_nested_match_query(path, field, term)
210
+ {
211
+ nested: {
212
+ path: path.to_s,
213
+ query: create_match_query(field, term)
214
+ }
215
+ }
216
+ end
217
+
218
+ def create_has_child_match_query(child_type, field, term)
219
+ {
220
+ has_child: {
221
+ type: child_type.to_s,
222
+ query: create_match_query(field, term)
223
+ }
224
+ }
177
225
  end
178
226
 
179
227
  # Internal: Create boolean filter based on the filter matches.
@@ -1,6 +1,6 @@
1
1
  class EsQueryBuilder
2
2
  class Token
3
- attr_reader :full, :field, :term
3
+ attr_reader :full, :field, :term, :field_namespace
4
4
 
5
5
  TYPE_KINDS = %i(query filter or).freeze
6
6
 
@@ -10,6 +10,7 @@ class EsQueryBuilder
10
10
  @field = field
11
11
  @term = term
12
12
  @type = type
13
+ @field_namespace = field.to_s[/^(.+?)\./, 1]
13
14
  end
14
15
 
15
16
  def minus?
@@ -14,7 +14,7 @@ class EsQueryBuilder
14
14
 
15
15
  OR_CONDITION = /^OR$/i
16
16
 
17
- # Public: COnstruct the tokenizer object.
17
+ # Public: Construct the tokenizer object.
18
18
  #
19
19
  # filter_fields - An Array of Strings for specifing allowed filtering
20
20
  # types (default: []).
@@ -35,8 +35,8 @@ class EsQueryBuilder
35
35
  #
36
36
  # tokenize('hello OR tag:world')
37
37
  # # => [<Token: @full="hello", @type=:query, ...>,
38
- # <Token: @full="OR", @type=:or, ...>,
39
- # <Token: @full="tag:world", @type=:filter, ...>]
38
+ # # <Token: @full="OR", @type=:or, ...>,
39
+ # # <Token: @full="tag:world", @type=:filter, ...>]
40
40
  #
41
41
  # Returns an Array of Tokens.
42
42
  def tokenize(query_string)
@@ -1,3 +1,3 @@
1
1
  class EsQueryBuilder
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -90,6 +90,426 @@ describe EsQueryBuilder do
90
90
  end
91
91
  end
92
92
  end
93
+
94
+ context 'and it is constructed with nested_fields' do
95
+ let(:param) do
96
+ { nested_fields: { hoge: ['hoge.title'] } }
97
+ end
98
+
99
+ let(:all_query_fields) do
100
+ { nested: { hoge: ['hoge.title'] } }
101
+ end
102
+
103
+ it "returns a nested query for the specified nested path and fields" do
104
+ should eq(
105
+ {
106
+ bool: {
107
+ should: [
108
+ {
109
+ match: {
110
+ '_all' => term
111
+ }
112
+ },
113
+ {
114
+ nested: {
115
+ path: 'hoge',
116
+ query: {
117
+ multi_match: {
118
+ fields: ['hoge.title'],
119
+ query: term
120
+ }
121
+ }
122
+ }
123
+ }
124
+ ]
125
+ }
126
+ }
127
+ )
128
+ end
129
+
130
+ context 'and the query starts with a minus char' do
131
+ let(:query_string) do
132
+ '-' + term
133
+ end
134
+
135
+ it 'returns a must_not query for the specified query fields' do
136
+ should eq(
137
+ bool: {
138
+ must_not: [
139
+ {
140
+ bool: {
141
+ should: [
142
+ {
143
+ match: {
144
+ '_all' => term
145
+ }
146
+ },
147
+ {
148
+ nested: {
149
+ path: 'hoge',
150
+ query: {
151
+ multi_match: {
152
+ fields: ['hoge.title'],
153
+ query: term
154
+ }
155
+ }
156
+ }
157
+ }
158
+ ]
159
+ }
160
+ }
161
+ ]
162
+ }
163
+ )
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'and it is constructed with child_fields' do
169
+ let(:param) do
170
+ { child_fields: { child_type => child_field } }
171
+ end
172
+
173
+ let(:child_type) do
174
+ 'hoge'
175
+ end
176
+
177
+ let(:child_field) do
178
+ ['title']
179
+ end
180
+
181
+ it "returns a has_child query for the specified child field" do
182
+ should eq(
183
+ {
184
+ bool: {
185
+ should: [
186
+ {
187
+ match: {
188
+ '_all' => term
189
+ }
190
+ },
191
+ {
192
+ has_child: {
193
+ type: child_type,
194
+ query: {
195
+ multi_match: {
196
+ fields: child_field,
197
+ query: term
198
+ }
199
+ }
200
+ }
201
+ }
202
+ ]
203
+ }
204
+ }
205
+ )
206
+ end
207
+
208
+ context 'and the query starts with a minus char' do
209
+ let(:query_string) do
210
+ '-' + term
211
+ end
212
+
213
+ it 'returns a must_not query for the specified query fields' do
214
+ should eq(
215
+ bool: {
216
+ must_not: [
217
+ {
218
+ bool: {
219
+ should: [
220
+ {
221
+ match: {
222
+ '_all' => term
223
+ }
224
+ },
225
+ {
226
+ has_child: {
227
+ type: child_type,
228
+ query: {
229
+ multi_match: {
230
+ fields: child_field,
231
+ query: term
232
+ }
233
+ }
234
+ }
235
+ }
236
+ ]
237
+ }
238
+ }
239
+ ]
240
+ }
241
+ )
242
+ end
243
+ end
244
+ end
245
+
246
+ context 'and it is constructed with both all_query_fields and nested fields' do
247
+ let(:param) do
248
+ {
249
+ all_query_fields: ['hoge'],
250
+ nested_fields: { hoge: ['hoge.title'] }
251
+ }
252
+ end
253
+
254
+ it "returns a nested query for the specified nested path and fields" do
255
+ should eq(
256
+ {
257
+ bool: {
258
+ should: [
259
+ {
260
+ multi_match: {
261
+ fields: ['hoge'],
262
+ query: term
263
+ }
264
+ },
265
+ {
266
+ nested: {
267
+ path: 'hoge',
268
+ query: {
269
+ multi_match: {
270
+ fields: ['hoge.title'],
271
+ query: term
272
+ }
273
+ }
274
+ }
275
+ }
276
+ ]
277
+ }
278
+ }
279
+ )
280
+ end
281
+
282
+ context 'and the query starts with a minus char' do
283
+ let(:query_string) do
284
+ '-' + term
285
+ end
286
+
287
+ it 'returns a must_not query for the specified query fields' do
288
+ should eq(
289
+ bool: {
290
+ must_not: [
291
+ {
292
+ bool: {
293
+ should: [
294
+ {
295
+ multi_match: {
296
+ fields: ['hoge'],
297
+ query: term
298
+ }
299
+ },
300
+ {
301
+ nested: {
302
+ path: 'hoge',
303
+ query: {
304
+ multi_match: {
305
+ fields: ['hoge.title'],
306
+ query: term
307
+ }
308
+ }
309
+ }
310
+ }
311
+ ]
312
+ }
313
+ }
314
+ ]
315
+ }
316
+ )
317
+ end
318
+ end
319
+ end
320
+
321
+ context 'and it is constructed with both all_query_fields and child_fields' do
322
+ let(:param) do
323
+ {
324
+ all_query_fields: ['hoge'],
325
+ child_fields: { hoge: ['hoge.title'] }
326
+ }
327
+ end
328
+
329
+ it "returns a nested query for the specified nested path and fields" do
330
+ should eq(
331
+ {
332
+ bool: {
333
+ should: [
334
+ {
335
+ multi_match: {
336
+ fields: ['hoge'],
337
+ query: term
338
+ }
339
+ },
340
+ {
341
+ has_child: {
342
+ type: 'hoge',
343
+ query: {
344
+ multi_match: {
345
+ fields: ['hoge.title'],
346
+ query: term
347
+ }
348
+ }
349
+ }
350
+ }
351
+ ]
352
+ }
353
+ }
354
+ )
355
+ end
356
+
357
+ context 'and the query starts with a minus char' do
358
+ let(:query_string) do
359
+ '-' + term
360
+ end
361
+
362
+ it 'returns a must_not query for the specified query fields' do
363
+ should eq(
364
+ bool: {
365
+ must_not: [
366
+ {
367
+ bool: {
368
+ should: [
369
+ {
370
+ multi_match: {
371
+ fields: ['hoge'],
372
+ query: term
373
+ }
374
+ },
375
+ {
376
+ has_child: {
377
+ type: 'hoge',
378
+ query: {
379
+ multi_match: {
380
+ fields: ['hoge.title'],
381
+ query: term
382
+ }
383
+ }
384
+ }
385
+ }
386
+ ]
387
+ }
388
+ }
389
+ ]
390
+ }
391
+ )
392
+ end
393
+ end
394
+ end
395
+
396
+ context 'and it is constructed with all_query_fields, nested_fields, child_fields' do
397
+ let(:param) do
398
+ {
399
+ all_query_fields: all_query_fields,
400
+ nested_fields: { nested_path => nested_fields },
401
+ child_fields: { child_type => child_fields },
402
+ }
403
+ end
404
+
405
+ let(:all_query_fields) do
406
+ ['parent_field1']
407
+ end
408
+
409
+ let(:nested_path) do
410
+ 'hoge'
411
+ end
412
+
413
+ let(:nested_fields) do
414
+ ['hoge.field1']
415
+ end
416
+
417
+ let(:child_type) do
418
+ 'fuga'
419
+ end
420
+
421
+ let(:child_fields) do
422
+ ['child_field1']
423
+ end
424
+
425
+ it "returns a bool query for the match in nested or child" do
426
+ should eq(
427
+ {
428
+ bool: {
429
+ should: [
430
+ {
431
+ multi_match: {
432
+ fields: all_query_fields,
433
+ query: term
434
+ }
435
+ },
436
+ {
437
+ nested: {
438
+ path: nested_path,
439
+ query: {
440
+ multi_match: {
441
+ fields: nested_fields,
442
+ query: term
443
+ }
444
+ }
445
+ }
446
+ },
447
+ {
448
+ has_child: {
449
+ type: child_type,
450
+ query: {
451
+ multi_match: {
452
+ fields: child_fields,
453
+ query: term
454
+ }
455
+ }
456
+ }
457
+ }
458
+ ]
459
+ }
460
+ }
461
+ )
462
+ end
463
+
464
+ context 'and the query starts with a minus char' do
465
+ let(:query_string) do
466
+ '-' + term
467
+ end
468
+
469
+ it 'returns a must_not query for the specified query fields' do
470
+ should eq(
471
+ bool: {
472
+ must_not: [
473
+ {
474
+ bool: {
475
+ should: [
476
+ {
477
+ multi_match: {
478
+ fields: all_query_fields,
479
+ query: term
480
+ }
481
+ },
482
+ {
483
+ nested: {
484
+ path: nested_path,
485
+ query: {
486
+ multi_match: {
487
+ fields: nested_fields,
488
+ query: term
489
+ }
490
+ }
491
+ }
492
+ },
493
+ {
494
+ has_child: {
495
+ type: child_type,
496
+ query: {
497
+ multi_match: {
498
+ fields: child_fields,
499
+ query: term
500
+ }
501
+ }
502
+ }
503
+ }
504
+ ]
505
+ }
506
+ }
507
+ ]
508
+ }
509
+ )
510
+ end
511
+ end
512
+ end
93
513
  end
94
514
 
95
515
  context 'when term queries are given' do
@@ -206,6 +626,140 @@ describe EsQueryBuilder do
206
626
  )
207
627
  end
208
628
  end
629
+
630
+ context 'and it is constructed with nested_fields' do
631
+ let(:param) do
632
+ { nested_fields: { nested_path => nested_fields } }
633
+ end
634
+
635
+ let(:nested_path) do
636
+ 'hoge'
637
+ end
638
+
639
+ let(:nested_fields) do
640
+ ['hoge.title']
641
+ end
642
+
643
+ it 'returns a bool query with must match queries' do
644
+ should eq(
645
+ bool: {
646
+ must: [
647
+ {
648
+ bool: {
649
+ should: [
650
+ {
651
+ match: {
652
+ '_all' => term_1
653
+ }
654
+ },
655
+ {
656
+ nested: {
657
+ path: nested_path,
658
+ query: {
659
+ multi_match: {
660
+ fields: nested_fields,
661
+ query: term_1,
662
+ }
663
+ }
664
+ }
665
+ }
666
+ ]
667
+ }
668
+ },
669
+ {
670
+ bool: {
671
+ should: [
672
+ {
673
+ match: {
674
+ '_all' => term_2
675
+ }
676
+ },
677
+ {
678
+ nested: {
679
+ path: nested_path,
680
+ query: {
681
+ multi_match: {
682
+ fields: nested_fields,
683
+ query: term_2,
684
+ }
685
+ }
686
+ }
687
+ }
688
+ ]
689
+ }
690
+ }
691
+ ]
692
+ }
693
+ )
694
+ end
695
+ end
696
+
697
+ context 'and it is constructed with child_fields' do
698
+ let(:param) do
699
+ { child_fields: { child_type => child_fields } }
700
+ end
701
+
702
+ let(:child_type) do
703
+ 'hoge'
704
+ end
705
+
706
+ let(:child_fields) do
707
+ ['hoge.title']
708
+ end
709
+
710
+ it 'returns a bool query with must match queries' do
711
+ should eq(
712
+ bool: {
713
+ must: [
714
+ {
715
+ bool: {
716
+ should: [
717
+ {
718
+ match: {
719
+ '_all' => term_1
720
+ }
721
+ },
722
+ {
723
+ has_child: {
724
+ type: child_type,
725
+ query: {
726
+ multi_match: {
727
+ fields: child_fields,
728
+ query: term_1,
729
+ }
730
+ }
731
+ }
732
+ }
733
+ ]
734
+ }
735
+ },
736
+ {
737
+ bool: {
738
+ should: [
739
+ {
740
+ match: {
741
+ '_all' => term_2
742
+ }
743
+ },
744
+ {
745
+ has_child: {
746
+ type: child_type,
747
+ query: {
748
+ multi_match: {
749
+ fields: child_fields,
750
+ query: term_2,
751
+ }
752
+ }
753
+ }
754
+ }
755
+ ]
756
+ }
757
+ }
758
+ ]
759
+ }
760
+ )
761
+ end
762
+ end
209
763
  end
210
764
 
211
765
  context 'when a field query is given' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: es-query-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuku Takahashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-15 00:00:00.000000000 Z
11
+ date: 2015-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -61,6 +61,7 @@ extra_rdoc_files: []
61
61
  files:
62
62
  - ".gitignore"
63
63
  - ".travis.yml"
64
+ - CHANGELOG.md
64
65
  - Gemfile
65
66
  - LICENSE.txt
66
67
  - README.md