10
10
import org .apache .lucene .document .Document ;
11
11
import org .apache .lucene .document .Field ;
12
12
import org .apache .lucene .document .LongPoint ;
13
+ import org .apache .lucene .document .NumericDocValuesField ;
13
14
import org .apache .lucene .document .SortedNumericDocValuesField ;
15
+ import org .apache .lucene .document .SortedSetDocValuesField ;
14
16
import org .apache .lucene .index .DirectoryReader ;
15
17
import org .apache .lucene .index .IndexReader ;
16
18
import org .apache .lucene .index .IndexableField ;
32
34
import org .elasticsearch .index .mapper .DateFieldMapper ;
33
35
import org .elasticsearch .index .mapper .DateFieldMapper .Resolution ;
34
36
import org .elasticsearch .index .mapper .DocCountFieldMapper ;
37
+ import org .elasticsearch .index .mapper .FieldNamesFieldMapper ;
35
38
import org .elasticsearch .index .mapper .KeywordFieldMapper ;
36
39
import org .elasticsearch .index .mapper .KeywordFieldMapper .KeywordFieldType ;
37
40
import org .elasticsearch .index .mapper .MappedFieldType ;
38
41
import org .elasticsearch .index .mapper .NumberFieldMapper ;
39
42
import org .elasticsearch .index .mapper .NumberFieldMapper .NumberType ;
40
43
import org .elasticsearch .index .mapper .ObjectMapper ;
44
+ import org .elasticsearch .index .query .ExistsQueryBuilder ;
41
45
import org .elasticsearch .index .query .MatchAllQueryBuilder ;
42
46
import org .elasticsearch .index .query .MatchQueryBuilder ;
43
47
import org .elasticsearch .index .query .QueryBuilder ;
61
65
import org .elasticsearch .search .aggregations .support .AggregationContext ;
62
66
import org .elasticsearch .search .aggregations .support .AggregationInspectionHelper ;
63
67
import org .elasticsearch .search .internal .ContextIndexSearcherTests .DocumentSubsetDirectoryReader ;
64
- import org .junit .Before ;
65
68
66
69
import java .io .IOException ;
67
70
import java .util .ArrayList ;
72
75
import java .util .Map ;
73
76
import java .util .Set ;
74
77
import java .util .concurrent .TimeUnit ;
78
+ import java .util .function .IntFunction ;
75
79
76
80
import static org .hamcrest .Matchers .both ;
77
81
import static org .hamcrest .Matchers .equalTo ;
85
89
import static org .mockito .Mockito .mock ;
86
90
87
91
public class FiltersAggregatorTests extends AggregatorTestCase {
88
- private MappedFieldType fieldType ;
89
-
90
- @ Before
91
- public void setUpTest () throws Exception {
92
- super .setUp ();
93
- fieldType = new KeywordFieldMapper .KeywordFieldType ("field" );
94
- }
95
-
96
92
public void testEmpty () throws Exception {
97
93
Directory directory = newDirectory ();
98
94
RandomIndexWriter indexWriter = new RandomIndexWriter (random (), directory );
@@ -106,7 +102,12 @@ public void testEmpty() throws Exception {
106
102
}
107
103
FiltersAggregationBuilder builder = new FiltersAggregationBuilder ("test" , filters );
108
104
builder .otherBucketKey ("other" );
109
- InternalFilters response = searchAndReduce (indexSearcher , new MatchAllDocsQuery (), builder , fieldType );
105
+ InternalFilters response = searchAndReduce (
106
+ indexSearcher ,
107
+ new MatchAllDocsQuery (),
108
+ builder ,
109
+ new KeywordFieldMapper .KeywordFieldType ("field" )
110
+ );
110
111
assertEquals (response .getBuckets ().size (), numFilters );
111
112
for (InternalFilters .InternalBucket filter : response .getBuckets ()) {
112
113
assertEquals (filter .getDocCount (), 0 );
@@ -206,7 +207,12 @@ public void testKeyedFilter() throws Exception {
206
207
FiltersAggregationBuilder builder = new FiltersAggregationBuilder ("test" , keys );
207
208
builder .otherBucket (true );
208
209
builder .otherBucketKey ("other" );
209
- final InternalFilters filters = searchAndReduce (indexSearcher , new MatchAllDocsQuery (), builder , fieldType );
210
+ final InternalFilters filters = searchAndReduce (
211
+ indexSearcher ,
212
+ new MatchAllDocsQuery (),
213
+ builder ,
214
+ new KeywordFieldMapper .KeywordFieldType ("field" )
215
+ );
210
216
assertEquals (filters .getBuckets ().size (), 7 );
211
217
assertEquals (filters .getBucketByKey ("foobar" ).getDocCount (), 2 );
212
218
assertEquals (filters .getBucketByKey ("foo" ).getDocCount (), 2 );
@@ -261,7 +267,12 @@ public void testRandom() throws Exception {
261
267
builder .otherBucket (true );
262
268
builder .otherBucketKey ("other" );
263
269
264
- final InternalFilters response = searchAndReduce (indexSearcher , new MatchAllDocsQuery (), builder , fieldType );
270
+ final InternalFilters response = searchAndReduce (
271
+ indexSearcher ,
272
+ new MatchAllDocsQuery (),
273
+ builder ,
274
+ new KeywordFieldMapper .KeywordFieldType ("field" )
275
+ );
265
276
List <InternalFilters .InternalBucket > buckets = response .getBuckets ();
266
277
assertEquals (buckets .size (), filters .length + 1 );
267
278
@@ -789,6 +800,157 @@ public void testSubAggsManyFilters() throws IOException {
789
800
}, dateFt , intFt );
790
801
}
791
802
803
+ public void testDocValuesFieldExistsForDate () throws IOException {
804
+ DateFieldMapper .DateFieldType ft = new DateFieldMapper .DateFieldType ("f" );
805
+ QueryBuilder exists ;
806
+ if (randomBoolean ()) {
807
+ exists = new ExistsQueryBuilder ("f" );
808
+ } else {
809
+ // Range query covering all values in the index is rewritten to exists
810
+ exists = new RangeQueryBuilder ("f" ).gte ("2020-01-01" ).lt ("2020-01-02" );
811
+ }
812
+ long start = DateFieldMapper .DEFAULT_DATE_TIME_FORMATTER .parseMillis ("2020-01-01T00:00:01" );
813
+ docValuesFieldExistsTestCase (exists , ft , true , i -> {
814
+ long date = start + TimeUnit .HOURS .toMillis (i );
815
+ return List .of (new LongPoint ("f" , date ), new NumericDocValuesField ("f" , date ));
816
+ });
817
+ }
818
+
819
+ public void testDocValuesFieldExistsForDateWithMultiValuedFields () throws IOException {
820
+ DateFieldMapper .DateFieldType ft = new DateFieldMapper .DateFieldType ("f" );
821
+ long start = DateFieldMapper .DEFAULT_DATE_TIME_FORMATTER .parseMillis ("2020-01-01T00:00:01" );
822
+ docValuesFieldExistsTestCase (new ExistsQueryBuilder ("f" ), ft , true , i -> {
823
+ long date = start + TimeUnit .HOURS .toMillis (i );
824
+ return List .of (
825
+ new LongPoint ("f" , date ),
826
+ new LongPoint ("f" , date + 10 ),
827
+ new SortedNumericDocValuesField ("f" , date ),
828
+ new SortedNumericDocValuesField ("f" , date + 10 )
829
+ );
830
+ });
831
+ }
832
+
833
+ public void testDocValuesFieldExistsForDateWithoutData () throws IOException {
834
+ docValuesFieldExistsNoDataTestCase (new DateFieldMapper .DateFieldType ("f" ));
835
+ }
836
+
837
+ public void testDocValuesFieldExistsForNumber () throws IOException {
838
+ NumberFieldMapper .NumberType numberType = randomFrom (NumberFieldMapper .NumberType .values ());
839
+ NumberFieldMapper .NumberFieldType ft = new NumberFieldMapper .NumberFieldType (
840
+ "f" ,
841
+ numberType ,
842
+ true ,
843
+ false ,
844
+ true ,
845
+ true ,
846
+ null ,
847
+ Map .of (),
848
+ null
849
+ );
850
+ docValuesFieldExistsTestCase (new ExistsQueryBuilder ("f" ), ft , true , i -> {
851
+ return numberType .createFields ("f" , i , true , true , false );
852
+ });
853
+ }
854
+
855
+ public void testDocValuesFieldExistsForNumberWithoutData () throws IOException {
856
+ docValuesFieldExistsNoDataTestCase (new NumberFieldMapper .NumberFieldType (
857
+ "f" ,
858
+ randomFrom (NumberFieldMapper .NumberType .values ()),
859
+ true ,
860
+ false ,
861
+ true ,
862
+ true ,
863
+ null ,
864
+ Map .of (),
865
+ null
866
+ ));
867
+ }
868
+
869
+ public void testDocValuesFieldExistsForKeyword () throws IOException {
870
+ KeywordFieldMapper .KeywordFieldType ft = new KeywordFieldMapper .KeywordFieldType ("f" , true , true , Map .of ());
871
+ docValuesFieldExistsTestCase (new ExistsQueryBuilder ("f" ), ft , false , i -> {
872
+ BytesRef text = new BytesRef (randomAlphaOfLength (5 ));
873
+ return List .of (new Field ("f" , text , KeywordFieldMapper .Defaults .FIELD_TYPE ), new SortedSetDocValuesField ("f" , text ));
874
+ });
875
+ }
876
+
877
+ public void testDocValuesFieldExistsForKeywordWithoutData () throws IOException {
878
+ docValuesFieldExistsNoDataTestCase (new KeywordFieldMapper .KeywordFieldType ("f" , true , true , Map .of ()));
879
+ }
880
+
881
+ private void docValuesFieldExistsTestCase (
882
+ QueryBuilder exists ,
883
+ MappedFieldType fieldType ,
884
+ boolean canUseMetadata ,
885
+ IntFunction <List <? extends IndexableField >> buildDocWithField
886
+ ) throws IOException {
887
+ AggregationBuilder builder = new FiltersAggregationBuilder ("test" , new KeyedFilter ("q1" , exists ));
888
+ CheckedConsumer <RandomIndexWriter , IOException > buildIndex = iw -> {
889
+ for (int i = 0 ; i < 10 ; i ++) {
890
+ iw .addDocument (buildDocWithField .apply (i ));
891
+ }
892
+ for (int i = 0 ; i < 10 ; i ++) {
893
+ iw .addDocument (List .of ());
894
+ }
895
+ };
896
+ // Exists queries convert to MatchNone if this isn't defined
897
+ FieldNamesFieldMapper .FieldNamesFieldType fnft = new FieldNamesFieldMapper .FieldNamesFieldType (true );
898
+ debugTestCase (
899
+ builder ,
900
+ new MatchAllDocsQuery (),
901
+ buildIndex ,
902
+ (InternalFilters result , Class <? extends Aggregator > impl , Map <String , Map <String , Object >> debug ) -> {
903
+ assertThat (result .getBuckets (), hasSize (1 ));
904
+ assertThat (result .getBucketByKey ("q1" ).getDocCount (), equalTo (10L ));
905
+
906
+ assertThat (impl , equalTo (FiltersAggregator .FilterByFilter .class ));
907
+ Map <?, ?> filterAggDebug = debug .get ("test" );
908
+ List <?> filtersDebug = (List <?>) filterAggDebug .get ("filters" );
909
+ Map <?, ?> filterDebug = (Map <?, ?>) filtersDebug .get (0 );
910
+ assertThat (filterDebug , hasEntry ("specialized_for" , "docvalues_field_exists" ));
911
+ assertThat ((int ) filterDebug .get ("results_from_metadata" ), canUseMetadata ? greaterThan (0 ) : equalTo (0 ));
912
+ },
913
+ fieldType ,
914
+ fnft
915
+ );
916
+ withAggregator (builder , new MatchAllDocsQuery (), buildIndex , (searcher , aggregator ) -> {
917
+ long estimatedCost = ((FiltersAggregator .FilterByFilter ) aggregator ).estimateCost (Long .MAX_VALUE );
918
+ Map <String , Object > debug = new HashMap <>();
919
+ aggregator .collectDebugInfo (debug ::put );
920
+ List <?> filtersDebug = (List <?>) debug .get ("filters" );
921
+ Map <?, ?> filterDebug = (Map <?, ?>) filtersDebug .get (0 );
922
+ assertThat (estimatedCost , canUseMetadata ? equalTo (0L ) : greaterThan (0L ));
923
+ assertThat ((int ) filterDebug .get ("scorers_prepared_while_estimating_cost" ), canUseMetadata ? equalTo (0 ) : greaterThan (0 ));
924
+ }, fieldType , fnft );
925
+ }
926
+
927
+ private void docValuesFieldExistsNoDataTestCase (
928
+ MappedFieldType fieldType
929
+ ) throws IOException {
930
+ QueryBuilder exists = new ExistsQueryBuilder (fieldType .name ());
931
+ AggregationBuilder builder = new FiltersAggregationBuilder ("test" , new KeyedFilter ("q1" , exists ));
932
+ CheckedConsumer <RandomIndexWriter , IOException > buildIndex = iw -> {
933
+ for (int i = 0 ; i < 10 ; i ++) {
934
+ iw .addDocument (List .of ());
935
+ }
936
+ };
937
+ // Exists queries convert to MatchNone if this isn't defined
938
+ FieldNamesFieldMapper .FieldNamesFieldType fnft = new FieldNamesFieldMapper .FieldNamesFieldType (true );
939
+ withAggregator (builder , new MatchAllDocsQuery (), buildIndex , (searcher , aggregator ) -> {
940
+ assertThat (aggregator , instanceOf (FiltersAggregator .FilterByFilter .class ));
941
+ long estimatedCost = ((FiltersAggregator .FilterByFilter ) aggregator ).estimateCost (Long .MAX_VALUE );
942
+ Map <String , Object > debug = collectAndGetFilterDebugInfo (searcher , aggregator );
943
+ assertThat (debug , hasEntry ("specialized_for" , "docvalues_field_exists" ));
944
+ assertThat (estimatedCost , equalTo (0L ));
945
+ assertThat ((int ) debug .get ("results_from_metadata" ), greaterThan (0 ));
946
+ assertThat ((int ) debug .get ("scorers_prepared_while_estimating_cost" ), equalTo (0 ));
947
+ }, fieldType , fnft );
948
+ testCase (builder , new MatchAllDocsQuery (), buildIndex , (InternalFilters result ) -> {
949
+ assertThat (result .getBuckets (), hasSize (1 ));
950
+ assertThat (result .getBucketByKey ("q1" ).getDocCount (), equalTo (0L ));
951
+ }, fieldType , fnft );
952
+ }
953
+
792
954
@ Override
793
955
protected List <ObjectMapper > objectMappers () {
794
956
return MOCK_OBJECT_MAPPERS ;
0 commit comments