
it.unibz.inf.ontop.model.term.functionsymbol.impl.MinOrMaxOrSampleSPARQLFunctionSymbolImpl Maven / Gradle / Ivy
package it.unibz.inf.ontop.model.term.functionsymbol.impl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import it.unibz.inf.ontop.exception.MinorOntopInternalBugException;
import it.unibz.inf.ontop.iq.node.VariableNullability;
import it.unibz.inf.ontop.iq.request.DefinitionPushDownRequest;
import it.unibz.inf.ontop.model.term.*;
import it.unibz.inf.ontop.model.term.functionsymbol.InequalityLabel;
import it.unibz.inf.ontop.model.term.functionsymbol.SPARQLAggregationFunctionSymbol;
import it.unibz.inf.ontop.model.type.*;
import it.unibz.inf.ontop.model.vocabulary.SPARQL;
import it.unibz.inf.ontop.substitution.Substitution;
import it.unibz.inf.ontop.utils.ImmutableCollectors;
import it.unibz.inf.ontop.utils.VariableGenerator;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class MinOrMaxOrSampleSPARQLFunctionSymbolImpl extends SPARQLFunctionSymbolImpl
implements SPARQLAggregationFunctionSymbol {
public enum MinMaxSampleType {
MIN,
MAX,
SAMPLE
}
private final TypeFactory typeFactory;
private final MinMaxSampleType type;
private final RDFDatatype abstractNumericType;
private final RDFDatatype dateTimeType;
private final ObjectRDFType bnodeType;
private final ObjectRDFType iriType;
private final String defaultAggVariableName;
private final InequalityLabel inequalityLabel;
protected MinOrMaxOrSampleSPARQLFunctionSymbolImpl(TypeFactory typeFactory, MinMaxSampleType type) {
this(
type == MinMaxSampleType.MAX ? "SP_MAX" : (type == MinMaxSampleType.MIN ? "SP_MIN" : "SP_SAMPLE"),
type == MinMaxSampleType.MAX ? SPARQL.MAX : (type == MinMaxSampleType.MIN ? SPARQL.MIN : SPARQL.SAMPLE),
typeFactory,
type
);
}
protected MinOrMaxOrSampleSPARQLFunctionSymbolImpl(String name, String officialName, TypeFactory typeFactory, MinMaxSampleType type) {
super(name, officialName, ImmutableList.of(typeFactory.getAbstractRDFTermType()));
this.typeFactory = typeFactory;
this.type = type;
this.abstractNumericType = typeFactory.getAbstractOntopNumericDatatype();
this.dateTimeType = typeFactory.getXsdDatetimeDatatype();
this.bnodeType = typeFactory.getBlankNodeType();
this.iriType = typeFactory.getIRITermType();
this.defaultAggVariableName = type == MinMaxSampleType.MAX ? "max1" : (type == MinMaxSampleType.MIN ? "min1" : "sample1");
this.inequalityLabel = type == MinMaxSampleType.MAX ? InequalityLabel.GT : InequalityLabel.LT;
}
@Override
protected boolean tolerateNulls() {
return false;
}
@Override
public boolean isAlwaysInjectiveInTheAbsenceOfNonInjectiveFunctionalTerms() {
return false;
}
@Override
public boolean isAggregation() {
return true;
}
/**
* Too complex to be implemented for the moment
*/
@Override
public Optional inferType(ImmutableList extends ImmutableTerm> terms) {
return Optional.empty();
}
@Override
public boolean canBePostProcessed(ImmutableList extends ImmutableTerm> arguments) {
return false;
}
@Override
public boolean isNullable(ImmutableSet nullableIndexes) {
return true;
}
/**
* TODO: put it into common in an abstract class
*/
@Override
public Optional decomposeIntoDBAggregation(ImmutableList extends ImmutableTerm> subTerms,
ImmutableList> possibleRDFTypes,
boolean hasGroupBy, VariableNullability variableNullability,
VariableGenerator variableGenerator, TermFactory termFactory) {
if (possibleRDFTypes.size() != getArity()) {
throw new IllegalArgumentException("The size of possibleRDFTypes is expected to match the arity of " +
"the function symbol");
}
ImmutableTerm subTerm = subTerms.get(0);
ImmutableSet subTermPossibleTypes = possibleRDFTypes.get(0);
switch (subTermPossibleTypes.size()) {
case 0:
throw new MinorOntopInternalBugException("At least one RDF type was expected to be inferred for the first sub-term");
case 1:
return Optional.of(decomposeUnityped(subTerm, subTermPossibleTypes.iterator().next(), hasGroupBy, variableNullability,
variableGenerator, termFactory));
default:
return Optional.of(decomposeMultityped(subTerm, subTermPossibleTypes, hasGroupBy, variableNullability,
variableGenerator, termFactory));
}
}
private AggregationSimplification decomposeUnityped(ImmutableTerm subTerm, RDFTermType subTermType, boolean hasGroupBy,
VariableNullability variableNullability, VariableGenerator variableGenerator,
TermFactory termFactory) {
ImmutableTerm subTermLexicalTerm = extractLexicalTerm(subTerm, termFactory);
ImmutableFunctionalTerm dbAggTerm = createAggregate(
subTermType,
termFactory.getConversionFromRDFLexical2DB(subTermLexicalTerm, subTermType),
termFactory);
RDFTermTypeConstant subTermTypeConstant = termFactory.getRDFTermTypeConstant(subTermType);
Variable dbAggregationVariable = variableGenerator.generateNewVariable(defaultAggVariableName);
boolean isSubTermNullable = subTermLexicalTerm.isNullable(variableNullability.getNullableVariables());
boolean dbAggMayReturnNull = !(hasGroupBy && (!isSubTermNullable));
ImmutableTerm typeTerm = dbAggMayReturnNull
? termFactory.getIfElseNull(
termFactory.getDBIsNotNull(dbAggregationVariable), subTermTypeConstant)
: subTermTypeConstant;
ImmutableFunctionalTerm liftedTerm = termFactory.getRDFFunctionalTerm(
termFactory.getConversion2RDFLexical(dbAggregationVariable, subTermType),
typeTerm);
ImmutableFunctionalTerm.FunctionalTermDecomposition decomposition = termFactory.getFunctionalTermDecomposition(
liftedTerm,
termFactory.getSubstitution(ImmutableMap.of(dbAggregationVariable, dbAggTerm)));
return AggregationSimplification.create(decomposition);
}
private ImmutableFunctionalTerm createAggregate(RDFTermType rdfType, ImmutableTerm dbSubTerm, TermFactory termFactory) {
DBTermType dbType = rdfType.getClosestDBType(typeFactory.getDBTypeFactory());
return type == MinMaxSampleType.MAX
? termFactory.getDBMax(dbSubTerm, dbType)
: (type == MinMaxSampleType.MIN
? termFactory.getDBMin(dbSubTerm, dbType)
: termFactory.getDBSample(dbSubTerm, dbType)
);
}
private AggregationSimplification decomposeMultityped(ImmutableTerm subTerm, ImmutableSet subTermPossibleTypes,
boolean hasGroupBy, VariableNullability variableNullability,
VariableGenerator variableGenerator, TermFactory termFactory) {
ImmutableTerm subTermLexicalTerm = extractLexicalTerm(subTerm, termFactory);
ImmutableTerm subTermTypeTerm = extractRDFTermTypeTerm(subTerm, termFactory);
// One fresh variable per type
ImmutableMap subVariableMap = subTermPossibleTypes.stream()
.collect(ImmutableCollectors.toMap(
t -> t,
t -> variableGenerator.generateNewVariable()));
// Each variable of the subVariableMap will have the closest DB type to its associated RDF type
ImmutableSet pushDownRequests = computeRequests(subTermLexicalTerm, subTermTypeTerm,
subVariableMap, termFactory, variableNullability);
// One fresh variable per type
ImmutableMap aggregateMap = subTermPossibleTypes.stream()
.collect(ImmutableCollectors.toMap(
t -> t,
t -> variableGenerator.generateNewVariable()));
Substitution substitution = computeSubstitution(subVariableMap, aggregateMap,
termFactory);
ImmutableFunctionalTerm liftableTerm = computeLiftableTerm(aggregateMap, termFactory);
return AggregationSimplification.create(
termFactory.getFunctionalTermDecomposition(liftableTerm, substitution),
pushDownRequests);
}
private ImmutableSet computeRequests(ImmutableTerm subTermLexicalTerm, ImmutableTerm subTermTypeTerm,
ImmutableMap subVariableMap,
TermFactory termFactory, VariableNullability variableNullability) {
return subVariableMap.entrySet().stream()
.map(e -> computeRequest(subTermLexicalTerm, subTermTypeTerm, e.getKey(), e.getValue(), termFactory,
variableNullability))
.collect(ImmutableCollectors.toSet());
}
private DefinitionPushDownRequest computeRequest(ImmutableTerm subTermLexicalTerm, ImmutableTerm subTermTypeTerm,
RDFTermType rdfType, Variable newVariable, TermFactory termFactory,
VariableNullability variableNullability) {
ImmutableTerm definition = termFactory.getConversionFromRDFLexical2DB(subTermLexicalTerm, rdfType)
.simplify(variableNullability);
ImmutableExpression condition = termFactory.getStrictEquality(subTermTypeTerm, termFactory.getRDFTermTypeConstant(rdfType));
return DefinitionPushDownRequest.create(newVariable, definition, condition);
}
private Substitution computeSubstitution(ImmutableMap subVariableMap,
ImmutableMap aggregateMap,
TermFactory termFactory) {
return termFactory.getSubstitution(aggregateMap.entrySet().stream()
.collect(ImmutableCollectors.toMap(
Map.Entry::getValue,
e -> createAggregate(e.getKey(), subVariableMap.get(e.getKey()), termFactory))));
}
private ImmutableFunctionalTerm computeLiftableTerm(ImmutableMap aggregateMap,
TermFactory termFactory) {
ImmutableList> typeTermWhenPairs = computeTypeTermWhenPairs(
aggregateMap, termFactory);
ImmutableFunctionalTerm typeTerm = termFactory.getDBCase(typeTermWhenPairs.stream(), termFactory.getNullConstant(), true);
ImmutableFunctionalTerm lexicalTerm = computeLexicalTerm(typeTermWhenPairs, aggregateMap, termFactory);
return termFactory.getRDFFunctionalTerm(lexicalTerm, typeTerm);
}
private ImmutableList> computeTypeTermWhenPairs(
ImmutableMap aggregateMap,
TermFactory termFactory) {
Optional> bnodeEntry =
Optional.ofNullable(aggregateMap.get(bnodeType))
.map(v -> createRegularTypeWhenPair(v, bnodeType, termFactory));
Optional> iriEntry =
Optional.ofNullable(aggregateMap.get(iriType))
.map(v -> createRegularTypeWhenPair(v, iriType, termFactory));
Stream> objectEntries = Stream.of(bnodeEntry, iriEntry)
.filter(Optional::isPresent)
.map(Optional::get);
Stream> numericEntries = computeNumericOrDatetimeTypePairs(
abstractNumericType, aggregateMap,
(agg1, agg2) -> termFactory.getDBNumericInequality(inequalityLabel, agg1, agg2),
termFactory);
Stream> datetimeEntries = computeNumericOrDatetimeTypePairs(
dateTimeType, aggregateMap,
(agg1, agg2) -> termFactory.getDBDatetimeInequality(inequalityLabel, agg1, agg2),
termFactory);
Stream> otherEntries = aggregateMap.entrySet().stream()
.filter(e -> e.getKey() instanceof RDFDatatype)
.filter(e -> (!e.getKey().isA(abstractNumericType))
&& (!e.getKey().isA(dateTimeType)))
.map(e -> createRegularTypeWhenPair(e.getValue(), e.getKey(), termFactory));
return Stream.concat(
Stream.concat(objectEntries, numericEntries),
Stream.concat(datetimeEntries, otherEntries))
.collect(ImmutableCollectors.toList());
}
private BiFunction orOnlyFirstArgumentIsNotNull(
BiFunction fct, TermFactory termFactory) {
return (agg1, agg2) -> termFactory.getDisjunction(
fct.apply(agg1, agg2),
termFactory.getConjunction(
termFactory.getDBIsNotNull(agg1),
termFactory.getDBIsNull(agg2)));
}
private Map.Entry createRegularTypeWhenPair(Variable aggregateVariable,
RDFTermType rdfType,
TermFactory termFactory) {
return Maps.immutableEntry(
termFactory.getDBIsNotNull(aggregateVariable),
termFactory.getRDFTermTypeConstant(rdfType));
}
private Stream> computeNumericOrDatetimeTypePairs(
RDFDatatype baseDatatype, ImmutableMap aggregateMap,
BiFunction partialComparisonFct,
TermFactory termFactory) {
BiFunction comparisonFct = orOnlyFirstArgumentIsNotNull(
partialComparisonFct, termFactory);
ImmutableList matchingTypes = aggregateMap.keySet().stream()
.filter(t -> t.isA(baseDatatype))
.collect(ImmutableCollectors.toList());
if (matchingTypes.isEmpty())
return Stream.empty();
Stream> firstPairStream = IntStream.range(0, matchingTypes.size() - 1)
.mapToObj(i -> createNumericOrDatetimeTypePair(matchingTypes.get(i),
matchingTypes.subList(i + 1, matchingTypes.size()), aggregateMap,
comparisonFct, termFactory));
RDFTermType lastType = matchingTypes.get(matchingTypes.size() - 1);
Map.Entry lastPair = createRegularTypeWhenPair(
aggregateMap.get(lastType), lastType, termFactory);
return Stream.concat(firstPairStream, Stream.of(lastPair));
}
private Map.Entry createNumericOrDatetimeTypePair(
RDFTermType rdfType, ImmutableList typesToCompareWith,
ImmutableMap aggregateMap, BiFunction comparisonFct,
TermFactory termFactory) {
Variable aggregateVariable = aggregateMap.get(rdfType);
ImmutableExpression expression = termFactory.getConjunction(
typesToCompareWith.stream()
.map(aggregateMap::get)
.map(agg2 -> comparisonFct.apply(aggregateVariable, agg2)))
.orElseThrow(() -> new MinorOntopInternalBugException("At least one type to compare with was expected"));
return Maps.immutableEntry(expression, termFactory.getRDFTermTypeConstant(rdfType));
}
/**
* Replaces the "then values" of typeTermWhenPairs by the lexical values of the corresponding
* aggregation variables.
*/
private ImmutableFunctionalTerm computeLexicalTerm(
ImmutableList> typeTermWhenPairs,
ImmutableMap aggregateMap, TermFactory termFactory) {
Stream> lexicalWhenPairs = typeTermWhenPairs.stream()
.map(e -> {
RDFTermType rdfType = e.getValue().getRDFTermType();
ImmutableFunctionalTerm thenLexicalTerm = termFactory.getConversion2RDFLexical(
aggregateMap.get(rdfType),
rdfType);
return Maps.immutableEntry(e.getKey(), thenLexicalTerm);
});
return termFactory.getDBCase(lexicalWhenPairs, termFactory.getNullConstant(),
// TODO: double-check
true);
}
@Override
public Constant evaluateEmptyBag(TermFactory termFactory) {
return termFactory.getNullConstant();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy