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

it.unibz.inf.ontop.dbschema.impl.json.JsonLens Maven / Gradle / Ivy

The newest version!
package it.unibz.inf.ontop.dbschema.impl.json;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.common.collect.*;
import it.unibz.inf.ontop.dbschema.*;
import it.unibz.inf.ontop.dbschema.impl.AbstractRelationDefinition;
import it.unibz.inf.ontop.dbschema.impl.RawQuotedIDFactory;
import it.unibz.inf.ontop.exception.MetadataExtractionException;
import it.unibz.inf.ontop.injection.CoreSingletons;
import it.unibz.inf.ontop.injection.IntermediateQueryFactory;
import it.unibz.inf.ontop.iq.IQ;
import it.unibz.inf.ontop.iq.IQTree;
import it.unibz.inf.ontop.iq.node.ConstructionNode;
import it.unibz.inf.ontop.iq.request.FunctionalDependencies;
import it.unibz.inf.ontop.iq.type.SingleTermTypeExtractor;
import it.unibz.inf.ontop.model.atom.impl.AtomPredicateImpl;
import it.unibz.inf.ontop.model.term.ImmutableTerm;
import it.unibz.inf.ontop.model.term.TermFactory;
import it.unibz.inf.ontop.model.term.Variable;
import it.unibz.inf.ontop.model.term.functionsymbol.db.IRISafenessDeclarationFunctionSymbol;
import it.unibz.inf.ontop.model.type.DBTermType;
import it.unibz.inf.ontop.model.type.TermType;
import it.unibz.inf.ontop.substitution.InjectiveSubstitution;
import it.unibz.inf.ontop.substitution.Substitution;
import it.unibz.inf.ontop.substitution.SubstitutionFactory;
import it.unibz.inf.ontop.utils.ImmutableCollectors;
import it.unibz.inf.ontop.utils.VariableGenerator;
import it.unibz.inf.ontop.utils.impl.VariableGeneratorImpl;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;

@JsonDeserialize(using = JsonLens.JSONLensDeserializer.class)
public abstract class JsonLens extends JsonOpenObject {
    @Nonnull
    public final List name;

    @Nullable
    public final UniqueConstraints uniqueConstraints;

    @Nullable
    public final OtherFunctionalDependencies otherFunctionalDependencies;

    @Nullable
    public final ForeignKeys foreignKeys;

    @Nullable
    public final NonNullConstraints nonNullConstraints;

    @Nullable
    public final IRISafeConstraints iriSafeConstraints;

    public JsonLens(List name, @Nullable UniqueConstraints uniqueConstraints,
                    @Nullable OtherFunctionalDependencies otherFunctionalDependencies, @Nullable ForeignKeys foreignKeys,
                    @Nullable NonNullConstraints nonNullConstraints,
                    @Nullable IRISafeConstraints iriSafeConstraints) {
        this.name = name;
        this.uniqueConstraints = uniqueConstraints;
        this.otherFunctionalDependencies = otherFunctionalDependencies;
        this.foreignKeys = foreignKeys;
        this.nonNullConstraints = nonNullConstraints;
        this.iriSafeConstraints = iriSafeConstraints;
    }

    public abstract Lens createViewDefinition(DBParameters dbParameters, MetadataLookup parentCacheMetadataLookup)
            throws MetadataExtractionException;

    public abstract void insertIntegrityConstraints(Lens relation,
                                                    ImmutableList baseRelations,
                                                    MetadataLookup metadataLookup, DBParameters dbParameters) throws MetadataExtractionException;

    /**
     * Propagates unique constraints of this lens to its parents, if possible. Returns true if at least one constraint was propagated.
     */
    public boolean propagateUniqueConstraintsUp(Lens relation, ImmutableList parents, QuotedIDFactory idFactory) throws MetadataExtractionException {
        //Does nothing by default, but is implemented by JsonBasicLens. May also be implemented by other lenses under certain conditions.
        return false;
    }

    /**
     * Propagates foreign key constraints of this lens to its parents, if possible. Returns true if at least one constraint was propagated.
     */
    public boolean propagateForeignKeyConstraintsUp(Lens relation, ImmutableList parents, QuotedIDFactory idFactory) throws MetadataExtractionException {
        //Does nothing by default, but is implemented by JsonBasicLens. May also be implemented by other lenses under certain conditions.
        return false;
    }

    /**
     * May be incomplete, but must not produce any false positive.
     *
     * Returns the attributes for which it can be proved that the projection over them includes the results
     * of the projection of the parent relation over the parent attributes under set semantics (no concern for duplicates).
     *
     * Parent attributes are expected to all come from the same parent.
     */
    public abstract ImmutableList> getAttributesIncludingParentOnes(
            Lens lens, ImmutableList parentAttributes);

    protected RelationDefinition.AttributeListBuilder createAttributeBuilder(IQ iq, DBParameters dbParameters)  {
        SingleTermTypeExtractor uniqueTermTypeExtractor = dbParameters.getCoreSingletons().getUniqueTermTypeExtractor();
        QuotedIDFactory quotedIdFactory = dbParameters.getQuotedIDFactory();

        RelationDefinition.AttributeListBuilder builder = AbstractRelationDefinition.attributeListBuilder();
        IQTree iqTree = iq.getTree();

        ImmutableSet addedNonNullAttributes = nonNullConstraints == null
                ? ImmutableSet.of()
                : nonNullConstraints.added.stream()
                .map(quotedIdFactory::createAttributeID)
                .collect(ImmutableCollectors.toSet());

        RawQuotedIDFactory rawQuotedIqFactory = new RawQuotedIDFactory(quotedIdFactory);

        // TODO: preserve the original order?
        for (Variable v : iq.getProjectionAtom().getVariables()) {
            QuotedID attributeId = rawQuotedIqFactory.createAttributeID(v.getName());

            boolean isNullable = (!addedNonNullAttributes.contains(attributeId))
                    && iqTree.getVariableNullability().isPossiblyNullable(v);

            builder.addAttribute(attributeId,
                    (DBTermType) uniqueTermTypeExtractor.extractSingleTermType(v, iqTree)
                            .orElseGet(() -> dbParameters.getDBTypeFactory().getAbstractRootDBType()),
                    isNullable);
        }
        return builder;
    }

    protected IQTree addIRISafeConstraints(IQTree iqTreeBeforeIRISafeConstraints, DBParameters dbParameters) {
        if (iriSafeConstraints == null || iriSafeConstraints.added.isEmpty())
            return iqTreeBeforeIRISafeConstraints;

        ImmutableSet initialProjectedVariables = iqTreeBeforeIRISafeConstraints.getVariables();

        QuotedIDFactory quotedIdFactory = dbParameters.getQuotedIDFactory();
        RawQuotedIDFactory rawQuotedIqFactory = new RawQuotedIDFactory(quotedIdFactory);

        ImmutableSet iriSafeVariables = iriSafeConstraints.added.stream()
                .map(quotedIdFactory::createAttributeID)
                .map(a -> initialProjectedVariables.stream()
                        .filter(v -> rawQuotedIqFactory.createAttributeID(v.getName()).equals(a))
                        .findAny())
                .flatMap(Optional::stream)
                .collect(ImmutableCollectors.toSet());

        if (iriSafeVariables.isEmpty())
            // TODO: issue a warning
            return iqTreeBeforeIRISafeConstraints;

        CoreSingletons coreSingletons = dbParameters.getCoreSingletons();
        VariableGenerator variableGenerator = coreSingletons.getCoreUtilsFactory()
                .createVariableGenerator(iqTreeBeforeIRISafeConstraints.getKnownVariables());

        TermFactory termFactory = coreSingletons.getTermFactory();
        IntermediateQueryFactory iqFactory = coreSingletons.getIQFactory();
        SubstitutionFactory substitutionFactory = coreSingletons.getSubstitutionFactory();


        InjectiveSubstitution renaming = iriSafeVariables.stream()
                .collect(substitutionFactory.toFreshRenamingSubstitution(variableGenerator));

        IRISafenessDeclarationFunctionSymbol iriSafenessDeclarationFunctionSymbol = coreSingletons.getDBFunctionsymbolFactory()
                .getIRISafenessDeclaration();

        Substitution substitution = iriSafeVariables.stream()
                .collect(substitutionFactory.toSubstitution(
                        v -> termFactory.getImmutableFunctionalTerm(iriSafenessDeclarationFunctionSymbol, renaming.get(v))));

        ConstructionNode newConstructionNode = iqFactory.createConstructionNode(initialProjectedVariables, substitution);

        return iqFactory.createUnaryIQTree(
                newConstructionNode,
                iqTreeBeforeIRISafeConstraints.applyFreshRenaming(renaming))
                .normalizeForOptimization(variableGenerator);
    }

    protected void insertTransitiveFunctionalDependencies(ImmutableSet previousDependencies, NamedRelationDefinition relation, CoreSingletons coreSingletons) throws AttributeNotFoundException, MetadataExtractionException {
        var uselessVariableGenerator = new VariableGeneratorImpl(ImmutableSet.of(), coreSingletons.getTermFactory());
        var var2Attr = relation.getAttributes().stream()
                .collect(ImmutableCollectors.toMap(
                        attr -> uselessVariableGenerator.generateNewVariable(attr.getID().getName()),
                        attr -> attr
                ));
        var id2Var = var2Attr.entrySet().stream()
                .collect(ImmutableCollectors.toMap(
                        entry -> entry.getValue().getID(),
                        Map.Entry::getKey)
                );
        if(previousDependencies.stream()
                .anyMatch(fd ->
                                fd.getDeterminants().stream()
                                        .anyMatch(id -> !id2Var.containsKey(id))
                                || fd.getDeterminants().stream()
                                        .anyMatch(id -> !id2Var.containsKey(id))
                        ))
            throw new MetadataExtractionException(String.format(
                    "Cannot find attribute for Functional Dependency of %s.", relation.getID()));
        FunctionalDependencies allDependencies = previousDependencies.stream()
                .map(fd -> Maps.immutableEntry(
                        fd.getDeterminants().stream()
                                .map(id2Var::get).collect(ImmutableCollectors.toSet()),
                        fd.getDependents().stream()
                                .map(id2Var::get).collect(ImmutableCollectors.toSet())
                ))
                .collect(FunctionalDependencies.toFunctionalDependencies());
        for(var entry : allDependencies.stream().collect(ImmutableCollectors.toList())) {
            addTransitiveDependency(
                    relation,
                    entry.getKey().stream()
                            .map(var2Attr::get)
                            .collect(ImmutableCollectors.toSet()),
                    entry.getValue().stream()
                            .map(var2Attr::get)
                            .collect(ImmutableCollectors.toSet()));
        }
    }

    private void addTransitiveDependency(NamedRelationDefinition relation, ImmutableSet determinants, Set dependents) throws AttributeNotFoundException {
        var builder = FunctionalDependency.defaultBuilder(relation);
        for (Attribute determinant : determinants) {
            builder.addDeterminant(determinant.getID());
        }
        for (Attribute attribute : dependents) {
            builder.addDependent(attribute.getID());
        }
        builder.build();
    }


    protected static class UniqueConstraints extends JsonOpenObject {
        @Nonnull
        public final List added;

        @JsonCreator
        public UniqueConstraints(@JsonProperty("added") List added) {
            this.added = added;
        }
    }

    protected static class AddUniqueConstraints extends JsonOpenObject {
        @Nonnull
        public final String name;
        @Nonnull
        public final List determinants;
        public final Boolean isPrimaryKey;


        @JsonCreator
        public AddUniqueConstraints(@JsonProperty("name") String name,
                                    @JsonProperty("determinants") List determinants,
                                    @JsonProperty("isPrimaryKey") Boolean isPrimaryKey) {
            this.name = name;
            this.determinants = determinants;
            this.isPrimaryKey = isPrimaryKey;
        }

        /*
         * Override equals method to ensure we can check for object equality
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            AddUniqueConstraints other = (AddUniqueConstraints) obj;
            return Objects.equals(determinants, other.determinants);
        }

        /*
         * Override hashCode method to ensure we can check for object equality
         */
        @Override
        public int hashCode() {
            return Objects.hash(determinants);
        }
    }

    protected static class OtherFunctionalDependencies extends JsonOpenObject {
        @Nonnull
        public final List added;

        @JsonCreator
        public OtherFunctionalDependencies(@JsonProperty("added") List added) {
            this.added = added;
        }
    }

    protected static class AddFunctionalDependency extends JsonOpenObject {
        @Nonnull
        public final List determinants;
        @Nonnull
        public final List dependents;

        public AddFunctionalDependency(@JsonProperty("determinants") List determinants,
                                       @JsonProperty("dependents") List dependents) {
            this.determinants = determinants;
            this.dependents = dependents;
        }

        /*
         * Override equals method to ensure we can check for object equality
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            AddFunctionalDependency other = (AddFunctionalDependency) obj;
            return Objects.equals(ImmutableMap.of(determinants, dependents),
                    ImmutableMap.of(other.determinants, other.dependents));
        }

        /*
         * Override hashCode method to ensure we can check for object equality
         */
        @Override
        public int hashCode() {
            return Objects.hash(ImmutableMap.of(determinants, dependents));
        }
    }

    protected static class ForeignKeys extends JsonOpenObject {
        @Nonnull
        public final List added;

        @JsonCreator
        public ForeignKeys(@JsonProperty("added") List added) {
            this.added = added;
        }
    }

    protected static class AddForeignKey extends JsonOpenObject {
        // TODO: make it nullable
        @Nonnull
        public final String name;
        @Nonnull
        public final List from;
        @Nonnull
        public final ForeignKeyPart to;

        public AddForeignKey(@JsonProperty("name") String name,
                             @JsonProperty("from") List from,
                             @JsonProperty("to") ForeignKeyPart to) {
            this.name = name;
            this.from = from;
            this.to = to;
        }

        /**
         * Name is not considered
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            AddForeignKey that = (AddForeignKey) o;
            return from.equals(that.from) && to.equals(that.to);
        }

        /**
         * Name is not considered
         */
        @Override
        public int hashCode() {
            return Objects.hash(from, to);
        }
    }

    public static class ForeignKeyPart extends JsonOpenObject {
        public final List relation;
        public final List columns;

        @JsonCreator
        public ForeignKeyPart(@JsonProperty("relation") List relation,
                              @JsonProperty("columns") List columns) {
            this.relation = relation;
            this.columns = columns;
        }
    }

    protected static class TemporaryLensPredicate extends AtomPredicateImpl {

        protected TemporaryLensPredicate(String name, ImmutableList baseTypesForValidation) {
            super(name, baseTypesForValidation);
        }
    }

    public static class JSONLensDeserializer extends JsonDeserializer {

        @Override
        public JsonLens deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException {
            ObjectMapper mapper = (ObjectMapper) jp.getCodec();
            JsonNode node = mapper.readTree(jp);
            String type = node.get("type").asText();

            Class instanceClass;
            switch (type) {
                case "BasicLens":
                // Deprecated
                case "BasicViewDefinition":
                    instanceClass = JsonBasicLens.class;
                    break;
                case "SQLLens":
                // Deprecated
                case "SQLViewDefinition":
                    instanceClass = JsonSQLLens.class;
                    break;
                case "JoinLens":
                // Deprecated
                case "JoinViewDefinition":
                    instanceClass = JsonJoinLens.class;
                    break;
                case "FlattenLens":
                // Deprecated
                case "FlattenedViewDefinition":
                    instanceClass = JsonFlattenLens.class;
                    break;
                case "UnionLens":
                    instanceClass = JsonUnionLens.class;
                    break;
                default:
                    // TODO: throw proper exception
                    throw new RuntimeException("Unsupported type of lens: " + type);
            }
            return mapper.treeToValue(node, instanceClass);
        }
    }

    protected static class NonNullConstraints extends JsonOpenObject {
        @Nonnull
        public final List added;

        @JsonCreator
        public NonNullConstraints(@JsonProperty("added") List added) {
            this.added = added;
        }
    }

    protected static class IRISafeConstraints extends JsonOpenObject {
        @Nonnull
        public final List added;

        @JsonCreator
        public IRISafeConstraints(@JsonProperty("added") List added) {
            this.added = added;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy