/backend/elasticsearch/src/main/java/org/hibernate/search/backend/elasticsearch/search/aggregation/impl/ElasticsearchTermsAggregation.java

https://github.com/Unimarket/hibernate-search · Java · 173 lines · 135 code · 27 blank · 11 comment · 3 complexity · c02df883e8f8cb7d19993665b02057ce MD5 · raw file

  1. /*
  2. * Hibernate Search, full-text search for your domain model
  3. *
  4. * License: GNU Lesser General Public License (LGPL), version 2.1 or later
  5. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
  6. */
  7. package org.hibernate.search.backend.elasticsearch.search.aggregation.impl;
  8. import java.util.Map;
  9. import org.hibernate.search.backend.elasticsearch.search.common.impl.AbstractElasticsearchCodecAwareSearchQueryElementFactory;
  10. import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
  11. import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexValueFieldContext;
  12. import org.hibernate.search.backend.elasticsearch.types.codec.impl.ElasticsearchFieldCodec;
  13. import org.hibernate.search.engine.backend.types.converter.runtime.FromDocumentValueConvertContext;
  14. import org.hibernate.search.engine.backend.types.converter.spi.ProjectionConverter;
  15. import org.hibernate.search.engine.search.aggregation.spi.TermsAggregationBuilder;
  16. import org.hibernate.search.engine.search.common.ValueConvert;
  17. import org.hibernate.search.util.common.impl.CollectionHelper;
  18. import com.google.gson.JsonArray;
  19. import com.google.gson.JsonElement;
  20. import com.google.gson.JsonObject;
  21. /**
  22. * @param <F> The type of field values.
  23. * @param <K> The type of keys in the returned map. It can be {@code F}
  24. * or a different type if value converters are used.
  25. */
  26. public class ElasticsearchTermsAggregation<F, K>
  27. extends AbstractElasticsearchBucketAggregation<K, Long> {
  28. private final String absoluteFieldPath;
  29. private final ProjectionConverter<F, ? extends K> fromFieldValueConverter;
  30. private final ElasticsearchFieldCodec<F> codec;
  31. private final JsonObject order;
  32. private final int size;
  33. private final int minDocCount;
  34. private ElasticsearchTermsAggregation(Builder<F, K> builder) {
  35. super( builder );
  36. this.absoluteFieldPath = builder.field.absolutePath();
  37. this.fromFieldValueConverter = builder.fromFieldValueConverter;
  38. this.codec = builder.codec;
  39. this.order = builder.order;
  40. this.size = builder.size;
  41. this.minDocCount = builder.minDocCount;
  42. }
  43. @Override
  44. protected void doRequest(JsonObject outerObject, JsonObject innerObject) {
  45. outerObject.add( "terms", innerObject );
  46. innerObject.addProperty( "field", absoluteFieldPath );
  47. if ( order != null ) {
  48. innerObject.add( "order", order );
  49. }
  50. innerObject.addProperty( "size", size );
  51. innerObject.addProperty( "min_doc_count", minDocCount );
  52. }
  53. @Override
  54. protected Map<K, Long> doExtract(AggregationExtractContext context, JsonElement buckets) {
  55. JsonArray bucketArray = buckets.getAsJsonArray();
  56. Map<K, Long> result = CollectionHelper.newLinkedHashMap( bucketArray.size() );
  57. FromDocumentValueConvertContext convertContext = context.fromDocumentValueConvertContext();
  58. for ( JsonElement bucketElement : bucketArray ) {
  59. JsonObject bucket = bucketElement.getAsJsonObject();
  60. JsonElement keyJson = bucket.get( "key" );
  61. JsonElement keyAsStringJson = bucket.get( "key_as_string" );
  62. K key = fromFieldValueConverter.fromDocumentValue(
  63. codec.decodeAggregationKey( keyJson, keyAsStringJson ),
  64. convertContext
  65. );
  66. long documentCount = getBucketDocCount( bucket );
  67. result.put( key, documentCount );
  68. }
  69. return result;
  70. }
  71. public static class Factory<F>
  72. extends AbstractElasticsearchCodecAwareSearchQueryElementFactory<TermsAggregationBuilder.TypeSelector, F> {
  73. public Factory(ElasticsearchFieldCodec<F> codec) {
  74. super( codec );
  75. }
  76. @Override
  77. public TypeSelector<?> create(ElasticsearchSearchIndexScope<?> scope,
  78. ElasticsearchSearchIndexValueFieldContext<F> field) {
  79. return new TypeSelector<>( codec, scope, field );
  80. }
  81. }
  82. private static class TypeSelector<F> implements TermsAggregationBuilder.TypeSelector {
  83. private final ElasticsearchFieldCodec<F> codec;
  84. private final ElasticsearchSearchIndexScope<?> scope;
  85. private final ElasticsearchSearchIndexValueFieldContext<F> field;
  86. private TypeSelector(ElasticsearchFieldCodec<F> codec,
  87. ElasticsearchSearchIndexScope<?> scope, ElasticsearchSearchIndexValueFieldContext<F> field) {
  88. this.codec = codec;
  89. this.scope = scope;
  90. this.field = field;
  91. }
  92. @Override
  93. public <T> Builder<F, T> type(Class<T> expectedType, ValueConvert convert) {
  94. return new Builder<>( codec, scope, field,
  95. field.type().projectionConverter( convert ).withConvertedType( expectedType, field ) );
  96. }
  97. }
  98. private static class Builder<F, K> extends AbstractBuilder<K, Long>
  99. implements TermsAggregationBuilder<K> {
  100. private final ElasticsearchFieldCodec<F> codec;
  101. private final ProjectionConverter<F, ? extends K> fromFieldValueConverter;
  102. private JsonObject order;
  103. private int minDocCount = 1;
  104. private int size = 100;
  105. private Builder(ElasticsearchFieldCodec<F> codec, ElasticsearchSearchIndexScope<?> scope,
  106. ElasticsearchSearchIndexValueFieldContext<F> field,
  107. ProjectionConverter<F, ? extends K> fromFieldValueConverter) {
  108. super( scope, field );
  109. this.codec = codec;
  110. this.fromFieldValueConverter = fromFieldValueConverter;
  111. }
  112. @Override
  113. public void orderByCountDescending() {
  114. order( "_count", "desc" );
  115. }
  116. @Override
  117. public void orderByCountAscending() {
  118. order( "_count", "asc" );
  119. }
  120. @Override
  121. public void orderByTermAscending() {
  122. order( scope.searchSyntax().getTermAggregationOrderByTermToken(), "asc" );
  123. }
  124. @Override
  125. public void orderByTermDescending() {
  126. order( scope.searchSyntax().getTermAggregationOrderByTermToken(), "desc" );
  127. }
  128. @Override
  129. public void minDocumentCount(int minDocumentCount) {
  130. this.minDocCount = minDocumentCount;
  131. }
  132. @Override
  133. public void maxTermCount(int maxTermCount) {
  134. this.size = maxTermCount;
  135. }
  136. @Override
  137. public ElasticsearchTermsAggregation<F, K> build() {
  138. return new ElasticsearchTermsAggregation<>( this );
  139. }
  140. protected final void order(String key, String order) {
  141. JsonObject orderObject = new JsonObject();
  142. orderObject.addProperty( key, order );
  143. this.order = orderObject;
  144. }
  145. }
  146. }