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

com.hp.autonomy.searchcomponents.idol.parametricvalues.IdolParametricValuesServiceImpl Maven / Gradle / Ivy

/*
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

package com.hp.autonomy.searchcomponents.idol.parametricvalues;

import com.autonomy.aci.client.services.AciErrorException;
import com.autonomy.aci.client.util.AciParameters;
import com.hp.autonomy.aci.content.ranges.Range;
import com.hp.autonomy.aci.content.ranges.Ranges;
import com.hp.autonomy.searchcomponents.core.caching.CacheNames;
import com.hp.autonomy.searchcomponents.core.fields.TagNameFactory;
import com.hp.autonomy.searchcomponents.core.parametricvalues.BucketingParams;
import com.hp.autonomy.searchcomponents.core.parametricvalues.BucketingParamsHelper;
import com.hp.autonomy.searchcomponents.core.parametricvalues.DependentParametricField;
import com.hp.autonomy.searchcomponents.core.parametricvalues.ParametricRequest;
import com.hp.autonomy.searchcomponents.core.parametricvalues.ParametricValuesService;
import com.hp.autonomy.searchcomponents.core.search.QueryRequest;
import com.hp.autonomy.searchcomponents.idol.annotations.IdolService;
import com.hp.autonomy.searchcomponents.idol.fields.IdolFieldsRequestBuilder;
import com.hp.autonomy.searchcomponents.idol.fields.IdolFieldsService;
import com.hp.autonomy.searchcomponents.idol.search.HavenSearchAciParameterHandler;
import com.hp.autonomy.searchcomponents.idol.search.IdolQueryRestrictions;
import com.hp.autonomy.searchcomponents.idol.search.QueryExecutor;
import com.hp.autonomy.types.idol.responses.FlatField;
import com.hp.autonomy.types.idol.responses.GetQueryTagValuesResponseData;
import com.hp.autonomy.types.idol.responses.RecursiveField;
import com.hp.autonomy.types.idol.responses.TagValue;
import com.hp.autonomy.types.requests.idol.actions.tags.FieldPath;
import com.hp.autonomy.types.requests.idol.actions.tags.QueryTagCountInfo;
import com.hp.autonomy.types.requests.idol.actions.tags.QueryTagInfo;
import com.hp.autonomy.types.requests.idol.actions.tags.RangeInfo;
import com.hp.autonomy.types.requests.idol.actions.tags.TagActions;
import com.hp.autonomy.types.requests.idol.actions.tags.TagName;
import com.hp.autonomy.types.requests.idol.actions.tags.ValueDetails;
import com.hp.autonomy.types.requests.idol.actions.tags.params.FieldTypeParam;
import com.hp.autonomy.types.requests.idol.actions.tags.params.GetQueryTagValuesParams;
import com.hp.autonomy.types.requests.idol.actions.tags.params.SortParam;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.xml.bind.JAXBElement;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hp.autonomy.searchcomponents.core.parametricvalues.ParametricValuesService.PARAMETRIC_VALUES_SERVICE_BEAN_NAME;

/**
 * Default Idol implementation of {@link ParametricValuesService}
 */
@Service(PARAMETRIC_VALUES_SERVICE_BEAN_NAME)
@IdolService
class IdolParametricValuesServiceImpl implements IdolParametricValuesService {
    static final String VALUE_NODE_NAME = "value";
    static final String VALUES_NODE_NAME = "values";
    static final String VALUE_MIN_NODE_NAME = "valuemin";
    static final String VALUE_MAX_NODE_NAME = "valuemax";
    static final String VALUE_AVERAGE_NODE_NAME = "valueaverage";
    static final String VALUE_SUM_NODE_NAME = "valuesum";

    private final HavenSearchAciParameterHandler parameterHandler;
    private final IdolFieldsService fieldsService;
    private final ObjectFactory fieldsRequestBuilderFactory;
    private final BucketingParamsHelper bucketingParamsHelper;
    private final TagNameFactory tagNameFactory;
    private final QueryExecutor queryExecutor;

    @SuppressWarnings("ConstructorWithTooManyParameters")
    @Autowired
    IdolParametricValuesServiceImpl(
            final HavenSearchAciParameterHandler parameterHandler,
            final IdolFieldsService fieldsService,
            final ObjectFactory fieldsRequestBuilderFactory,
            final BucketingParamsHelper bucketingParamsHelper,
            final TagNameFactory tagNameFactory,
            final QueryExecutor queryExecutor
    ) {
        this.parameterHandler = parameterHandler;
        this.fieldsService = fieldsService;
        this.fieldsRequestBuilderFactory = fieldsRequestBuilderFactory;
        this.bucketingParamsHelper = bucketingParamsHelper;
        this.tagNameFactory = tagNameFactory;
        this.queryExecutor = queryExecutor;
    }

    @Override
    public Set getParametricValues(final IdolParametricRequest parametricRequest) throws AciErrorException {
        final Collection fieldNames = new HashSet<>();
        fieldNames.addAll(parametricRequest.getFieldNames());

        if (fieldNames.isEmpty()) {
            fieldNames.addAll(lookupFields());
        }

        return fieldNames.isEmpty()
                ? Collections.emptySet()
                : getFlatFields(parametricRequest, fieldNames).stream()
                .map(this::flatFieldToTagInfo)
                .filter(queryTagInfo -> !queryTagInfo.getValues().isEmpty())
                .collect(Collectors.toCollection(LinkedHashSet::new));
    }

    @Override
    @Cacheable(CacheNames.PARAMETRIC_VALUES_IN_BUCKETS)
    public List getNumericParametricValuesInBuckets(final IdolParametricRequest parametricRequest, final Map bucketingParamsPerField) throws AciErrorException {
        if (parametricRequest.getFieldNames().isEmpty()) {
            return Collections.emptyList();
        } else {
            bucketingParamsHelper.validateBucketingParams(parametricRequest, bucketingParamsPerField);

            final Map> boundariesPerField = bucketingParamsPerField.entrySet().stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, entry -> bucketingParamsHelper.calculateBoundaries(entry.getValue())));

            final List ranges = boundariesPerField.entrySet().stream()
                    .map(entry -> new Range(entry.getKey().getNormalisedPath(), ArrayUtils.toPrimitive(entry.getValue().toArray(new Double[entry.getValue().size()]))))
                    .collect(Collectors.toList());

            final IdolParametricRequest bucketingRequest = parametricRequest.toBuilder()
                    .maxValues(null)
                    .start(1)
                    .ranges(ranges)
                    .sort(SortParam.NumberIncreasing)
                    .build();

            return getFlatFields(bucketingRequest, parametricRequest.getFieldNames()).stream()
                    .map(flatFieldToRangeInfo(boundariesPerField))
                    .collect(Collectors.toList());
        }
    }

    @Override
    public List getDependentParametricValues(final IdolParametricRequest parametricRequest) throws AciErrorException {
        final Collection fieldNames = new ArrayList<>();
        fieldNames.addAll(parametricRequest.getFieldNames());
        if (fieldNames.isEmpty()) {
            fieldNames.addAll(lookupFields());
        }

        final List results;
        if (fieldNames.isEmpty()) {
            results = Collections.emptyList();
        } else {
            final AciParameters aciParameters = createAciParameters(parametricRequest, fieldNames);
            aciParameters.add(GetQueryTagValuesParams.FieldDependence.name(), true);
            aciParameters.add(GetQueryTagValuesParams.FieldDependenceMultiLevel.name(), true);

            final GetQueryTagValuesResponseData responseData = executeAction(parametricRequest, aciParameters);

            results = !responseData.getField().isEmpty() && responseData.getValues() != null
                    ? addDisplayNamesToRecursiveFields(responseData.getValues().getField(), responseData.getField().get(0).getName())
                    : Collections.emptyList();
        }

        return results;
    }

    @Override
    public Map getValueDetails(final IdolParametricRequest parametricRequest) throws AciErrorException {
        if (parametricRequest.getFieldNames().isEmpty()) {
            return Collections.emptyMap();
        } else {
            final AciParameters aciParameters = createAciParameters(parametricRequest, parametricRequest.getFieldNames());

            aciParameters.add(GetQueryTagValuesParams.MaxValues.name(), 1);
            aciParameters.add(GetQueryTagValuesParams.ValueDetails.name(), true);

            final GetQueryTagValuesResponseData responseData = executeAction(parametricRequest, aciParameters);
            final Collection fields = responseData.getField();

            final Map output = new LinkedHashMap<>();

            for (final FlatField field : fields) {
                final List> valueElements = field.getValueAndSubvalueOrValues();

                final ValueDetails.Builder builder = new ValueDetails.Builder();

                Integer values = null;
                for (final JAXBElement element : valueElements) {
                    final String elementLocalName = element.getName().getLocalPart();

                    if (VALUE_MIN_NODE_NAME.equals(elementLocalName)) {
                        builder.setMin((Double) element.getValue());
                    } else if (VALUE_MAX_NODE_NAME.equals(elementLocalName)) {
                        builder.setMax((Double) element.getValue());
                    } else if (VALUE_AVERAGE_NODE_NAME.equals(elementLocalName)) {
                        builder.setAverage((Double) element.getValue());
                    } else if (VALUE_SUM_NODE_NAME.equals(elementLocalName)) {
                        builder.setSum((Double) element.getValue());
                    } else if (VALUES_NODE_NAME.equals(elementLocalName)) {
                        values = (Integer) element.getValue();
                    }
                }
                builder.setTotalValues(flatFieldTotalValues(field, values));

                final FieldPath fieldPath = tagNameFactory.getFieldPath(field.getName().get(0));
                output.put(fieldPath, builder.build());
            }

            return output;
        }
    }

    private AciParameters createAciParameters(final IdolParametricRequest parametricRequest, final Collection fieldNames) {
        final AciParameters aciParameters = new AciParameters(TagActions.GetQueryTagValues.name());
        parameterHandler.addSearchRestrictions(aciParameters, parametricRequest.getQueryRestrictions());
        if (parametricRequest.isModified()) {
            parameterHandler.addQmsParameters(aciParameters, parametricRequest.getQueryRestrictions());
        }
        parameterHandler.addSecurityInfo(aciParameters);

        aciParameters.add(GetQueryTagValuesParams.DocumentCount.name(), true);
        aciParameters.add(GetQueryTagValuesParams.FieldName.name(), StringUtils.join(fieldNames.stream().map(FieldPath::getNormalisedPath).toArray(String[]::new), ','));
        aciParameters.add(GetQueryTagValuesParams.Predict.name(), true);

        return aciParameters;
    }

    private Collection lookupFields() {
        return fieldsService.getFields(fieldsRequestBuilderFactory.getObject().fieldType(FieldTypeParam.Parametric).build()).get(FieldTypeParam.Parametric)
                .stream()
                .map(TagName::getId)
                .collect(Collectors.toList());
    }

    private Function flatFieldToRangeInfo(final Map> boundariesPerField) {
        return flatField -> {
            final TagName tagName = tagNameFactory.buildTagName(flatField.getName().get(0));

            final List> valueElements = flatField.getValueAndSubvalueOrValues();
            int count = 0;

            final Collection values = new LinkedList<>();

            for (final JAXBElement element : valueElements) {
                final String elementLocalName = element.getName().getLocalPart();

                if (VALUE_NODE_NAME.equals(elementLocalName)) {
                    final TagValue tagValue = (TagValue) element.getValue();
                    final String[] rangeValues = tagValue.getValue().split(",");
                    final double min = Double.parseDouble(rangeValues[0]);
                    final double max = Double.parseDouble(rangeValues[1]);
                    values.add(new RangeInfo.Value(min, max, tagValue.getCount()));
                } else if (VALUES_NODE_NAME.equals(elementLocalName)) {
                    count = (Integer) element.getValue();
                }
            }

            final List boundaries = boundariesPerField.get(tagName.getId());

            // If no documents match the query parameters, GetQueryTagValues does not return any buckets
            if (values.isEmpty()) {
                values.addAll(bucketingParamsHelper.emptyBuckets(boundaries));
            }

            // All buckets have the same size, so just use the value from the first one
            final double bucketSize = boundaries.get(1) - boundaries.get(0);
            return RangeInfo.builder()
                    .id(tagName.getId().getNormalisedPath())
                    .displayName(tagName.getDisplayName())
                    .count(count)
                    .min(boundaries.get(0))
                    .max(boundaries.get(boundaries.size() - 1))
                    .bucketSize(bucketSize)
                    .values(values)
                    .build();
        };
    }

    private QueryTagInfo flatFieldToTagInfo(final FlatField flatField) {
        final String name = flatField.getName().get(0);
        final Set values = flatField.getValueAndSubvalueOrValues().stream()
                .filter(element -> VALUE_NODE_NAME.equals(element.getName().getLocalPart()))
                .map(element -> {
                    final TagValue tagValue = (TagValue) element.getValue();
                    final String value = tagValue.getValue();
                    final String displayValue = tagNameFactory.getTagDisplayValue(name, value);
                    return new QueryTagCountInfo(value, displayValue, tagValue.getCount());
                })
                .collect(Collectors.toCollection(LinkedHashSet::new));

        final TagName tagName = tagNameFactory.buildTagName(name);
        return QueryTagInfo.builder()
                .id(tagName.getId().getNormalisedPath())
                .displayName(tagName.getDisplayName())
                .values(values)
                .totalValues(flatFieldTotalValues(flatField, null))
                .build();
    }

    private int flatFieldTotalValues(final FlatField flatField, final Integer values) {
        // If no values are returned for a field, IDOL does not return a  element
        // For (non-parametric) numeric fields we should use values as total_values won't be populated
        return Optional.ofNullable(flatField.getTotalValues())
                .orElse(Optional.ofNullable(values)
                        .orElse(0));
    }

    private Collection getFlatFields(final IdolParametricRequest parametricRequest, final Collection fieldNames) {
        final AciParameters aciParameters = createAciParameters(parametricRequest, fieldNames);

        aciParameters.add(GetQueryTagValuesParams.Start.name(), parametricRequest.getStart());
        aciParameters.add(GetQueryTagValuesParams.MaxValues.name(), parametricRequest.getMaxValues());
        aciParameters.add(GetQueryTagValuesParams.Sort.name(), parametricRequest.getSort());
        aciParameters.add(GetQueryTagValuesParams.Ranges.name(), new Ranges(parametricRequest.getRanges()));
        aciParameters.add(GetQueryTagValuesParams.ValueDetails.name(), true);
        aciParameters.add(GetQueryTagValuesParams.TotalValues.name(), true);
        aciParameters.add(GetQueryTagValuesParams.ValueRestriction.name(), String.join(",", parametricRequest.getValueRestrictions()));

        final GetQueryTagValuesResponseData responseData = executeAction(parametricRequest, aciParameters);
        return responseData.getField();
    }

    private GetQueryTagValuesResponseData executeAction(final ParametricRequest idolParametricRequest, final AciParameters aciParameters) {
        return queryExecutor.executeGetQueryTagValues(
                aciParameters,
                idolParametricRequest.isModified() ? QueryRequest.QueryType.MODIFIED : QueryRequest.QueryType.RAW
        );
    }

    private List addDisplayNamesToRecursiveFields(final Collection recursiveFields, final List fieldNames) {
        return fieldNames.isEmpty()
                ? Collections.emptyList()
                : recursiveFields.stream()
                .map(recursiveField -> DependentParametricField.builder()
                        .value(recursiveField.getValue())
                        .displayValue(tagNameFactory.getTagDisplayValue(fieldNames.get(0), recursiveField.getValue()))
                        .count(recursiveField.getCount())
                        .subFields(addDisplayNamesToRecursiveFields(recursiveField.getField(), fieldNames.subList(1, fieldNames.size())))
                        .build())
                .collect(Collectors.toList());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy