All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.liferay.search.experiences.internal.blueprint.aggregation.AggregationWrapperConverter Maven / Gradle / Ivy

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Liferay Enterprise
 * Subscription License ("License"). You may not use this file except in
 * compliance with the License. You can obtain a copy of the License by
 * contacting Liferay, Inc. See the License for the specific language governing
 * permissions and limitations under the License, including but not limited to
 * distribution rights of the Software.
 *
 *
 *
 */

package com.liferay.search.experiences.internal.blueprint.aggregation;

import com.liferay.petra.reflect.ReflectionUtil;
import com.liferay.petra.string.StringBundler;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.json.JSONUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.HashMapBuilder;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.search.aggregation.Aggregation;
import com.liferay.portal.search.aggregation.Aggregations;
import com.liferay.portal.search.aggregation.bucket.CollectionMode;
import com.liferay.portal.search.aggregation.bucket.DateHistogramAggregation;
import com.liferay.portal.search.aggregation.bucket.DateRangeAggregation;
import com.liferay.portal.search.aggregation.bucket.DiversifiedSamplerAggregation;
import com.liferay.portal.search.aggregation.bucket.FilterAggregation;
import com.liferay.portal.search.aggregation.bucket.FiltersAggregation;
import com.liferay.portal.search.aggregation.bucket.GeoDistanceAggregation;
import com.liferay.portal.search.aggregation.bucket.GeoHashGridAggregation;
import com.liferay.portal.search.aggregation.bucket.GlobalAggregation;
import com.liferay.portal.search.aggregation.bucket.HistogramAggregation;
import com.liferay.portal.search.aggregation.bucket.IncludeExcludeClause;
import com.liferay.portal.search.aggregation.bucket.MissingAggregation;
import com.liferay.portal.search.aggregation.bucket.NestedAggregation;
import com.liferay.portal.search.aggregation.bucket.Order;
import com.liferay.portal.search.aggregation.bucket.Range;
import com.liferay.portal.search.aggregation.bucket.RangeAggregation;
import com.liferay.portal.search.aggregation.bucket.ReverseNestedAggregation;
import com.liferay.portal.search.aggregation.bucket.SamplerAggregation;
import com.liferay.portal.search.aggregation.bucket.SignificantTermsAggregation;
import com.liferay.portal.search.aggregation.bucket.SignificantTextAggregation;
import com.liferay.portal.search.aggregation.bucket.TermsAggregation;
import com.liferay.portal.search.aggregation.metrics.AvgAggregation;
import com.liferay.portal.search.aggregation.metrics.CardinalityAggregation;
import com.liferay.portal.search.aggregation.metrics.ExtendedStatsAggregation;
import com.liferay.portal.search.aggregation.metrics.GeoBoundsAggregation;
import com.liferay.portal.search.aggregation.metrics.GeoCentroidAggregation;
import com.liferay.portal.search.aggregation.metrics.MaxAggregation;
import com.liferay.portal.search.aggregation.metrics.MinAggregation;
import com.liferay.portal.search.aggregation.metrics.PercentileRanksAggregation;
import com.liferay.portal.search.aggregation.metrics.PercentilesAggregation;
import com.liferay.portal.search.aggregation.metrics.PercentilesMethod;
import com.liferay.portal.search.aggregation.metrics.ScriptedMetricAggregation;
import com.liferay.portal.search.aggregation.metrics.StatsAggregation;
import com.liferay.portal.search.aggregation.metrics.SumAggregation;
import com.liferay.portal.search.aggregation.metrics.TopHitsAggregation;
import com.liferay.portal.search.aggregation.metrics.ValueCountAggregation;
import com.liferay.portal.search.aggregation.metrics.WeightedAvgAggregation;
import com.liferay.portal.search.aggregation.pipeline.AvgBucketPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.BucketScriptPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.BucketSelectorPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.BucketSortPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.CumulativeSumPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.DerivativePipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.ExtendedStatsBucketPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.GapPolicy;
import com.liferay.portal.search.aggregation.pipeline.MaxBucketPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.MinBucketPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.MovingFunctionPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.PercentilesBucketPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.PipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.SerialDiffPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.StatsBucketPipelineAggregation;
import com.liferay.portal.search.aggregation.pipeline.SumBucketPipelineAggregation;
import com.liferay.portal.search.geolocation.DistanceUnit;
import com.liferay.portal.search.geolocation.GeoBuilders;
import com.liferay.portal.search.geolocation.GeoDistanceType;
import com.liferay.portal.search.query.Query;
import com.liferay.portal.search.script.Script;
import com.liferay.portal.search.script.ScriptField;
import com.liferay.portal.search.script.ScriptFieldBuilder;
import com.liferay.portal.search.script.Scripts;
import com.liferay.portal.search.significance.SignificanceHeuristic;
import com.liferay.portal.search.significance.SignificanceHeuristics;
import com.liferay.portal.search.sort.FieldSort;
import com.liferay.portal.search.sort.SortOrder;
import com.liferay.portal.search.sort.Sorts;
import com.liferay.search.experiences.internal.blueprint.highlight.HighlightConverter;
import com.liferay.search.experiences.internal.blueprint.query.QueryConverter;
import com.liferay.search.experiences.internal.blueprint.script.ScriptConverter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author Petteri Karttunen
 */
public class AggregationWrapperConverter {

	public AggregationWrapperConverter(
		Aggregations aggregations, GeoBuilders geoBuilders,
		HighlightConverter highlightConverter, QueryConverter queryConverter,
		ScriptConverter scriptConverter,
		SignificanceHeuristics significanceHeuristics, Sorts sorts) {

		_aggregations = aggregations;
		_geoBuilders = geoBuilders;
		_highlightConverter = highlightConverter;
		_queryConverter = queryConverter;
		_scriptConverter = scriptConverter;
		_significanceHeuristics = significanceHeuristics;
		_sorts = sorts;

		// Bucket

		_convertFunctions.putAll(
			HashMapBuilder.put(
				"date_histogram", this::_toDateHistogramAggregation
			).put(
				"date_range", this::_toDateRangeAggregation
			).put(
				"diversified_sampler", this::_toDiversifiedSamplerAggregation
			).put(
				"filter", this::_toFilterAggregation
			).put(
				"filters", this::_toFiltersAggregation
			).put(
				"geo_distance", this::_toGeoDistanceAggregation
			).put(
				"geohash_grid", this::_toGeoHashGridAggregation
			).put(
				"global", this::_toGlobalAggregation
			).put(
				"histogram", this::_toHistogramAggregation
			).put(
				"missing", this::_toMissingAggregation
			).put(
				"nested", this::_toNestedAggregation
			).put(
				"range", this::_toRangeAggregation
			).put(
				"reverse_nested", this::_toReverseNestedAggregation
			).put(
				"sampler", this::_toSamplerAggregation
			).put(
				"significant_terms", this::_toSignificantTermsAggregation
			).put(
				"significant_text", this::_toSignificantTextAggregation
			).put(
				"terms", this::_toTermsAggregation
			).build());

		// Metrics

		_convertFunctions.putAll(
			HashMapBuilder.put(
				"avg", this::_toAvgAggregation
			).put(
				"cardinality", this::_toCardinalityAggregation
			).put(
				"extended_stats", this::_toExtendedStatsAggregation
			).put(
				"geo_bounds", this::_toGeoBoundsAggregation
			).put(
				"geo_centroid", this::_toGeoCentroidAggregation
			).put(
				"max", this::_toMaxAggregation
			).put(
				"min", this::_toMinAggregation
			).put(
				"percentile_ranks", this::_toPercentileRanksAggregation
			).put(
				"percentiles", this::_toPercentilesAggregation
			).put(
				"scripted_metric", this::_toScriptedMetricAggregation
			).put(
				"scripted_metric", this::_toStatsAggregation
			).put(
				"sum", this::_toSumAggregation
			).put(
				"top_hits", this::_toTopHitsAggregation
			).put(
				"value_count", this::_toValueCountAggregation
			).put(
				"weighted_avg", this::_toWeightedAvgAggregation
			).build());

		// Pipeline

		_convertFunctions.putAll(
			HashMapBuilder.put(
				"avg_bucket", this::_toAvgBucketPipelineAggregation
			).put(
				"bucket_script", this::_toBucketScriptPipelineAggregation
			).put(
				"bucket_selector", this::_toBucketSelectorPipelineAggregation
			).put(
				"bucket_sort", this::_toBucketSortPipelineAggregation
			).put(
				"cumulative_sum", this::_toCumulativeSumPipelineAggregation
			).put(
				"derivative", this::_toDerivativePipelineAggregation
			).put(
				"extended_stats_bucket",
				this::_toExtendedStatsBucketPipelineAggregation
			).put(
				"max_bucket", this::_toMaxBucketPipelineAggregation
			).put(
				"min_bucket", this::_toMinBucketPipelineAggregation
			).put(
				"moving_function", this::_toMovingFunctionPipelineAggregation
			).put(
				"percentiles_bucket",
				this::_toPercentilesBucketPipelineAggregation
			).put(
				"serial_differencing", this::_toSerialDiffPipelineAggregation
			).put(
				"stats_bucket", this::_toStatsBucketPipelineAggregation
			).put(
				"sum_bucket", this::_toSumBucketPipelineAggregation
			).build());

		_scripts = scriptConverter.getScripts();
	}

	public AggregationWrapper toAggregationWrapper(
		JSONObject jsonObject, String name, String type) {

		ConvertFunction convertFunction = _convertFunctions.get(type);

		if (convertFunction == null) {
			throw new IllegalArgumentException("Unknown aggregation " + type);
		}

		try {
			Object object = convertFunction.apply(jsonObject, name);

			if (object == null) {
				return null;
			}

			if (object instanceof Aggregation) {
				return new AggregationWrapper((Aggregation)object);
			}
			else if (object instanceof PipelineAggregation) {
				return new AggregationWrapper((PipelineAggregation)object);
			}

			throw new RuntimeException(
				StringBundler.concat(
					"JSON ", jsonObject, " converted to unexpected class ",
					object.getClass()));
		}
		catch (Exception exception) {
			return ReflectionUtil.throwException(exception);
		}
	}

	@FunctionalInterface
	public interface ConvertFunction {

		public Object apply(JSONObject jsonObject, String name)
			throws Exception;

	}

	private void _addBucketsPaths(
		BiConsumer biConsumer, JSONObject jsonObject) {

		Object object = jsonObject.get("buckets_path");

		if ((object == null) || !(object instanceof JSONObject)) {
			return;
		}

		JSONObject bucketsPathJSONObject = (JSONObject)object;

		for (String key : bucketsPathJSONObject.keySet()) {
			biConsumer.accept(key, bucketsPathJSONObject.getString(key));
		}
	}

	private void _addOrders(Consumer consumer, JSONObject jsonObject) {
		JSONObject orderJSONObject = jsonObject.getJSONObject("order");

		if (orderJSONObject == null) {
			return;
		}

		List orders = new ArrayList<>();

		for (String key : orderJSONObject.keySet()) {
			Order order = null;

			boolean ascending = StringUtil.equalsIgnoreCase(
				orderJSONObject.getString(key), "asc");

			if (Order.COUNT_METRIC_NAME.equals(key)) {
				order = Order.count(ascending);
			}
			else if (Order.KEY_METRIC_NAME.equals(key)) {
				order = Order.key(ascending);
			}
			else {
				order = new Order(key);

				order.setAscending(ascending);
			}

			orders.add(order);
		}

		consumer.accept(orders.toArray(new Order[0]));
	}

	private void _addRange(Consumer consumer, JSONObject jsonObject) {
		JSONArray rangesJSONArray = jsonObject.getJSONArray("ranges");

		if (rangesJSONArray == null) {
			return;
		}

		for (int i = 0; i < rangesJSONArray.length(); i++) {
			JSONObject rangeJSONObject = rangesJSONArray.getJSONObject(i);

			String key = rangeJSONObject.getString("key");

			if (Validator.isNotNull(key)) {
				consumer.accept(
					new Range(
						key, rangeJSONObject.getString("from", null),
						rangeJSONObject.getString("to", null)));
			}
			else {
				consumer.accept(
					new Range(
						rangeJSONObject.getString("from", null),
						rangeJSONObject.getString("to", null)));
			}
		}
	}

	private SignificanceHeuristic _getSignificanceHeuristics(
		JSONObject jsonObject) {

		JSONObject chiSquareJSONObject = jsonObject.getJSONObject("chi_square");

		if (chiSquareJSONObject != null) {
			return _significanceHeuristics.chiSquare(
				chiSquareJSONObject.getBoolean("background_is_superset", true),
				chiSquareJSONObject.getBoolean("include_negatives", true));
		}

		JSONObject gndJSONObject = jsonObject.getJSONObject("gnd");

		if (gndJSONObject != null) {
			return _significanceHeuristics.gnd(
				gndJSONObject.getBoolean("background_is_superset", true));
		}

		if (jsonObject.has("jlh")) {
			return _significanceHeuristics.jlhScore();
		}

		JSONObject mutualInformationJSONObject = jsonObject.getJSONObject(
			"mutual_information");

		if (mutualInformationJSONObject != null) {
			return _significanceHeuristics.mutualInformation(
				mutualInformationJSONObject.getBoolean(
					"background_is_superset", true),
				mutualInformationJSONObject.getBoolean(
					"include_negatives", true));
		}

		if (jsonObject.has("percentage")) {
			return _significanceHeuristics.percentageScore();
		}

		JSONObject scriptHeuristicJSONObject = jsonObject.getJSONObject(
			"script_heuristic");

		if (scriptHeuristicJSONObject != null) {
			Script script = _scriptConverter.toScript(
				scriptHeuristicJSONObject);

			return _significanceHeuristics.script(script);
		}

		return null;
	}

	private void _setBackgroundFilterQuery(
		Consumer consumer, JSONObject jsonObject) {

		JSONObject backgroundFilterJSONObject = jsonObject.getJSONObject(
			"background_filter");

		if (backgroundFilterJSONObject == null) {
			return;
		}

		Query query = _queryConverter.toQuery(backgroundFilterJSONObject);

		if (query == null) {
			return;
		}

		consumer.accept(query);
	}

	private void _setBoolean(
		Consumer consumer, JSONObject jsonObject, String key) {

		if (!jsonObject.has(key)) {
			return;
		}

		consumer.accept(jsonObject.getBoolean(key));
	}

	private void _setDouble(
		Consumer consumer, JSONObject jsonObject, String key) {

		if (!jsonObject.has(key)) {
			return;
		}

		consumer.accept(jsonObject.getDouble(key));
	}

	private void _setGapPolicy(
		Consumer consumer, JSONObject jsonObject) {

		String gapPolicy = jsonObject.getString("gap_policy");

		if (Validator.isNull(gapPolicy)) {
			return;
		}

		consumer.accept(GapPolicy.valueOf(StringUtil.toUpperCase(gapPolicy)));
	}

	private void _setIncludeExcludeClause(
		Consumer consumer, JSONObject jsonObject) {

		Object excludeObject = jsonObject.get("exclude");
		Object includeObject = jsonObject.get("include");

		if ((excludeObject == null) && (includeObject == null)) {
			return;
		}

		String[] excludedValues = null;
		String excludeRegex = null;
		String[] includedValues = null;
		String includeRegex = null;

		if (excludeObject != null) {
			if (excludeObject instanceof JSONArray) {
				excludedValues = JSONUtil.toStringArray(
					(JSONArray)excludeObject);
			}
			else {
				excludeRegex = GetterUtil.getString(excludeObject);
			}
		}

		if (includeObject != null) {
			if (includeObject instanceof JSONArray) {
				includedValues = JSONUtil.toStringArray(
					(JSONArray)includeObject);
			}
			else {
				includeRegex = GetterUtil.getString(includeObject);
			}
		}

		final String[] finalExcludedValues = excludedValues;
		final String finalExcludeRegex = excludeRegex;
		final String[] finalIncludedValues = includedValues;
		final String finalIncludeRegex = includeRegex;

		consumer.accept(
			new IncludeExcludeClause() {

				@Override
				public String[] getExcludedValues() {
					return finalExcludedValues;
				}

				@Override
				public String getExcludeRegex() {
					return finalExcludeRegex;
				}

				@Override
				public String[] getIncludedValues() {
					return finalIncludedValues;
				}

				@Override
				public String getIncludeRegex() {
					return finalIncludeRegex;
				}

			});
	}

	private void _setInteger(
		Consumer consumer, JSONObject jsonObject, String key) {

		if (!jsonObject.has(key)) {
			return;
		}

		consumer.accept(jsonObject.getInt(key));
	}

	private void _setLong(
		Consumer consumer, JSONObject jsonObject, String key) {

		if (!jsonObject.has(key)) {
			return;
		}

		consumer.accept(jsonObject.getLong(key));
	}

	private void _setObject(
		Consumer consumer, JSONObject jsonObject, String key) {

		if (!jsonObject.has(key)) {
			return;
		}

		consumer.accept(jsonObject.get(key));
	}

	private void _setScript(
		Consumer