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

com.eventsourcing.postgresql.index.EqualityIndex Maven / Gradle / Ivy

/**
 * Copyright (c) 2016, All Contributors (see CONTRIBUTORS file)
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package com.eventsourcing.postgresql.index;

import com.eventsourcing.Entity;
import com.eventsourcing.EntityHandle;
import com.eventsourcing.index.Attribute;
import com.eventsourcing.index.ReflectableAttribute;
import com.eventsourcing.layout.Layout;
import com.eventsourcing.layout.TypeHandler;
import com.eventsourcing.postgresql.PostgreSQLSerialization;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.io.BaseEncoding;
import com.googlecode.cqengine.index.support.KeyStatisticsAttributeIndex;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.simple.Equal;
import com.googlecode.cqengine.query.simple.Has;
import com.googlecode.cqengine.resultset.ResultSet;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.sql.DataSource;
import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.HashSet;

@Slf4j
public class EqualityIndex extends PostgreSQLAttributeIndex
        implements KeyStatisticsAttributeIndex> {

    protected static final int INDEX_RETRIEVAL_COST = 30;
    protected static final int UNIQUE_INDEX_RETRIEVAL_COST = 25;

    @Getter
    private final DataSource dataSource;
    @Getter
    private String tableName;
    @Getter
    private Layout layout;
    @Getter
    private final TypeHandler attributeTypeHandler;
    @Getter
    private final boolean unique;

    public static  EqualityIndex onAttribute(DataSource dataSource,
                                                                        Attribute attribute, boolean unique) {
        return new EqualityIndex<>(dataSource, attribute, unique);
    }

    @SneakyThrows
    protected EqualityIndex(DataSource dataSource, Attribute attribute, boolean unique) {
        super(attribute, new HashSet>() {{
            add(Equal.class);
            add(Has.class);
        }});
        this.dataSource = dataSource;
        this.unique = unique;
        layout = Layout.forClass(attribute.getEffectiveObjectType());
        TypeResolver typeResolver = new TypeResolver();
        ResolvedType resolvedType;
        if (attribute instanceof ReflectableAttribute) {
            resolvedType = typeResolver.resolve(((ReflectableAttribute) attribute).getAttributeReflectedType());
        } else {
            resolvedType = typeResolver.resolve(attribute.getAttributeType());
        }
        attributeTypeHandler = TypeHandler.lookup(resolvedType);
        init();
    }

    @SneakyThrows
    private void init() {
        try(Connection connection = dataSource.getConnection()) {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(layout.getHash());
            digest.update(attribute.getAttributeName().getBytes());
            String encodedHash = BaseEncoding.base16().encode(digest.digest());
            tableName = "index_v1_" + encodedHash + "_eq";
            if (unique) {
                tableName += "_unique";
            }
            String attributeType = PostgreSQLSerialization.getMappedType(connection, attributeTypeHandler);
            if (unique) {
                attributeType += " UNIQUE";
            }
            String create = "CREATE TABLE IF NOT EXISTS " + getTableName() + " (" +
                    "\"key\" " + attributeType + ",\n" +
                    "\"object\" UUID,\n" +
                    "PRIMARY KEY(\"key\", \"object\")" +
                    ")";
            try (PreparedStatement s = connection.prepareStatement(create)) {
                s.executeUpdate();
            }
            if (!unique) {
                String indexKey = "CREATE INDEX IF NOT EXISTS " + getTableName() + "_key_idx ON " + getTableName() + " (\"key\")";
                try (PreparedStatement s = connection.prepareStatement(indexKey)) {
                    s.executeUpdate();
                }
            }
            String indexObj = "CREATE INDEX IF NOT EXISTS " + getTableName() + "_obj_idx ON " + getTableName() + " (\"object\")";
            try (PreparedStatement s = connection.prepareStatement(indexObj)) {
                s.executeUpdate();
            }
            String indexComment = layout.getName() + "." + attribute.getAttributeName() + " EQ";
            if (unique) {
                indexComment += " UNIQUE";
            }
            String comment = "COMMENT ON TABLE " + getTableName() + " IS '" + indexComment + "'";
            try (PreparedStatement s = connection.prepareStatement(comment)) {
                s.executeUpdate();
            }

        }
    }

    @Override public ResultSet> retrieve(Query> query, QueryOptions queryOptions) {
        return super.retrieve(query, queryOptions);
    }

    @Override public String toString() {
        return "EqualityIndex[PostgreSQL, table=" + getTableName() + "]";
    }

    @Override protected int indexRetrievalCost() {
        return isUnique() ? UNIQUE_INDEX_RETRIEVAL_COST : INDEX_RETRIEVAL_COST;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy